# WP Multi City Мультигородская подсистема для WordPress: один сайт обслуживает несколько городов по схеме `/{city}/{path}/` с подстановкой контента, ссылок и шорткодов. **Версия:** 1.0.0 — MVP завершён. ## Требования - WordPress 6.0+ - PHP 8.0+ - Advanced Custom Fields Pro ## Установка 1. Скачать zip последнего релиза: 2. WP admin → Plugins → Add new → Upload (или распаковать в `wp-content/plugins/wp-multi-city/`). 3. Активировать. Если ACF Pro не активен — увидите admin notice об ошибке зависимости. Обновления приходят автоматически: плагин-update-checker опрашивает Gitea hourly. Force-check: WP admin → Plugins → «Check again». ## Quick Start (5 минут) 1. WP admin → **Шорткоды → Города** → создать term `tyumen`, `spb`. 2. WP admin → **WP Multi City** → выставить `main_town_slug` = `spb`. 3. WP admin → **Шорткоды → Добавить** пост: - title: `Тарифы` - ACF repeater `list_shortcode`: row town=tyumen, text="Тюменский тариф". - Запомнить ID поста (например 42). 4. Создать страницу с контентом `[shortcode_dmr id=42]`. 5. Открыть `/tyumen//` → увидеть «Тюменский тариф». Открыть `//` без префикса → пусто (main_town=spb, не tyumen). ## Hook API ### Процедурные функции | Функция | Возвращает | Назначение | |---|---|---| | `wpmc_current_town()` | `?string` | town slug из текущего URL или null | | `wpmc_get_term_by_slug($slug)` | `?\WP_Term` | `town`-term по slug | | `wpmc_alt_get_field($key, $post_id)` | `mixed` | `get_field` с fallback на term meta | | `wpmc_remove_domain_link($url)` | `string` | strip `home_url` prefix | | `wpmc_main_town_slug()` | `?string` | slug из options page | ### WP-фильтры от плагина - `the_title` / `single_cat_title` → `do_shortcode` (priority 9999, после Yoast SEO). - `home_url` / `post_link` / `acf/load_value` → `LinkFilter::add_town_prefix` (auto-prefix URL текущим town). - `rest_url` → `RestUrlFilter::preserve_host` (форсирует HTTPS + `HTTP_HOST`). ### Bypass URL-фильтров Когда нужно построить «канонический» URL без городского префикса: ```php $canonical = \WPMultiCity\Helpers::without_url_filters( fn() => get_permalink($post_id) ); ``` Counter-based, поддерживает вложенные вызовы. ## Migration guide (из темы washanyanya) 1. В `wp-content/themes/washanyanya/functions.php` закомментировать `require_once get_template_directory() . '/inc/town-functions.php';`. 2. Активировать wp-multi-city. 3. ACF Pro field group из темы → re-import в новую location (CPT `shortcode-town`). 4. Заменить вызовы `current_town()` на `wpmc_current_town()`. Либо прописать алиас в functions.php темы: ```php if (function_exists('wpmc_current_town')) { function current_town(): ?string { return wpmc_current_town(); } } ``` 5. Проверить: `/tyumen/about/` отдаёт правильный page; меню префиксится; `[shortcode_dmr]` рендерит. ## Testing ### Automated (PHPUnit) ```bash composer install vendor/bin/phpunit ``` Expected: 104 tests, ~185 assertions green. Unit-level coverage via Brain Monkey + Mockery + Patchwork — no real WordPress required; полный suite за <1 секунду. ### Manual QA Для end-to-end проверки против реального WordPress см. `docs/QA-CHECKLIST.md` — 13 секций (активация, admin UI, helpers, shortcode, router, page clones, URL filters, REST URL). ## CI `.gitea/workflows/test.yml` запускает PHPUnit на каждый push в `main` и каждый PR. Активируется автоматически после push в Gitea. ## Roadmap MVP | Этап | Содержание | Beads | Статус | |---|---|---|---| | S1 | Скелет (header, autoload, bootstrap) | cp-8ci | done | | S2 | Taxonomy `town` + CPT `shortcode-town` + ACF `list_shortcode` | cp-q7g | done | | S3 | `[shortcode_dmr id=N]` + хелперы | cp-arq | done | | S4 | URL-роутер `/{city}/{path}/` | cp-83m | done | | S5 | Глобальные фильтры (`the_title`/URL/`rest_url`) | cp-i17 | done | | S6 | CI workflow + manual QA checklist | cp-la7 | done | | S7 | Gitea release + plugin-update-checker | cp-7cf | done | Эпик: `cp-cw4` — MVP CLOSED. ## Источник Плагин — переработка кастомной мультигородской подсистемы темы `washanyanya` (1555 строк `inc/town-functions.php`) в переиспользуемый плагин для других проектов. ## Лицензия GPLv2 или новее.