# Database (PocketMine-MP) Плагин для PMMP 5.36+ с поддержкой PostgreSQL/MySQL через PDO, пулом соединений, миграциями, ORM на атрибутах и кэшем сущностей. ## Требования - PHP 8.1+ (как у PMMP 5.x). - Расширения: `pdo` + `pdo_pgsql` (PostgreSQL) или `pdo_mysql` (MySQL). Проверяется при запуске. ## Установка 1. Соберите плагин (zip папку `pmmp/Database` или сборка через DevTools). 2. Поместите архив/папку в `plugins/`. 3. Запустите сервер один раз для генерации `config.yml`. 4. Заполните `config.yml`, перезапустите сервер. ## Конфигурация (`config.yml`) ```yaml driver: "postgres" # postgres | mysql host: "127.0.0.1" port: 5432 # 3306 для mysql по умолчанию database: "server" username: "postgres" password: "" options: persistent: false # persistent соединения PDO timeoutSeconds: 10 pool: size: 4 # размер пула PDO cache: enabled: true writeBehind: true flushIntervalSeconds: 10 maxDirty: 100 ttlSeconds: 0 maxEntries: 0 preloadBatchSize: 20 preloadDelayTicks: 20 preloadAsync: true migrations: enabled: true table: "db_migrations" ``` ## Быстрый старт ```php use andrewkydev\Database\DatabaseProvider; use andrewkydev\Database\ORM\Attributes as ORM; use andrewkydev\Database\ORM\Conditions; $db = DatabaseProvider::get(); // Raw SQL $db->query()->execute("UPDATE players SET level = level + 1 WHERE username = ?", ["Steve"]); // ORM-модель #[ORM\Entity(table: "players")] final class Player { #[ORM\Id(autoIncrement: true)] #[ORM\Column(name: "id")] public int $id; #[ORM\Column(unique: true, length: 32)] #[andrewkydev\Database\ORM\Attributes\CacheKey] public string $username; #[ORM\Column] public int $level; } $orm = $db->orm(); $orm->enableCache(Player::class, preload: true); $player = $orm->findOneWhere(Player::class, "username = ?", ["Steve"]); $top = $orm->query(Player::class) ->where(Conditions::gt("level", 10)) ->orderBy("level DESC") ->limit(10) ->list(); ``` ## Компоненты API - `DatabaseApi` (через `DatabaseProvider::get()`): - `config()` — текущий `DatabaseConfig`. - `schema()` — `SchemaManager` (DDL: create/drop db/table, колонки, индексы). - `query()` — `QueryRunner` (сырой SQL sync/async). - `orm()` — `EntityManager` (ORM, кэш, QueryBuilder). - `migrations()` — `MigrationManager`. ### Сырые запросы (`QueryRunner`) - `execute($sql, $params=[])` — `rowCount()`. - `query($sql, $params=[], $mapper=null)` — массив строк или маппер. - `executeAsync/queryAsync($sql, $params, $onSuccess, $onError=null)` — выполняется в async-пуле PMMP, создаёт отдельный PDO. ### ORM и атрибуты - Основные атрибуты: `Entity`, `Id(autoIncrement)`, `Column(name, nullable, length, unique, type)`, `Json`, `CacheKey`, `Transient`. - Связи: `OneToOne`, `ManyToOne`, `OneToMany`, `ManyToMany` (`target`, `mappedBy`, `joinColumn`, `referencedColumn`, `joinTable`, `inverseJoinColumn`, `cascadePersist/remove`, `lazy`). - Ленивая загрузка свойств: `LazyRelationsTrait`. ### Операции ORM (`EntityManager`) - CRUD: `insert/update/delete/save` + async аналоги. - Поиск: `findById/findAll/findOneWhere/findWhere/count/exists/deleteWhere` (+ async). - Построитель: `query(Class)::QueryBuilder` (select/where/and/or/group/having/order/limit, `joinFetch` для manyToOne/oneToOne, `withRelations` для загрузки связей, async `listAsync/oneAsync/countAsync/existsAsync/deleteAsync`). - Загрузка связей: `loadRelation(s)` и батч `loadRelationsFor`. ### Условия (`Conditions`) `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `like`, `in`, `isNull`, `isNotNull`, `raw`. Комбинации через `Condition->and()/or()`. ### Кэш сущностей - Включается `cache.enabled`. - Регистрация: `$orm->enableCache(Model::class, ['id','username'], preload: true);` - Ключи по умолчанию: Id, поля с `CacheKey`, уникальные колонки. - `writeBehind=true`: `update/delete` складываются в очередь (`CacheManager`) и сбрасываются периодически (`CacheFlushTask`) или при достижении `maxDirty`. - `ttlSeconds` и `maxEntries` контролируют размер/старение. Прогрев — `CachePreloadTask`/`CachePreloadAsyncTask`. ### Миграции - Интерфейс `Migration { version(): int; up(SchemaManager $schema, QueryRunner $query): void; }` - Регистрация: `$db->migrations()->register(new class implements Migration {...});` - Запуск: `$db->migrations()->runPending();` (создаёт таблицу `migrations.table` при необходимости). - Генерация схемы по модели: `OrmSchema::fromEntity(Model::class, SqlDialect::POSTGRES|MYSQL)` → `SchemaManager::createTable(...)`. ### DDL (`SchemaManager`) - `createDatabase/dropDatabase` - `createTable(TableSpec)`, `dropTable` - `addColumn/updateColumn/dropColumn` - `addIndex/dropIndex` (учитывает синтаксис Postgres/MySQL). ## Асинхронность - Все async методы используют `Server::getInstance()->getAsyncPool()` и собственный PDO, не блокируя main thread. - Колбэки: `onSuccess($rows|$entity|$count)` и опциональный `onError($message)`. ## Практические советы - Всегда задавайте `#[Id]` и уникальные поля — это улучшает кэш и генерацию схемы. - Для критичных данных можно отключить `writeBehind`, чтобы обновления уходили сразу. - `joinFetch` используйте для `manyToOne/oneToOne`, а большие коллекции грузите через `withRelations` или отдельные запросы. - Postgres: автоинкремент переводится в `SERIAL/BIGSERIAL`; MySQL — `AUTO_INCREMENT`. ## Отладка и проблемы - Ошибка о расширениях — установите/включите `pdo`, `pdo_pgsql` или `pdo_mysql`. - `DatabaseApi is not initialized` — плагин ещё не загрузился; обращайтесь к `DatabaseProvider::get()` после `onEnable`. - Пустой пул — проверьте доступ к БД и креды в конфиге.