filter = null; $this->filterUrl = null; } /** * Системные настройки модуля * @param array $options @ref * @return string */ public function settingsSystem(array &$options = []): string { return $this->template('admin/settings.sys', ['options' => &$options]); } /** * Инициализируем фильтр по региону * @param mixed $key ключ требуемых данных о текущих настройках фильтра по региону или FALSE (все данные) * @return mixed */ public function filter($key = false) { $enabled = ! $this->filterDisabled(); if ($enabled && is_null($this->filter)) { $url = $this->filterUrl(); $this->filter = $url; # Меняем фильтр региона пользователя: if ( # Исходя из текущего URL $this->filterUrlOnly() || # При поиске объявлений / компаний $this->router->isCurrent([ 'listings-items.search', 'listings-items.search-geo', 'business-search', 'business-search-geo', ]) || # На главной региона ($url['id'] && bff::isIndex()) ) { $this->filterUser($url['id'], 'filter-init'); } $user = $this->regionData($this->filterUser()); if ($user && ! $this->filterUrlOnly()) { if ($this->coveringRegionCorrect($user)) { $url = array_merge([ 'id' => $user['id'], 'keyword' => $user['keyword'] ?: '', ], $this->filterUrlData($user)); } } if (! empty($url['id'])) { $this->filter = $this->regionData($url['id']); } } switch ($key) { case 'id': # id текущего: города | региона | 0 return $this->filter['id'] ?? 0; case 'id-city': # id текущего: города | 0 return ! empty($this->filter['city']) ? ($this->filter['id'] ?? 0) : 0; case 'id-country': # id текущей: страны | 0 return $this->regionCountry($this->filter); case 'url': # данные для формирования урл return $this->filter['url'] ?? $this->filterUrlData($this->filter); default: return $this->filter; } } /** * Блок выбора региона в фильтре * @return FilterBlock */ public function filterBlock() { $filter = $this->view->getShared('filterGeo'); if (! $filter) { $filter = new FilterBlock(); $this->view->share('filterGeo', $filter); } return $filter; } /** * Применять фильтр по региону только исходя из указанного в URL * @return bool */ public function filterUrlOnly() { return $this->config('geo.filter.url', 0, TYPE_INT) === 1 || $this->request->isRobot(); } /** * Выключен фильтр по региону * @return bool */ public function filterDisabled() { return $this->config('geo.filter.url', 0, TYPE_INT) === -1; } /** * Получаем ID города/региона на основе текущего URL * @param mixed $key ключ требуемых данных * @return int|array * @throws \bff\exception\NotFoundException */ public function filterUrl($key = false) { if (is_null($this->filterUrl)) { $this->filterUrl = ['id' => 0, 'city' => '', 'region1' => '', 'keyword' => '']; do { $data = false; if ($this->isAdminPanel()) { break; } if ($this->coveringType(static::COVERING_CITY)) { $data = $this->regionData($this->coveringRegion()); break; } switch ($this->urlType()) { case static::URL_SUBDOMAIN: # поддомены $host = $this->request->host(); if (preg_match('/(.*)\.' . preg_quote(SITEHOST) . '/', $host, $matches) > 0 && !empty($matches[1])) { # страна / область / город $data = $this->regionDataByKeyword($matches[1]); if ($data && ! $this->coveringRegionCorrect($data)) { $data = false; } if ($data) { if (isset($data['enabled']) && !$data['enabled'] && $this->request->isGET()) { $this->errors->notFoundException(); } } } break; case static::URL_SUBDIR: # поддиректории $extra = Url::segments(['before' => 'region', 'regexp' => true]); $uri = $this->request->uri(); if (preg_match('/^\/' . (!empty($extra) ? join('\/', $extra) . '\/' : '') . '([^\/]+)\/(.*)/', $uri, $matches) > 0 && !empty($matches[1])) { $data = $this->regionDataByKeyword($matches[1]); if ($data && ! $this->coveringRegionCorrect($data)) { $data = false; } if ($data) { if (isset($data['enabled']) && !$data['enabled'] && $this->request->isGET()) { $this->errors->notFoundException(); } } } break; case static::URL_NONE: # регион не фигурирует в URL # не анализируем URL break; } } while (false); if (! empty($data['id'])) { $url = array_merge([ 'id' => $data['id'], 'keyword' => $data['keyword'] ?: '', ], $this->filterUrlData($data)); $this->filterUrl = $url; } } if ($key == 'id') { return $this->filterUrl['id'] ?? 0; } else { if ($key == 'keyword') { return $this->filterUrl['keyword'] ?? ''; } } return $this->filterUrl; } public function filterUrlData($data) { $result = ['city' => '', 'region1' => '']; if (! empty($data['city'])) { $result['city'] = $data['keyword'] ?? ''; } if (! empty($data['parents'])) { foreach ($data['parents'] as $k => $v) { $result['region' . $k] = $v['keyword'] ?? ''; } } if (! empty($data['numlevel'])) { $result['region' . $data['numlevel']] = $data['keyword'] ?? ''; } return $result; } /** * Получаем / устанавливаем ID города/региона пользователя * @param int|bool $regionID устанавливаем ID города/региона или FALSE (получаем текущий) * @param string|bool $reason причина по которой регион пользователя был обновлен * @return int */ public function filterUser($regionID = false, $reason = false) { if ($this->request->isRobot()) { if ($regionID !== false) { return $regionID; } else { return 0; } } $cookieKey = $this->regionCookie(); if ($regionID !== false) { # обновляем куки if (! $this->request->hasCookie($cookieKey) || $this->request->cookie($cookieKey) != $regionID) { Response::setCookie($cookieKey, $regionID, '+' . $this->config('geo.filter.cookie.expire', 100, TYPE_UINT) . ' days'); if (! ($reason === 'filter-init' && $this->ipLocationConfirm())) { $_COOKIE[$cookieKey] = $regionID; # todo } } } else { if ($this->coveringType(static::COVERING_CITY)) { $regionID = $this->coveringRegion(); } else { if (! $this->request->hasCookie($cookieKey)) { # определяем ID город по IP адресу пользователя # - если не включено подтверждение if (!$this->ipLocationConfirm()) { if ($this->ipLocationEnabled()) { $data = $this->regionDataByIp(); if (empty($data)) { $data = ['id' => 0]; } else { if (!$this->coveringRegionCorrect($data)) { $data['id'] = 0; } } $regionID = $data['id']; if ($regionID > 0) { # кешируем $this->regionData($data['id'], $data); } } else { $regionID = 0; } $this->filterUser($regionID, 'ip-location-auto'); } } else { # получаем из куков $regionID = $this->request->cookie($cookieKey, TYPE_UINT); } } } return $regionID; } /** * Формирование URL, с учетом фильтра по "региону" * @param array $opts параметры: * > region1 - keyword/array данные страны * > region(2-3-4) - keyword/array данные регионов(областей) * > city - keyword/array данные города * @param bool $dynamic динамическая ссылка * @param bool $trailingSlash завершающий слеш * @return string */ public function url(array $opts = [], bool $dynamic = false, bool $trailingSlash = true) { $urlType = $this->urlType(); $deep = $this->maxDeep(); $geo = []; # формируем URL с учетом указанного города (city), области (region) $notEmpty = function ($r) use (&$opts, &$geo) { if (! empty($opts[$r])) { if (is_string($opts[$r])) { $geo[$r] = $opts[$r]; } elseif (isset($opts[$r]['keyword'])) { $geo[$r] = $opts[$r]['keyword']; } } }; for ($i = $deep; $i >= 1; $i--) { $notEmpty('region' . $i); } $notEmpty('city'); if (empty($geo)) { # формируем URL с учетом текущего фильтра по "региону" $geo = $this->filter('url'); } $sub = ''; switch ($this->coveringType()) { case static::COVERING_COUNTRY: case static::COVERING_COUNTRIES: $sub = $geo['city'] ?? ''; if (empty($sub)) { for ($i = $deep; $i >= 1; $i--) { $k = 'region' . $i; if (! empty($geo[$k])) { $sub = $geo[$k]; break; } } } break; case static::COVERING_REGION: case static::COVERING_CITIES: # нескольких городов: регион не участвует в URL $sub = $geo['city'] ?? ''; break; case static::COVERING_CITY: # один город: город не участвует в URL break; } $url = '/'; $urlOptions = ['dynamic' => $dynamic, 'segments' => true]; switch ($urlType) { case static::URL_SUBDOMAIN: # поддомены if (! empty($sub)) { $urlOptions['subdomains'] = [$sub]; } break; case static::URL_SUBDIR: # поддиректории if (! empty($sub)) { if ($dynamic) { $url .= $sub . '/'; unset($urlOptions['segments']); } else { $urlOptions['segments'] = Url::segments(['region' => $sub]); } } break; case static::URL_NONE: # регион не задействован в URL break; } $url = Url::to($url, $urlOptions); if (! $trailingSlash) { $url = rtrim($url, '/'); } return $this->app->filter('geo.url', $url, [ 'opts' => $opts, 'dynamic' => $dynamic, 'trailingSlash' => $trailingSlash, 'geo' => $geo, ]); } /** * Текущий тип формирования URL с учетом региона и города * @return mixed */ public function urlType() { return $this->config('geo.url', static::URL_SUBDIR, TYPE_UINT); } /** * Текущий тип покрытия регионов * @param int|array $type >0|array - проверяем на соответствие, 0 - возвращаем текущий * @return bool|int */ public function coveringType($type = 0) { $current = $this->config('geo.covering', static::COVERING_COUNTRY, TYPE_UINT); if (! empty($type)) { if (is_array($type)) { return in_array($current, $type); } return ($current == $type); } return $current; } /** * ID города/нескольких городов/области(региона)/страны/стран в зависимости от текущего покрытия регионов * @return int|array|void */ public function coveringRegion() { switch ($this->coveringType()) { case static::COVERING_COUNTRIES: $data = json_decode($this->config('geo.covering.countries', '', TYPE_STR), true); if (empty($data) || ! is_array($data)) { $data = [$this->defaultCountry()]; } return $data; case static::COVERING_COUNTRY: return $this->config('geo.covering.country', $this->defaultCountry(), TYPE_UINT); case static::COVERING_REGION: return $this->config('geo.covering.region', 0, TYPE_UINT); case static::COVERING_CITY: return $this->config('geo.covering.city', 0, TYPE_UINT); case static::COVERING_CITIES: $data = json_decode($this->config('geo.covering.cities', '', TYPE_STR), true); if (! is_array($data)) { $data = []; } return $data; } } /** * Проверка данных текущего региона на соответствие текущим настройка типа покрытия регионов * @param array $data @ref данные о текущем региона todo ref + facade * @return bool */ public function coveringRegionCorrect(array &$data) { if (empty($data) || !$data['id'] || empty($data['numlevel'])) { return true; } if (empty($data['parents']) && $data['numlevel'] > 1) { return false; } $parents = $data['parents']; switch ($this->coveringType()) { case static::COVERING_COUNTRIES: $countries = $this->coveringRegion(); if (in_array($data['id'], $countries)) { return true; } if (empty($parents[1]['id']) || ! in_array($parents[1]['id'], $countries)) { return false; } break; case static::COVERING_COUNTRY: if (empty($parents[1]['id']) || $parents[1]['id'] != $this->coveringRegion()) { return false; } break; case static::COVERING_REGION: foreach ($parents as $k => $v) { if ($k == 1) { continue; } if (! empty($v['id']) && $v['id'] == $this->coveringRegion()) { return true; } } return false; case static::COVERING_CITIES: if (!in_array($data['id'], $this->coveringRegion())) { return false; } break; case static::COVERING_CITY: if ($data['id'] != $this->coveringRegion()) { return false; } break; } return true; } /** * Autocomplete для городов/областей * @param array $opts * @return array|\bff\http\Response */ public function regionSuggest(array $opts = []) { # часть названия города/области (вводимая пользователем) $query = $opts['q'] ?? $this->input->post('q', TYPE_NOTAGS); # выполнять поиск среди городов и областей(регионов), false - только среди городов $reg = $opts['reg'] ?? $this->input->post('reg', TYPE_BOOL); # выполнять поиск среди стран $country = $opts['country'] ?? $this->input->post('country', TYPE_BOOL); # выполнять поиск среди городов в которых есть метро //$metro = $opts['metro'] ?? $this->input->post('metro', TYPE_BOOL); # ID области(региона) или 0 (во всех областях) $region = $opts['region'] ?? $this->input->post('region', TYPE_UINT); # ID страны или 0 (defaultCountry()) $country_id = $opts['country_id'] ?? $this->input->post('country_id', TYPE_UINT); # список полей $fields = $opts['fields'] ?? $this->input->post('fields', TYPE_STR); if (! empty($fields)) { $fields = explode(',', $fields); array_map('trim', $fields); } else { $fields = []; } $fields = $this->regionSuggestFields($fields); $opts = $this->defaults($opts, [ 'fields' => ['parents', 'declension', 'extra'], 'asArray' => false, # вернуть результат в виде массива 'lang' => $this->locale->current(), 'limit' => $this->config('geo.city.select.suggest.limit', 10, TYPE_UINT), ]); $filter = ['enabled' => 1]; $applyLevels = function () use (&$filter, $reg, $country) { if ($reg && $country) { return; } if ($country && ! $reg) { $filter['or'][] = ['numlevel' => 1, 'city' => 1]; return; } if (! $country && $reg) { $filter[] = ['numlevel', '>', 1]; return; } if (! $country && ! $reg) { $filter['city'] = 1; } }; $covering = $this->coveringType(); switch ($covering) { case static::COVERING_COUNTRIES: $filter['pid_parents'] = $country_id ?: $this->coveringRegion(); $applyLevels(); break; case static::COVERING_COUNTRY: case static::COVERING_REGION: $filter['pid_parents'] = $this->coveringRegion(); $applyLevels(); break; case static::COVERING_CITIES: case static::COVERING_CITY: $filter['id'] = $this->coveringRegion(); $filter['city'] = 1; break; } if (isset($opts['id'])) { $filter['id'] = $opts['id']; } else { $filter['search'] = $this->input->cleanSearchString($query, 50); } if ($region) { $filter['pid_parents'] = $region; } $asArray = $opts['asArray']; unset($opts['asArray']); $list = $this->model->regionsListing($filter, $opts); $response = []; foreach ($list as &$v) { $v['decl'] = $v['declension']['where'] ?? ''; $v['metro'] = $v['extra']['metro'] ?? 0; $v['link'] = $this->url($this->filterUrlData($v)); $r = []; foreach ($fields as $f) { if (! isset($v[$f])) { continue; } $r[$f] = $v[$f]; } $title = [$v['title']]; switch ($covering) { case static::COVERING_COUNTRIES: case static::COVERING_COUNTRY: if (isset($v['parents'][2])) { # область $title[] = $v['parents'][2]['title']; } if (! $country_id && $covering == static::COVERING_COUNTRIES) { if (isset($v['parents'][1])) { # страна $title[] = $v['parents'][1]['title']; } } break; case static::COVERING_REGION: case static::COVERING_CITIES: case static::COVERING_CITY: break; } $v['titles'] = $title; $response[] = [ $v['id'], join(', ', $title), $r, ]; } unset($v); if ($asArray) { return [ 'response' => $response, 'list' => $list, ]; } return $this->ajaxResponse($response); } /** * Список полей отдаваемой функциями regionSuggest и regionPreSuggest * @param array $list список дополнительных полей * @return array */ public function regionSuggestFields(array $list = []): array { return array_merge([ 'id', 'pid', 'numleft', 'numright', 'numlevel', 'keyword', 'title', 'decl', 'link', 'parents', 'metro', ], is_array($list) ? $list : []); } /** * Формируем подсказку(presuggest) состоящую из основных городов, в формате JSON * @param int $countryID ID страны или 0 (defaultCountry()) * @param array $opts * @return string|array */ public function regionPreSuggest(int $countryID = 0, $opts = []) { if (empty($countryID)) { $countryID = $this->defaultCountry(); } $opts = array_merge([ 'json' => true, 'lang' => $this->locale->current(), 'limit' => $this->config('geo.city.select.presuggest.limit', 15, TYPE_UINT), 'fields' => ['parents', 'declension', 'extra'], 'orderBy' => 'fav', ], (is_bool($opts) ? ['json' => !$opts] : $opts)); $fields = $this->regionSuggestFields(); $coveringType = $this->coveringType(); $cacheKey = join('-', ['city-presuggest', $coveringType, $countryID, $opts['lang'], $opts['limit']]); $data = Cache::group('geo')->rememberForever($cacheKey, function () use ($coveringType, $countryID, $opts) { # получаем список предвыбранных основных городов страны $filter = [ ['fav', '>', 0], 'enabled' => 1, 'city' => 1, ]; switch ($coveringType) { /** @noinspection PhpMissingBreakStatementInspection */ case static::COVERING_REGION: $countryID = $this->coveringRegion() ?: 0; case static::COVERING_COUNTRY: case static::COVERING_COUNTRIES: $country = $this->model->regionData([ 'id' => $countryID, 'enabled' => 1, 'numlevel' => 1, ]); if (! empty($country)) { $filter[] = ['numleft', '>', $country['numleft'] ?? 0]; $filter[] = ['numright', '<', $country['numright'] ?? 0]; } else { $filter['id'] = 0; # страна не найена } break; case static::COVERING_CITIES: $cities = $this->coveringRegion(); if (! empty($cities)) { $filter['id'] = $cities; $opts['orderBy'] = 'FIELD (id, ' . join(',', $filter['id']) . ')'; /* MySQL only */ unset($filter[0]); # ['fav', '>', 0] } else { $filter['id'] = 0; } break; case static::COVERING_CITY: $filter['id'] = $this->coveringRegion() ?: 0; break; } return $this->model->regionsListing($filter, $opts); }); $response = []; foreach ($data as $v) { $v['decl'] = $v['declension']['where'] ?? ''; $v['metro'] = $v['extra']['metro'] ?? 0; $v['link'] = $this->url(['city' => $v['keyword']]); $r = []; foreach ($fields as $f) { if (! isset($v[$f])) { continue; } $r[$f] = $v[$f]; } $response[] = [$v['id'], $v['title'], $r]; } return $opts['json'] ? func::php2js($response, ['numericCheck' => true]) : $response; # возвращаем в JSON-формате для autocomplete.js } /** * Формируем список стран * @param array $opts * @return array */ public function countriesList(array $opts = []) { $opts = array_merge([ 'lang' => $this->locale->current(), ], $opts); $coveringType = $this->coveringType(); $cacheKey = 'countries-list-' . $coveringType . '-' . $opts['lang']; return Cache::group('geo')->rememberForever($cacheKey, function () use ($coveringType, $opts) { $filter = ['enabled' => 1, 'numlevel' => 1]; if ($coveringType === static::COVERING_COUNTRIES) { $countriesID = $this->coveringRegion(); $filter['id'] = $countriesID; $orderBy = 'FIELD(id, ' . (is_array($countriesID) ? join(',', $countriesID) : strval($countriesID)) . ')'; } else { $orderBy = 'numleft'; } return $this->model->regionsListing($filter, [ 'lang' => $opts['lang'], 'orderBy' => $orderBy, 'keyBy' => 'id', 'fields' => ['declension', 'extra'], ]); }); } /** * Форма выбора города * @param int $cityID ID выбранного города * @param bool $isForm true - форма добавления/редактирования сущности * @param string $fieldName имя поля для хранения ID выбранного города * @param array $options доп. настройки * @return string HTML */ public function citySelect(int $cityID, bool $isForm, string $fieldName = '', array $options = []) { $data = [ 'covering_type' => $this->coveringType(), 'is_form' => $isForm, 'field_name' => (!empty($fieldName) ? $fieldName : 'geo_city'), 'field_country_name' => (!empty($options['field_country_name']) ? $options['field_country_name'] : 'geo_region1'), 'options' => $options, ]; if ($data['covering_type'] == static::COVERING_CITY) { $data['covering_city_id'] = $this->coveringRegion(); if (!$cityID) { $cityID = $data['covering_city_id']; } } $data['geo_region1'] = $this->defaultCountry(); if (!$cityID && $data['covering_type'] == static::COVERING_COUNTRIES) { if (!isset($options['country_empty'])) { $options['country_empty'] = _t('geo', 'Select Country'); } switch ($options['form']) { case 'listings-form': # форма добавления/редактирования объявлений if ($this->isAdminPanel()) { $data['geo_region1'] = 0; } else { $data['geo_region1'] = $this->filter('id-country'); if ($data['geo_region1'] > 0) { $options['country_empty'] = ''; } } break; case 'users-settings': # настройки пользователя case 'banners-form': # добавление банера $data['geo_region1'] = 0; break; } } $data['geo_city'] = $cityID; $data['city'] = $this->regionData($cityID); if (empty($data['city'])) { $data['city'] = ['title' => '']; if (isset($options['country_value'])) { $data['geo_region1'] = $options['country_value']; } } else { $data['geo_region1'] = $data['city']['parents'][1]['id'] ?? 0; } if ($data['covering_type'] == static::COVERING_COUNTRIES) { $data['country_options'] = HTML::selectOptions($this->countriesList(), $data['geo_region1'], ! empty($options['country_empty']) ? $options['country_empty'] : false, 'id', 'title'); } if ($this->isAdminPanel()) { return $this->template('admin/city.select', $data); } else { return $this->template('city.select', $data); } } /** * Форма выбора страны / региона / города один автокомплитер * @param int $regionID ID выбранной страны / региона / города * @param string $fieldName имя поля для хранения ID выбранного города * @param array $data доп. настройки * @return string HTML */ public function regionSelect(int $regionID, string $fieldName = '', array $data = []): string { $data['field_value'] = $regionID; $data['field_name'] = ! empty($fieldName) ? $fieldName : 'region_id'; $data['covering_type'] = $this->coveringType(); $data['field_title'] = ''; $data['attr'] = $data['attr'] ?? []; $regionData = $this->regionData($regionID); if (! empty($regionData['title'])) { $data['field_title'] = $regionData['title']; } if ($this->isAdminPanel()) { return $this->template('admin/region.select', $data); } return ''; } /** * Формируем список районов города * @param int $cityID ID города * @param array $opts * @return array */ public function districtList(int $cityID = 0, array $opts = []): array { $opts = array_merge([ 'lang' => $this->locale->current(), 'keyBy' => 'id', ], $opts); $cacheKey = 'district-list-' . $cityID . '-' . $opts['lang']; return Cache::group('geo')->rememberForever($cacheKey, function () use ($cityID, &$opts) { return $this->model->districtsListing(['city_id' => $cityID], $opts); }); } /** * Формируем список районов города в формате select::options * @param int $cityID ID города * @param int $selectedID * @param mixed $emptyOption название option-пункта в случае если район не указан * @param string|null $lang * @return string */ public function districtOptions(int $cityID = 0, int $selectedID = 0, $emptyOption = null, ?string $lang = null): string { $lang = $lang ?? $this->locale->current(); $emptyOption = $emptyOption ?? _t('', 'Select', [], $lang); return HTML::selectOptions($this->districtList($cityID, ['lang' => $lang]), $selectedID, $emptyOption, 'id', 'title'); } /** * Включена ли функция определения региона по IP пользователя * @return bool */ public function ipLocationEnabled(): bool { return $this->config('geo.ip.location', false, TYPE_BOOL); } /** * Спрашивать подтверждение региона пользователем * При включенной функции определения региона по IP * @return bool */ public function ipLocationConfirm(): bool { return $this->config('geo.ip.location.confirm', false, TYPE_BOOL) && $this->ipLocationEnabled() && ! $this->request->hasCookie($this->regionCookie()); // todo regions cookieset ??? } /** * Cookie ключ для хранения текущего региона пользователя * @return string */ public function regionCookie(): string { return $this->app->cookieKey('geo'); } /** * SEO макросы регионов * @param SeoTemplate $template * @param bool $fill * @param int|null $id active region id * @return array */ public function addRegionsSeoPlaceholders(SeoTemplate $template, $fill = false, ?int $id = null): array { $placeholders = []; $depth = Geo::maxDeep(); for ($i = 1; $i <= $depth; $i++) { $placeholders['region' . $i] = _t('@geo', 'Geo Region Level [:lvl]', [':lvl' => $i]); } if (Geo::coveringType([Geo::COVERING_COUNTRY,Geo::COVERING_COUNTRIES])) { $placeholders['region'] = _t('@geo', 'Active Region: Country / Province / City'); $placeholders['city'] = _t('@geo', 'City'); $placeholders['country'] = _t('@geo', 'Country'); } else { $placeholders['region'] = _t('@geo', 'Active Region'); } if (! $fill) { $template->placeholders($placeholders); return $placeholders; } # Load $data = ['region' => '', 'country' => '', 'city' => '']; for ($i = 0; $i <= $depth; $i++) { $data['region' . $i] = ''; } $id = $id ?? $this->filter('id'); if ($id) { $data['region'] = $this->regionData($id); } else { $regionID = 0; switch ($this->coveringType()) { case static::COVERING_COUNTRIES: case static::COVERING_COUNTRY: $regionID = $this->defaultCountry(); break; case static::COVERING_REGION: case static::COVERING_CITY: $regionID = $this->coveringRegion(); break; } if ($regionID > 0) { $data['region'] = $this->regionData($regionID); } } if (! empty($data['region']['id'])) { if ($this->isCity($data['region'])) { $data['city'] = $data['region']; } $data['country'] = ($data['region']['numlevel'] ?? 0) == 1 ? $data['region'] : ''; if (! empty($data['region']['numlevel'])) { $data['region' . $data['region']['numlevel']] = $data['region']; } foreach ($data['region']['parents'] ?? [] as $i => $parent) { $data['region' . $i] = $this->regionData($parent['id']); if ($i == 1) { $data['country'] = $this->regionData($parent['id']); } } } # Fill foreach (array_keys($placeholders) as $key) { if (! empty($data[$key])) { $region = $data[$key]; if (is_array($region)) { $data[$key] = $region['title']; } if (! isset($data[$key . '.in'])) { if (! empty($region['declension'])) { if (is_array($region['declension'])) { if (isset($region['declension']['where'])) { $data[$key . '.in'] = $region['declension']['where']; } else { $data[$key . '.in'] = reset($region['declension']); } } else { $data[$key . '.in'] = $region['declension']; } } else { $data[$key . '.in'] = ''; } } if (! isset($data[$key . '.key'])) { if (! empty($region['keyword'])) { $data[$key . '.key'] = $region['keyword']; } else { $data[$key . '.key'] = ''; } } } elseif (isset($data[$key])) { if (is_array($data[$key])) { $data[$key] = ''; } if (! isset($data[$key . '.in'])) { $data[$key . '.in'] = ''; } if (! isset($data[$key . '.key'])) { $data[$key . '.key'] = ''; } } } $template->with($data); return $data; } }