В EE_FrameWork модель — это слой данных и инвариантов, а не место для HTML, redirect или UI-решений.
Есть два основных способа.
$this->loadModel('m_pages');
$page = $this->models['m_pages']->getPageData($pageId, $languageCode);Это стандартный путь для controller/trait кода.
$modelPages = SysClass::getModelObject('admin', 'm_pages');
$page = $modelPages->getPageData($pageId, 'RU');Этот путь удобен:
- в service-классах;
- в hooks;
- в lifecycle-коде;
- в import/cron сценариях.
Внутри проекта нет одного абстрактного интерфейса на все модели, но практический стандарт такой:
- методы чтения возвращают массивы, scalar-значения или
null; - методы мутации возвращают
OperationResult; - модель сама делает SQL-фильтрацию и нормализацию входа;
- контроллер не должен гадать по типу результата.
Для pages и categories мультиязычность строится не через одну запись с множеством полей, а через отдельные сущности на каждый язык:
ee_pages.language_codeee_categories.language_codeee_entity_translationsдля связи языковых версий между собой
За orchestration этого слоя отвечает EntityTranslationService.
Практический workflow такой:
- модель сохраняет страницу или категорию в конкретной локали;
- после сохранения вызывается
EntityTranslationService::ensureEntity(...); - при создании нового перевода используется
linkEntityToSource(...); - property values копируются в новую языковую версию через
duplicatePropertyValuesFromSource(...).
Это позволяет вести три языка как три независимые записи, но при этом не терять связь между версиями в админке и сервисном коде.
Если метод модели меняет состояние, возвращайте:
OperationResult::success(...)
OperationResult::validation(...)
OperationResult::failure(...)Пример:
return OperationResult::success($pageId, '', 'updated');
return OperationResult::validation('Не указан title', $pageData);
return OperationResult::failure('Ошибка сохранения страницы', 'page_save_failed', $pageData);Контроллер потом работает только через единый путь:
$result = $this->notifyOperationResult(
$this->models['m_pages']->updatePageData($postData, $languageCode),
['success_message' => 'Страница сохранена']
);Практический стандарт границы такой:
- модель не возвращает
false,['error' => ...]илиErrorLoggerнаружу как основной контракт; - controller, trait, importer и cron-код получают единый результат;
- чтение может оставаться на массивах и scalar-значениях, но мутация должна быть стандартизована.
return OperationResult::success($pageId, '', 'updated');
return OperationResult::success(['page_id' => $pageId], '', 'created');return OperationResult::validation('Не указан title', $pageData);return OperationResult::failure(
'Ошибка сохранения страницы',
'page_save_failed',
$pageData
);Если код ещё находится в переходном сценарии, допускается адаптер OperationResult::fromLegacy(...), но для новых методов это уже не считается нормой.
Основной рабочий слой — SafeMySQL.
Типовые операции:
$row = SafeMySQL::gi()->getRow('SELECT * FROM ?n WHERE page_id = ?i', Constants::PAGES_TABLE, $pageId);
$rows = SafeMySQL::gi()->getAll('SELECT * FROM ?n WHERE category_id = ?i', Constants::PAGES_TABLE, $categoryId);
$value = SafeMySQL::gi()->getOne('SELECT COUNT(*) FROM ?n', Constants::PAGES_TABLE);
$ok = SafeMySQL::gi()->query('UPDATE ?n SET ?u WHERE page_id = ?i', Constants::PAGES_TABLE, $payload, $pageId);Практические правила:
- фильтруйте массивы через
SafeMySQL::gi()->filterArray(...); - не давайте контроллеру формировать SQL;
- пользуйтесь
Constants::*_TABLE, а не строками-именами таблиц по коду; - держите в модели нормализацию, а не в каждом контроллере отдельно.
Слои валидации такие:
Отвечает за:
- общий доступ к action;
- базовую форму запроса;
- выбор языка;
- наличие обязательных route-параметров.
Отвечает за:
- бизнес-валидацию;
- SQL-safe нормализацию;
- проверку инвариантов;
- дубликаты;
- связи между сущностями.
- не отправляйте redirect из модели;
- не вызывайте layout/view;
- не генерируйте HTML;
- не полагайтесь на bell-уведомления как на единственный канал ошибки.
Используйте транзакцию, если операция:
- пишет в несколько таблиц;
- меняет связи и сущность одновременно;
- может оставить систему в частично обновлённом состоянии.
Типичный пример:
- удаление сущности + чистка связанных property values;
- import;
- auth lifecycle;
- крупная lifecycle-синхронизация.
Смотрите:
- сохранение сущностей в
ModelPages,ModelCategories; - сохранение свойств в
ModelProperties; - import-путь в
WordpressImporter; - единый контракт результата в
OperationResult; - Контентная модель и сущности.