306 lines
7.6 KiB
PHP
306 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Locations;
|
|
|
|
use App\Models\Locations\City;
|
|
use App\Models\Locations\Country;
|
|
use App\Models\Locations\District;
|
|
use App\Models\Locations\Division;
|
|
use App\Models\Meeting;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class Location extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = ['name', 'country_id', 'division_id', 'city_id', 'district_id', 'locatable_id', 'locatable_type'];
|
|
|
|
/**
|
|
* Return related country.
|
|
* one-to-many
|
|
* @return void
|
|
*/
|
|
public function country()
|
|
{
|
|
return $this->belongsTo(Country::class);
|
|
}
|
|
|
|
/**
|
|
* Get the most appropriate country for this location.
|
|
* Tries multiple sources in order of preference.
|
|
* If resolved it syncs the location's missing country_id.
|
|
*
|
|
* @return Country|null
|
|
*/
|
|
public function getCountry()
|
|
{
|
|
// 1. Direct country relationship
|
|
if ($this->country_id && $this->country) {
|
|
return $this->country;
|
|
}
|
|
|
|
// 2. Country from division
|
|
if ($this->division_id && $this->division && $this->division->country_id) {
|
|
$this->syncCountryFromDivision();
|
|
return $this->fresh()->country;
|
|
}
|
|
|
|
// 3. Country from city
|
|
if ($this->city_id && $this->city && $this->city->country_id) {
|
|
$this->syncCountryFromCity();
|
|
return $this->fresh()->country;
|
|
}
|
|
|
|
// 4. Country from district's city (if district exists)
|
|
if ($this->district_id && $this->district && $this->district->city_id) {
|
|
// First ensure we have city synced
|
|
if (!$this->city_id) {
|
|
$this->syncCityFromDistrict();
|
|
}
|
|
|
|
// Now try to get country from city
|
|
if ($this->city && $this->city->country_id) {
|
|
$this->syncCountryFromCity();
|
|
return $this->fresh()->country;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the location's country_id based on the division's country.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function syncCountryFromDivision()
|
|
{
|
|
if ($this->division && $this->division->country_id) {
|
|
$this->country_id = $this->division->country_id;
|
|
return $this->save();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update the location's country_id based on the city's country.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function syncCountryFromCity()
|
|
{
|
|
if ($this->city && $this->city->country_id) {
|
|
$this->country_id = $this->city->country_id;
|
|
return $this->save();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return all related divisions.
|
|
* One-to-many
|
|
* @return void
|
|
*/
|
|
public function division()
|
|
{
|
|
return $this->belongsTo(Division::class);
|
|
}
|
|
|
|
/**
|
|
* Get the most appropriate division for this location.
|
|
* Tries multiple sources in order of preference.
|
|
* If resolved it syncs the location's missing division_id.
|
|
*
|
|
* @return Division|null
|
|
*/
|
|
public function getDivision()
|
|
{
|
|
// 1. Direct division relationship
|
|
if ($this->division_id && $this->division) {
|
|
return $this->division;
|
|
}
|
|
|
|
// 2. Division from city
|
|
if ($this->city_id && $this->city && $this->city->division_id) {
|
|
$this->syncDivisionFromCity();
|
|
return $this->fresh()->division; // Fresh instance to get updated division_id
|
|
}
|
|
|
|
// 3. Division from district's city (if district exists)
|
|
if ($this->district_id && $this->district && $this->district->city_id) {
|
|
// First sync city from district if city_id is missing
|
|
if (!$this->city_id) {
|
|
$this->syncCityFromDistrict();
|
|
}
|
|
|
|
// Now try to get division from city
|
|
if ($this->city && $this->city->division_id) {
|
|
$this->syncDivisionFromCity();
|
|
return $this->fresh()->division;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the location's country_id based on the division's country.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function syncCountryFromDistrict()
|
|
{
|
|
if ($this->division && $this->division->country_id) {
|
|
$this->country_id = $this->division->country_id;
|
|
return $this->save();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update the location's division_id based on the city's division.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function syncDivisionFromCity()
|
|
{
|
|
if ($this->city && $this->city->division_id) {
|
|
$this->division_id = $this->city->division_id;
|
|
return $this->save();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update the location's division_id based on the city's division.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function syncCityFromDistrict()
|
|
{
|
|
if ($this->district && $this->district->city_id) {
|
|
$this->city_id = $this->district->city_id;
|
|
return $this->save();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return related city.
|
|
* One-to-many
|
|
* @return void
|
|
*/
|
|
public function city()
|
|
{
|
|
return $this->belongsTo(City::class);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return related district.
|
|
* One-to-many
|
|
* @return void
|
|
*/
|
|
public function district()
|
|
{
|
|
return $this->belongsTo(District::class);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sync all missing location hierarchy data.
|
|
* This method will populate all missing IDs based on available relationships.
|
|
*
|
|
* @return array Returns what was synced
|
|
*/
|
|
public function syncAllLocationData()
|
|
{
|
|
$synced = [];
|
|
|
|
// Step 1: Sync city from district if missing
|
|
if (!$this->city_id && $this->district_id) {
|
|
if ($this->syncCityFromDistrict()) {
|
|
$synced[] = 'city_from_district';
|
|
}
|
|
}
|
|
|
|
// Step 2: Sync division from city if missing
|
|
if (!$this->division_id && $this->city_id) {
|
|
if ($this->syncDivisionFromCity()) {
|
|
$synced[] = 'division_from_city';
|
|
}
|
|
}
|
|
|
|
// Step 3: Sync country from division if missing
|
|
if (!$this->country_id && $this->division_id) {
|
|
if ($this->syncCountryFromDivision()) {
|
|
$synced[] = 'country_from_division';
|
|
}
|
|
}
|
|
|
|
// Step 4: Fallback - sync country from city if still missing
|
|
if (!$this->country_id && $this->city_id) {
|
|
if ($this->syncCountryFromCity()) {
|
|
$synced[] = 'country_from_city';
|
|
}
|
|
}
|
|
|
|
return $synced;
|
|
}
|
|
|
|
/**
|
|
* Get a complete location hierarchy.
|
|
* This method ensures all data is synced and returns the complete hierarchy.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getCompleteHierarchy()
|
|
{
|
|
// Sync all data first
|
|
$this->syncAllLocationData();
|
|
|
|
// Refresh the model to get updated relationships
|
|
$location = $this->fresh();
|
|
|
|
return [
|
|
'country' => $location->country,
|
|
'division' => $location->getDivision(),
|
|
'city' => $location->city,
|
|
'district' => $location->district,
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Return related locatable (i.e. user or organization).
|
|
* One-to-many polymorph
|
|
* @return void
|
|
*/
|
|
public function locatable()
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
|
|
|
|
/**
|
|
* Return related meeting.
|
|
* one-to-many
|
|
* @return void
|
|
*/
|
|
public function meeting()
|
|
{
|
|
return $this->hasOne(Meeting::class);
|
|
}
|
|
|
|
}
|