143 lines
6.8 KiB
Markdown
143 lines
6.8 KiB
Markdown
# 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`.
|
||
- Пустой пул — проверьте доступ к БД и креды в конфиге.
|
||
|