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