mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 17:31:11 -05:00
Add models, enums, and services for user roles, theme preferences, slug history, and ID generation
This commit is contained in:
221
app/Models/Location.php
Normal file
221
app/Models/Location.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class Location extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'address',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'postal_code',
|
||||
'latitude',
|
||||
'longitude',
|
||||
'elevation',
|
||||
'timezone',
|
||||
'metadata',
|
||||
'is_approximate',
|
||||
'source',
|
||||
'geocoding_data',
|
||||
'geocoded_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'latitude' => 'decimal:8',
|
||||
'longitude' => 'decimal:8',
|
||||
'elevation' => 'decimal:2',
|
||||
'metadata' => 'array',
|
||||
'geocoding_data' => 'array',
|
||||
'geocoded_at' => 'datetime',
|
||||
'is_approximate' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent locatable model.
|
||||
*/
|
||||
public function locatable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location's coordinates as an array.
|
||||
*
|
||||
* @return array<string, float|null>
|
||||
*/
|
||||
public function getCoordinatesAttribute(): array
|
||||
{
|
||||
return [
|
||||
'lat' => $this->latitude,
|
||||
'lng' => $this->longitude,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormattedAddressAttribute(): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($this->address) {
|
||||
$parts[] = $this->address;
|
||||
}
|
||||
|
||||
if ($this->city) {
|
||||
$parts[] = $this->city;
|
||||
}
|
||||
|
||||
if ($this->state) {
|
||||
$parts[] = $this->state;
|
||||
}
|
||||
|
||||
if ($this->postal_code) {
|
||||
$parts[] = $this->postal_code;
|
||||
}
|
||||
|
||||
if ($this->country) {
|
||||
$parts[] = $this->country;
|
||||
}
|
||||
|
||||
return implode(', ', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google Maps URL for the location.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMapUrlAttribute(): ?string
|
||||
{
|
||||
if (!$this->latitude || !$this->longitude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'https://www.google.com/maps?q=%f,%f',
|
||||
$this->latitude,
|
||||
$this->longitude
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the location's coordinates.
|
||||
*
|
||||
* @param float $latitude
|
||||
* @param float $longitude
|
||||
* @param float|null $elevation
|
||||
* @return bool
|
||||
*/
|
||||
public function updateCoordinates(float $latitude, float $longitude, ?float $elevation = null): bool
|
||||
{
|
||||
return $this->update([
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'elevation' => $elevation,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location's address components.
|
||||
*
|
||||
* @param array<string, string> $components
|
||||
* @return bool
|
||||
*/
|
||||
public function setAddress(array $components): bool
|
||||
{
|
||||
return $this->update([
|
||||
'address' => $components['address'] ?? $this->address,
|
||||
'city' => $components['city'] ?? $this->city,
|
||||
'state' => $components['state'] ?? $this->state,
|
||||
'country' => $components['country'] ?? $this->country,
|
||||
'postal_code' => $components['postal_code'] ?? $this->postal_code,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to find locations within a radius.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param float $latitude
|
||||
* @param float $longitude
|
||||
* @param float $radius
|
||||
* @param string $unit
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeNearby($query, float $latitude, float $longitude, float $radius, string $unit = 'km')
|
||||
{
|
||||
$earthRadius = $unit === 'mi' ? 3959 : 6371;
|
||||
|
||||
return $query->whereRaw(
|
||||
"($earthRadius * acos(
|
||||
cos(radians(?)) *
|
||||
cos(radians(latitude)) *
|
||||
cos(radians(longitude) - radians(?)) +
|
||||
sin(radians(?)) *
|
||||
sin(radians(latitude))
|
||||
)) <= ?",
|
||||
[$latitude, $longitude, $latitude, $radius]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to find locations within bounds.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array<string, float> $ne Northeast corner [lat, lng]
|
||||
* @param array<string, float> $sw Southwest corner [lat, lng]
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeInBounds($query, array $ne, array $sw)
|
||||
{
|
||||
return $query->whereBetween('latitude', [$sw['lat'], $ne['lat']])
|
||||
->whereBetween('longitude', [$sw['lng'], $ne['lng']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the distance to a point.
|
||||
*
|
||||
* @param float $latitude
|
||||
* @param float $longitude
|
||||
* @param string $unit
|
||||
* @return float|null
|
||||
*/
|
||||
public function distanceTo(float $latitude, float $longitude, string $unit = 'km'): ?float
|
||||
{
|
||||
if (!$this->latitude || !$this->longitude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$earthRadius = $unit === 'mi' ? 3959 : 6371;
|
||||
|
||||
$latFrom = deg2rad($this->latitude);
|
||||
$lonFrom = deg2rad($this->longitude);
|
||||
$latTo = deg2rad($latitude);
|
||||
$lonTo = deg2rad($longitude);
|
||||
|
||||
$latDelta = $latTo - $latFrom;
|
||||
$lonDelta = $lonTo - $lonFrom;
|
||||
|
||||
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
|
||||
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
|
||||
|
||||
return $angle * $earthRadius;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user