Add photo management features, update database configuration, and enhance park model seeding

This commit is contained in:
pacnpal
2025-02-25 15:44:21 -05:00
parent 15b2d4ebcf
commit b4462ba89e
31 changed files with 2700 additions and 71 deletions

View File

@@ -6,8 +6,10 @@ use App\Enums\ParkStatus;
use App\Traits\HasLocation;
use App\Traits\HasSlugHistory;
use App\Traits\HasParkStatistics;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -95,6 +97,95 @@ class Park extends Model
return $this->hasMany(ParkArea::class);
}
/**
* Get the photos for the park.
*/
public function photos(): MorphMany
{
return $this->morphMany(Photo::class, 'photoable');
}
/**
* Get the featured photo for the park.
*/
public function featuredPhoto()
{
return $this->photos()->where('is_featured', true)->first()
?? $this->photos()->orderBy('position')->first();
}
/**
* Get the URL of the featured photo or a default image.
*/
public function getFeaturedPhotoUrlAttribute(): string
{
$photo = $this->featuredPhoto();
return $photo ? $photo->url : asset('images/placeholders/default-park.jpg');
}
/**
* Add a photo to the park.
*
* @param array<string, mixed> $attributes
* @return \App\Models\Photo
*/
public function addPhoto(array $attributes): Photo
{
// Set position to be the last in the collection if not specified
if (!isset($attributes['position'])) {
$lastPosition = $this->photos()->max('position') ?? 0;
$attributes['position'] = $lastPosition + 1;
}
// If this is the first photo or is_featured is true, make it featured
if ($this->photos()->count() === 0 || ($attributes['is_featured'] ?? false)) {
$attributes['is_featured'] = true;
} else {
$attributes['is_featured'] = false;
}
return $this->photos()->create($attributes);
}
/**
* Set a photo as the featured photo.
*
* @param \App\Models\Photo|int $photo
* @return bool
*/
public function setFeaturedPhoto($photo): bool
{
if (is_numeric($photo)) {
$photo = $this->photos()->findOrFail($photo);
}
return $photo->setAsFeatured();
}
/**
* Reorder photos.
*
* @param array<int, int> $photoIds Ordered array of photo IDs
* @return bool
*/
public function reorderPhotos(array $photoIds): bool
{
// Begin transaction
DB::beginTransaction();
try {
foreach ($photoIds as $position => $photoId) {
$this->photos()->where('id', $photoId)->update(['position' => $position + 1]);
}
DB::commit();
return true;
} catch (\Exception $e) {
DB::rollBack();
return false;
}
}
/**
* Get formatted website URL (ensures proper URL format).
*/
@@ -165,6 +256,50 @@ class Park extends Model
]);
}
/**
* Get the formatted location for the park.
*/
public function getFormattedLocationAttribute(): string
{
return $this->formatted_address ?? '';
}
/**
* Get the absolute URL for the park detail page.
*/
public function getAbsoluteUrl(): string
{
return route('parks.show', ['slug' => $this->slug]);
}
/**
* Get a park by its current or historical slug.
*
* @param string $slug
* @return array{0: \App\Models\Park|null, 1: bool} Park and whether a historical slug was used
*/
public static function getBySlug(string $slug): array
{
// Try current slug
$park = static::where('slug', $slug)->first();
if ($park) {
return [$park, false];
}
// Try historical slug
$slugHistory = SlugHistory::where('slug', $slug)
->where('sluggable_type', static::class)
->latest()
->first();
if ($slugHistory) {
$park = static::find($slugHistory->sluggable_id);
return [$park, true];
}
return [null, false];
}
/**
* Boot the model.
*/