Какой фреймворк лучше подойдёт — Yii3 или Symfony — для разработки и поддержки множества сайтов?

Нужно создать несколько сайтов, которые могут иметь как много общих компонентов, так и быть полностью разными. Часть таблиц БД будет общей (универсальные сущности, например languages с полями id, code, title), другая — специфичной для каждого сайта (products, articles и т.п.), при этом некоторые имена таблиц могут совпадать (например categories). Разделять таблицы по разным базам нежелательно, по крайней мере для общих таблиц, так как планируется использовать внешние ключи и JOIN, поэтому рассматривается вариант использования префиксов для несущих общих таблиц. <br/> Какой фреймворк и какую структуру проекта (включая организацию БД и способ переиспользования общих частей) вы порекомендуете, чтобы минимизировать копипаст общих компонентов?
  • 9 мая 2026 г. 11:01
Ответы на вопрос 3
Коротко: если вы делаете «корпоративный» проект с множеством сайтов, долгой поддержкой, необходимостью аккуратно инкапсулировать повторно используемые куски и иметь мощный ORM/миграции — чаще всего я рекомендую Symfony. Если вам важна скорость разработки, лёгкость и вы уже хорошо знаете Yii — Yii3 тоже жизнеспособен, но экосистема/инструменты для построения большого модульного монорепо у Symfony более зрелые.

Ниже — подробная мотивация и практические рекомендации по структуре проекта, БД и переиспользованию общих частей (подходы, которые минимизируют копипаст).

1) Почему чаще выбирают Symfony для такого сценария
- Сильная модульная архитектура (bundles, компоненты), стандартная экосистема (Composer, Flex), удобная DI, зрелая интеграция с Doctrine ORM и миграциями.
- Doctrine даёт гибкие механизмы — кастомная NamingStrategy / event listeners / несколько entity‑manager’ов — что удобно для сценариев с префиксами/разделением таблиц.
- Легко делать переиспользуемые composer‑пакеты (shared entities, сервисы, UI компоненты), которые подключаются в разные сайты/ядра.
- Хорошие практики для монорепо / мультикернельных приложений (несколько kernel’ов/конфигов в одном коде).

2) Когда стоит рассмотреть Yii3
- Быстрая разработка, меньше бойлерплейта, если ваша команда уже знает Yii.
- В Yii удобно работать с tablePrefix и AR ({{%table}}), простая система модулей.
- Но экосистема Yii3 ещё моложе, и в больших мультисайтовых системах вам придётся самостоятельно выстраивать некоторые архитектурные шаблоны, которые для Symfony уже «из коробки».

3) Рекомендации по архитектуре кода (независимо от фреймворка)
- Монорепозиторий с внутренними пакетами (recommended):
  - Папка /packages/common — общие сущности (entities/models), сервисы, репозитории, shared migrations.
  - /packages/features — переиспользуемые модули/бандлы (категории, авторизация, i18n и т. п.).
  - /sites/site-a, /sites/site-b — приложение/конфиг/темы/специфичные миграции для каждого сайта.
  - Управление зависимостями через Composer (path repositories в dev, private packages в prod).
- Каждый пакет имеет свои миграции и namespace миграций. При деплое запускаете общие миграции, затем миграции сайтов.

4) Организация БД — варианты и рекомендации
Вариант A — одна база, префиксы таблиц для site‑specific таблиц (как вы описали)
- Общие таблицы (languages, users, settings) создаются «без префикса» (или в отдельной схеме), чтобы все сайты могли ссылаться на них прямо.
- Для каждой «сайтовой» таблицы используйте префикс site1_, site2_. Это удобно если FKs между site‑tables и shared‑tables нужны: site1_products -> languages (unprefixed).
- Минусы: много префиксов в именах, возможные коллизии имён (нужно соглашение).

Тонкий момент: ORM должен поддерживать селективное префиксирование (чтобы общие сущности не получили префикс, а site‑специфичные — получили). Это решается конфигурацией ORM (см. примеры ниже).

Вариант B — Postgres schemas (предпочтительный, если БД — PostgreSQL)
- Общие таблицы в схеме common, сайт‑специфичные в схемах site1, site2.
- Плюсы: чистые имена таблиц, поддержка FKs между схемами, более очевидное разделение.
- Минусы: если вы используете MySQL, это не вариант; некоторые инструменты/ORM настройки потребуют доработки, но Doctrine поддерживает схемы.

Вариант C — отдельные базы — вы отметили, что не хотите (и правда: FK/JOIN между БД в MySQL ограничены), поэтому обычно не рекомендую.

5) Как реализовать префиксы/селективное префиксирование в практике

Symfony + Doctrine: рекомендуемый подход
- Общие сущности: namespace App\Common\Entity (таблицы без префикса).
- Site‑специфичные сущности: namespace App\SiteA\Entity (таблицы без префикса в annotations), но при загрузке метаданных префиксировать их имена динамически.
- Реализуйте подписчик на событие loadClassMetadata (или собственную NamingStrategy), который для entity из заданных пространств имён добавляет префикс:
  - Если entity в namespace SiteA → $meta->table['name'] = $prefixSiteA . $meta->table['name'];
  - Если в namespace Common → не трогать.
- Зарегистрируйте этот listener как сервис и пометьте его тегом doctrine.event_listener (event = loadClassMetadata).
- Миграции: настройте DoctrineMigrations так, чтобы при выполнении миграций для siteA префикс применялся (механика зависит от того, как вы генерируете миграции — чаще генерируете миграции отдельными наборами для common и для каждого сайта).

Пример-идеи (psuedo‑код listener):
  public function loadClassMetadata(LoadClassMetadataEventArgs $args) {
    $meta = $args->getClassMetadata();
    if (strpos($meta->name, 'App\\SiteA\\')===0) {
      $meta->setPrimaryTable(['name' => $this->prefix.'_'.$meta->getTableName()]);
      foreach ($meta->associationMappings as & $m) { /* поправить joinColumns имя */ }
    }
  }

Yii3 вариант
- Используйте два подключения к БД:
  - db_common (без tablePrefix)
  - db_site (с tablePrefix = 'site1_')
- В общих AR-классах переопределите getDb() → возвращать db_common.
- В site‑специфичных AR используйте обычный db (db_site) и в tableName() используйте '{{%table}}' — тогда автоматически получаете префикс.
- Миграции: выполняйте общие миграции против db_common и site‑миграции против db_site.

6) Миграции и поддержка целостности
- Общие таблицы: миграции в пакете common. Деплой: сначала запускаете common migrations, затем site migrations.
- Site migrations: создают префикс‑таблицы и FKs на общие таблицы без префикса.
- Автоматизация: CI/CD скрипты, которые знают порядок миграций.
- Тесты и стейджинг: разворачивайте копию БД с префиксами как в проде, чтобы тестировать миграции.

7) Как минимизировать копипаст общих компонентов
- Вынесите: entities, DTO, репозитории, бизнес‑сервисы, общий UI (twig/шаблоны), форм‑поля, валидаторы — в общие пакеты.
- Используйте интерфейсы + DI, чтобы сайты могли подменять реализацию, не копируя код.
- Храните shared templates/partials отдельно и подключайте их в тему сайта (overrides возможны).
- Общие миграции и seed'ы — в общем пакете.
- Используйте feature toggles / конфиги per site вместо дублирования кода.

8) Практический план внедрения (пример)
- Выбираете фреймворк (Symfony если нет жёсткой зависимости на Yii).
- Создаёте монорепо:
  - /packages/common
  - /packages/bundles (categories, products, i18n)
  - /sites/site1, /sites/site2
- В common: общие entities (Languages), общие миграции.
- Для Symfony: реализуете Doctrine listener для префиксов; для Yii — два подключения (common и site).
- Настройка CI: порядок миграций → deploy.
- Документируете соглашения по неймингу таблиц и namespace’ам.

9) Резюме и рекомендации
- Если проект большой и долгосрочный — выбирайте Symfony. Он даёт больше «готовых» инструментов для управления сложной мультисайтовой архитектурой и повторного использования кода.
- Если команда сильнее в Yii и проект не будет экстремально сложным — можно делать и на Yii3, но планируйте структуру с общими пакетами и несколькими DB‑подключениями заранее.
- По БД: если можно использовать PostgreSQL — рассмотрите схемы (clean и удобнее). Если MySQL — выбранный вами подход с префиксами рабочий; реализуйте префиксинг на стороне ORM (Doctrine listener / Yii tablePrefix) и держите общие таблицы без префикса.
- Организация кода: общие пакеты + per‑site приложения + CI/migrations — это ключ к минимизации копипаста.

Если хотите, могу:
- подготовить пример реализации Doctrine loadClassMetadata listener для селективного префиксирования (реальный PHP‑код и конфигурация в services.yaml), или
- показать пример структуры монорепо и composer.json для общих пакетов + сайтов,
- или показать пример организации подключений и миграций для Yii3.

Что предпочитаете — пример кода для Symfony (Doctrine listener) или для Yii3 (две DB‑конфигурации + AR)?
У вас что-то типа мультенантного (multitenant) приложения с единой админкой. <br/> <br/> 1. В вашем случае чего-то особого именно под вашу задачу вам не даст ни Yii3, ни Symfony, ни какой-либо другой фреймворк. Ну разве что в Yii3 вам может помочь система виджетов (если у вас классический рендеринг HTML, а не какой-то React). <br/> 2. Основная сложность — не выбрать фреймворк, а придумать, как именно выстроить архитектуру. Тут очень сильно зависит от требований к изоляции или не изоляции данных между сайтами, от того, сколько общего у разных сайтов сейчас и, главное, насколько эти сайты могут разойтись в функциональности через год или два (иногда копипаст — добро).
У меня похожая история — в итоге взял Symfony, там это давно обкатано. Есть готовый Doctrine prefix bundle для сайт-специфичных таблиц, общие entity без изменений, куча примеров. Yii3 stable только с декабря, экосистема под такой кейс жиже. <br/> <br/> По структуре мыслишь верно: общий код и модели — в отдельный bundle, который все сайты подключают.
Похожие вопросы