first commit

This commit is contained in:
2026-02-07 15:54:21 +03:00
commit 74964c74f3
51 changed files with 3701 additions and 0 deletions

142
README.md Normal file
View File

@@ -0,0 +1,142 @@
# 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`.
- Пустой пул — проверьте доступ к БД и креды в конфиге.