Files
ORM/README.md
2026-02-07 15:54:21 +03:00

143 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.
- Пустой пул — проверьте доступ к БД и креды в конфиге.