illusiard / yii2-base-entity-system
Base Entity System for Yii2 (meta layer, bulk events, universal API)
Package info
github.com/Illusiard/yii2-base-entity-system
Type:yii2-extension
pkg:composer/illusiard/yii2-base-entity-system
Requires
- php: >=8.5
- ext-mbstring: *
- illusiard/yii2-db-toolkit: v0.4.0
- illusiard/yii2-entity-acl: v1.2.0
- illusiard/yii2-mass-event-layer: v0.2.0
- yiisoft/yii2: ^2.0.55
- yiisoft/yii2-redis: ^2.1
Requires (Dev)
- illusiard/yii2-testkit: 0.2.0
- phpunit/phpunit: ^10.5
- yiisoft/yii2-gii: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-17 20:06:55 UTC
README
illusiard/yii2-base-entity-system (BES) - meta-driven CRUDL/search/schema слой для Yii2 с reference SPA и локальным dev host для проверки живого API.
Состав Репозитория
src/- основной backend package BES: Yii2 module, bootstrap, registry, entity runtime, HTTP controllers, search, schema tooling, ACL integration, DTO и event facade.tests/- backend unit/integration tests для package contract.dev/host/- локальное Yii2-приложение наyii2-testkit, которое поднимает BES API на SQLite/FileCache для ручной проверки, live integration и browser E2E.view/- reference frontend SPA для BES. Поддерживает mock provider и live API provider.protocol/- protocol snapshots, mock payloads иmeta-state.jsonдля ревью meta/UI состояния.
Ключевые backend-слои:
Module- конфигурация BES в Yii2 application.Bootstrap- подключает модуль, строит runtime и регистрирует routes.BaseEntitySystem- центральный runtime object: DB/cache, registry, ACL, formatter,EventHub,urlPrefix.BaseEntitySystemBuilder- сборка BES runtime из module config.EntityRegistry- регистрация entity classes, meta providers и relation introspection.EntityController- CRUDL HTTP surface.EntityMetaController- entity index, meta и ACL/actions manifest.SchemaController- schema introspection и controlled schema write tooling.BaseActiveRecordиBaseActiveRecordQuery- model/query foundation, включая soft delete.SearchEngine- list/search pipeline: filters, sort, view presets, pagination, deleted filter.ACL- integration сyii2-entity-acl, compact operation masks и relation ACL.DTO- typed meta/UI contract:EntityMeta,EntityUi,FieldMeta,RelationMeta,RelationViaMeta,ViewPresetи related DTO.
Backend API
Dev host по умолчанию использует prefix:
http://127.0.0.1:8081/api/v1
Entity Endpoints
GET /entity
GET /{entity}/meta
GET /{entity}/actions
GET /{entity}/actions?id={id}
GET /{entity}/list
GET /{entity}/view?id={id}
POST /{entity}/create
POST /{entity}/update?id={id}
POST /{entity}/delete?id={id}
POST /{entity}/restore?id={id}
POST /{entity}/permanentDelete?id={id}
POST /{entity}/permanent-delete?id={id}
id поддерживает одиночный integer или string primary key, включая UUID. BES не генерирует UUID сам: значение должно назначаться моделью host-приложения или базой данных. Primary key исключается из writable body, а типы внешних ключей должны совпадать с типом связанного primary key.
delete использует model-level semantics: для моделей с activity field is_active выполняется soft delete, для моделей без activity field остается hard delete. restore восстанавливает soft-deleted запись. permanentDelete и alias permanent-delete удаляют запись физически.
List/Search Query
GET /{entity}/list поддерживает:
page- номер страницы.pageSize- размер страницы.sort- поле сортировки,-fieldдля descending.view- имяViewPreset.filter[field]=value- field filters.deleted=0- только активные записи.deleted=1- только soft-deleted записи.deleted=-1- все записи.
По умолчанию soft-deleted записи скрыты, если у модели есть activity field. Для сущностей без activity field параметр deleted не навязывает искусственный фильтр.
Body И Headers
create и update принимают JSON body с полями модели:
{
"title": "Runtime Notes",
"author_id": 1,
"is_active": true
}
Для JSON requests используйте Content-Type: application/json. Dev host не требует отдельного auth header и работает с permissive ACL; в production-приложении authentication/authorization остаются ответственностью host app и ACL policy.
Пагинация list response передается через headers:
X-Pagination-Total-Count
X-Pagination-Page-Count
X-Pagination-Current-Page
X-Pagination-Per-Page
Для browser clients dev host также выставляет CORS headers и expose headers для pagination.
Wire Format
Успешные ответы intentionally small:
{"items": []}
{"item": {}}
{"success": true}
actions возвращает compact ACL manifest:
{
"entityAcl": 127,
"recordAcl": 127,
"relationsAcl": {
"author": {
"entityAcl": 127,
"recordAcl": null
}
}
}
Formatted entity rows/items включают business fields, refs и acl. refs используются frontend runtime для relation labels:
{
"author_id": {
"entity": "author",
"id": 1,
"title": "Dana Writer"
},
"id": {
"entity": "tag",
"title": "Tags",
"count": 2
}
}
acl содержит compact bit mask операций:
| Operation | Bit |
|---|---|
list |
1 |
read |
2 |
create |
4 |
update |
8 |
delete |
16 |
restore |
32 |
permanentDelete |
64 |
Dev host permissive ACL обычно возвращает 127 для полной маски.
ACL Subject And Object Context
User IDs и record IDs в ACL integration принимают int|string, поэтому UUID передается в yii2-entity-acl без приведения к integer. Для передачи групп текущая Yii identity может реализовать AclSubjectInterface:
use illusiard\baseentity\interfaces\AclSubjectInterface; use yii\web\IdentityInterface; final class UserIdentity implements IdentityInterface, AclSubjectInterface { public function getGroupIds(): array { return $this->groupIds; } }
getGroupIds() возвращает список integer/string identifiers. BES передает его ACL adapter в context key groupIds. Группы используются только для текущей identity, чей getId() совпадает с проверяемым user ID; проверка через EntityEngine::forUser() для другого пользователя не получает группы текущего пользователя.
Record-level owner context задается только моделью, явно реализующей AclObjectInterface:
use illusiard\baseentity\components\base\BaseModel; use illusiard\baseentity\interfaces\AclObjectInterface; final class Document extends BaseModel implements AclObjectInterface { public function getOwnerId(): int|string { return $this->owner_id; } }
BES передает результат getOwnerId() в context key ownerId для загруженной записи, включая list formatting, record-level CRUD access checks, record actions manifest и relation read checks. Если модель не реализует интерфейс или запись не найдена, owner context отсутствует. BES не подставляет текущий user ID вместо owner ID.
Error Format
Ошибки нормализуются в envelope:
{
"error": {
"type": "not_found",
"message": "Record not found",
"details": {}
}
}
Типичные типы:
bad_request- некорректный query/body.forbidden- ACL запретил операцию.not_found- сущность или запись не найдена.conflict- generic HTTP 409 conflict.validation- ошибки валидации модели, обычно HTTP 422.integrity- DB/reference integrity conflict, обычно HTTP 409.internal_error- непредвиденная ошибка, generic message для клиента.
Meta/UI Contract
BES использует typed DTO для meta и UI contract:
EntityMeta- fields, relations, operations, UI и views.EntityUi-listFields,viewFields,formFields,filterFields,readOnlyFields,hiddenFields,hiddenRelations,sortFields,titleField,fieldUi,relations.FieldMeta- field type, label, required/writable/readable flags.RelationMeta-link,targetEntity,kind, optional via metadata.RelationViaMeta-type,table,name,link,targetEntity.ViewPreset- named list preset: fields, filter, sort, page size and title field.
Field Security
FieldMeta.security описывает безопасность поля через DTO Security. Свойство является дополнением к protocol contract; если старый provider или consumer не передает security, применяется mode: normal.
Поддерживаются три режима:
normalсохраняет прежнее поведение BES;protectedразрешает domain-коду работать со значением, но исключает его из generic responses, refs, title resolution, list/view/filter/sort/export и old/new event values;passwordимеет те же запреты и дополнительно является строго write-only: generic form использует password input, не получает сохраненное значение и не отправляет пустое неизмененное поле при update.
protected и password не входят в автоматически построенные UI schemas. Provider может явно добавить их в ui.formFields; наличие DB column само по себе не делает password-поле generic-writable. Пустая строка не очищает password через generic update. Очистка требует отдельной явно выраженной host-level операции.
Попытка filter/sort по чувствительному полю возвращает search error с message code restricted_field_operation. Error details содержат только operation и имя поля, без присланного значения. Явный fields не может принудительно вернуть чувствительное значение.
FieldSecurityPolicy является общим public contract для formatter/search/UI/event/audit boundaries. В частности, внешний журнал может использовать sanitizeRecord(), sanitizeValidationErrors(), sanitizeChange() и sanitizeMassEvent() перед сериализацией. BES очищает собственные state/activity events и структурированные MEL payloads; отдельной audit-подсистемы в этом репозитории нет.
BES не определяет security mode по имени column или DB type и не выполняет шифрование либо хеширование. Provider задает mode явно, а подготовка hash/ciphertext остается ответственностью владеющего модуля.
Meta, ActiveRecord model, DB schema и relation getters должны быть согласованы. Нельзя объявлять writable поле, которое модель не принимает через load(), или relation в meta без рабочего AR relation getter.
Текущий snapshot всех зарегистрированных meta/UI структур хранится в:
protocol/meta-state.json
Его можно обновить через dev host helper:
/usr/bin/php8.5 dev/host/bin/export-meta-state.php
Snapshot read-only: он не генерирует и не меняет runtime meta.
Soft Delete
Foundation-level semantics:
- Activity field на текущем этапе определяется конвенцией
is_active. BaseActiveRecord::find()возвращаетBaseActiveRecordQuery.- Query API:
deleted(0),deleted(1),deleted(-1). find()и list/search по умолчанию скрывают soft-deleted записи.delete()делает soft delete для моделей с activity field.restore()устанавливает activity field обратно в active.forceDelete()и APIpermanentDeleteудаляют физически.- Для моделей без activity field
delete()остается hard delete.
Mass operations вроде deleteAll() этим foundation-слоем автоматически не перехватываются.
Event Facade
BES содержит централизованный EventHub, доступный через BaseEntitySystem::getEventHub().
Основной API:
$hub->on($eventName, $callback); $hub->off($eventName, $callback); $hub->trigger($eventName, $payload);
Поддерживаемые BES events:
entity.activityChangedсActivityChangedPayload.entity.stateChangedсStateChangedPayload.
MEL events также прокидываются через этот facade, чтобы клиенты могли подписываться через одну точку. Soft delete/restore/permanentDelete не имеют отдельной скрытой event-магии: события активности и состояния срабатывают через существующий afterSave/behavior path.
Schema API
Schema endpoints:
GET /schema/tables
GET /schema/table/{name}
POST /schema/diff
POST /schema/migration
Read endpoints доступны для introspection. table/{name} принимает только имена с символами a-zA-Z0-9_.-.
diff и migration принимают JSON body с обязательным объектом desired в формате schema DTO из illusiard/dbtoolkit:
{
"desired": {
"tables": []
},
"options": {
"className": "m260101_000001_update_schema",
"namespace": "app\\migrations",
"transactional": true,
"useTablePrefix": false
}
}
Для migration options также поддерживается flat form на root-level: className, namespace, transactional, useTablePrefix. diff возвращает {"actions": [...]}, migration возвращает {"className": "...", "code": "..."}. Write tooling (diff, migration) должен включаться явно через controlled module config, например allowSchemaWriteTools, и не предназначен для открытой production-среды.
Dev Host
dev/host/ - локальный integration host. Он нужен для ручной проверки, live API integration tests и browser E2E.
Особенности:
- Собирается через
illusiard/yii2-testkit. - Использует SQLite:
dev/host/runtime/bes-dev.sqlite. - Использует Yii
FileCacheвdev/host/runtime. - Создает schema и seed при первом запуске.
- Регистрирует demo entities:
post,author,tag. - Использует permissive ACL policy для ручной проверки всех операций.
- Подключает CORS для frontend dev server.
Запуск из корня репозитория:
composer install /usr/bin/php8.5 -S 127.0.0.1:8081 -t dev/host/web dev/host/web/router.php
Live API base URL:
http://127.0.0.1:8081/api/v1
Smoke-check dev host:
bash dev/host/bin/smoke-http.sh
Dev host stateful. Если нужно начать с чистого состояния, удалите SQLite/cache runtime файлы в dev/host/runtime/ и запустите host снова.
Frontend SPA
view/ - reference SPA для проверки backend protocol в реальном runtime. Runtime architecture сохраняет направление:
container components -> screen model -> composables -> small UI components
Providers:
mock- isolated frontend/dev/test режим без backend.api- live provider поверх BES HTTP API.
Запуск mock provider:
cd view
npm install
VITE_BES_PROVIDER=mock npm run dev
Запуск live API provider:
/usr/bin/php8.5 -S 127.0.0.1:8081 -t dev/host/web dev/host/web/router.php
cd view
VITE_BES_PROVIDER=api VITE_BES_API_BASE_URL=http://127.0.0.1:8081/api/v1 npm run dev
Во frontend refs и ACL bit masks нормализуются через EntityFormatter/runtime provider path. listFields, popup stack и related popup flows закреплены в runtime. Нестандартные scenario/debug controls остаются dev/test harness и не являются частью production UI contract.
Tests
Backend unit/integration tests:
composer install vendor/bin/phpunit
Dev host HTTP smoke:
/usr/bin/php8.5 -S 127.0.0.1:8081 -t dev/host/web dev/host/web/router.php bash dev/host/bin/smoke-http.sh
Frontend Vitest mock/integration suite:
cd view npm install npm test
Эта suite включает provider/runtime contract tests, включая view/src/integration/__tests__/api-provider.integration.test.js, и работает без backend через mock/stub providers.
Live API integration tests against running dev host:
cd view
npm run test:live
test:live использует:
BES_LIVE_API=1
VITE_BES_PROVIDER=api
VITE_BES_API_BASE_URL=http://127.0.0.1:8081/api/v1
Основной live integration файл: view/src/integration/__tests__/live-api.integration.test.js. Dev host должен быть уже поднят на http://127.0.0.1:8081/api/v1.
Browser E2E через Playwright:
cd view
npm run test:e2e
Только live API E2E сценарии:
cd view
npm run test:e2e-live
Playwright config поднимает dev host и Vite frontend с live API provider. По умолчанию Chromium-compatible browser ожидается как:
/usr/bin/google-chrome
Если Chrome установлен в другом месте, настройте environment для Playwright, например PLAYWRIGHT_CHROMIUM_EXECUTABLE. Base URLs также можно переопределять через PLAYWRIGHT_FRONTEND_BASE_URL и PLAYWRIGHT_BACKEND_BASE_URL.
Current Coverage
Backend tests закрепляют:
- meta/DTO/schema contract;
- CRUDL endpoints;
- soft delete foundation;
restoreиpermanentDelete;deleted=-1|0|1search/list semantics;- ACL operation masks;
EventHubsubscription/unsubscription и payload triggers;- meta-state snapshot generation.
Live frontend integration и browser E2E покрывают:
- root list;
- view popup из list;
- update popup из view;
- root create;
- FK selectors и create-select popup chain;
- refs и ACL bits на live API;
- soft delete, restore, permanentDelete;
deletedquery parameter;- validation, forbidden, not_found, integrity/internal error paths где они относятся к provider/UI contract.
Mock provider tests остаются отдельным isolation layer для frontend-only сценариев.
Release Readiness
Текущий release status, host-app production contract и regression pack зафиксированы в:
RELEASE.md
На уровне репозитория known release blockers отсутствуют. Production-готовность зависит от host app: authentication, yii2-entity-acl policy, инфраструктура cache/db и закрытый доступ к schema write tooling.
Ограничения
- Dev host использует permissive ACL, поэтому он удобен для ручных CRUDL проверок, но не моделирует production policy.
- SQLite/FileCache изолированы в
dev/host/runtimeи предназначены только для локального dev/test. - Soft delete автоматически работает только для моделей с activity field
is_active. deleteAll()и другие mass delete operations не переводятся автоматически в soft delete.- Schema write endpoints должны быть включены явно и не должны быть открыты в production без дополнительной защиты.
- Frontend scenario/debug controls относятся только к dev/test harness.