Files
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

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);
}
}