PHPackages                             dynart/micro-entities - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. dynart/micro-entities

ActiveLibrary

dynart/micro-entities
=====================

Entity manager for Micro

00PHP

Since Aug 19Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/goph-R/dynart-micro-entities)[ Packagist](https://packagist.org/packages/dynart/micro-entities)[ RSS](/packages/dynart-micro-entities/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (2)Used By (0)

dynart-micro-entities
=====================

[](#dynart-micro-entities)

PDO-based ORM / entity library for the [dynart-micro](https://github.com/goph-R/dynart-micro) framework.

Provides database abstraction, PHP 8 attribute-driven entity metadata, query building, and full CRUD with dirty-field tracking.

- Namespace: `Dynart\Micro\Entities`
- PHP 8.0+, PSR-4 from `src/`
- Requires: `dynart/micro`, `ext-pdo`, `ext-json`

Installation
------------

[](#installation)

```
composer require dynart/micro-entities
```

Quick Start
-----------

[](#quick-start)

### 1. Define an entity

[](#1-define-an-entity)

```
use Dynart\Micro\Entities\Entity;
use Dynart\Micro\Entities\Attribute\Column;

class User extends Entity {

    #[Column(type: Column::TYPE_INT, primaryKey: true, autoIncrement: true, notNull: true)]
    public int $id = 0;

    #[Column(type: Column::TYPE_STRING, size: 100, notNull: true)]
    public string $name = '';

    #[Column(type: Column::TYPE_STRING, size: 150)]
    public string $email = '';

    #[Column(type: Column::TYPE_BOOL, default: false)]
    public bool $active = false;

    #[Column(type: Column::TYPE_DATETIME, default: Column::NOW)]
    public ?string $created_at = null;
}
```

### 2. Configure the database

[](#2-configure-the-database)

```
; config.ini
database.default.dsn      = "mysql:host=localhost"
database.default.name     = mydb
database.default.username = root
database.default.password = secret
database.default.table_prefix = app_
```

> **Note:** Quote the DSN value if it contains `=` (e.g. `host=localhost`), because `parse_ini_file` with `INI_SCANNER_TYPED` treats bare `=` inside values as a syntax error.

### 3. Wire up services

[](#3-wire-up-services)

Register the services in your dynart-micro application bootstrap:

```
Micro::add(Database::class, MariaDatabase::class);
Micro::add(QueryBuilder::class, MariaQueryBuilder::class);
Micro::add(EntityManager::class);
Micro::add(QueryExecutor::class);
Micro::add(AttributeHandlerInterface::class, ColumnAttributeHandler::class);
```

Enable attribute-based entity discovery:

```
$app->useRouteAnnotations(); // registers AttributeProcessor middleware
```

Or register entities manually:

```
$entityManager->addColumn(User::class, 'id',    new Column(type: Column::TYPE_INT, primaryKey: true, autoIncrement: true, notNull: true));
$entityManager->addColumn(User::class, 'name',  new Column(type: Column::TYPE_STRING, size: 100, notNull: true));
// ...
```

### 4. CRUD

[](#4-crud)

```
// Insert
$user = new User();
$user->name = 'Alice';
$user->email = 'alice@example.com';
$entityManager->save($user);   // INSERT; $user->id is back-filled automatically

// Update (only dirty fields are sent)
$user->name = 'Bob';
$entityManager->save($user);   // UPDATE SET name = 'Bob'

// Find by primary key
$user = $entityManager->findById(User::class, 42);

// Delete
$entityManager->deleteById(User::class, 42);
$entityManager->deleteByIds(User::class, [1, 2, 3]);
```

### 5. Querying

[](#5-querying)

```
use Dynart\Micro\Entities\Query;

$query = new Query(User::class);
$query->addCondition('`active` = :active', [':active' => 1]);
$query->addOrderBy('name');       // works with aliased fields
$query->setLimit(0, 20);

$rows  = $queryExecutor->findAll($query);       // array of assoc arrays
$count = $queryExecutor->findAllCount($query);  // integer
$names = $queryExecutor->findAllColumn($query, 'name'); // flat array
```

### 6. Schema management

[](#6-schema-management)

```
// Create table from entity metadata
$queryExecutor->createTable(User::class);
$queryExecutor->createTable(User::class, ifNotExists: true);

// Inspect
$queryExecutor->isTableExist(User::class);  // bool
$queryExecutor->listTables();               // string[]
```

---

Architecture
------------

[](#architecture)

```
#[Column] attributes on Entity subclasses
        ↓ processed by
ColumnAttributeHandler → EntityManager   (metadata registry)
                               ↓
                          Database        (PDO wrapper, lazy connect)
                               ↓
              QueryBuilder → QueryExecutor (query / DDL execution)

```

### Entity

[](#entity)

`Entity` (abstract) is the base for all persistent objects. It provides:

- **new/persisted flag** — `isNew()` / `setNew()`
- **dirty tracking** — `takeSnapshot()` / `getDirtyFields()` / `isDirty()` / `clearSnapshot()`
- **event names** — `beforeSaveEvent()` / `afterSaveEvent()` return strings like `User.before_save`

### Column attribute

[](#column-attribute)

All column metadata lives on the property's `#[Column]` attribute:

ParameterTypeDescription`type`stringOne of the `TYPE_*` constants`size`int|arrayColumn size; `[precision, scale]` for `numeric``fixSize`boolUse `CHAR` instead of `VARCHAR` for strings`notNull`bool`NOT NULL` constraint`autoIncrement`boolAuto-increment`primaryKey`boolPart of the primary key`default`mixedDefault value; `Column::NOW` for UTC timestamps; wrap in `[]` for raw SQL`foreignKey`array|null`[TargetClass::class, 'column']``onDelete`string|null`Column::ACTION_CASCADE` or `Column::ACTION_SET_NULL``onUpdate`string|null`Column::ACTION_CASCADE` or `Column::ACTION_SET_NULL`**Type constants:**

ConstantMariaDB type`TYPE_INT``int``TYPE_LONG``bigint``TYPE_FLOAT``float``TYPE_DOUBLE``double``TYPE_NUMERIC``decimal(p, s)``TYPE_STRING` (with size)`varchar(n)` / `char(n)` if `fixSize``TYPE_STRING` (no size)`longtext``TYPE_BOOL``tinyint(1)``TYPE_DATE``date``TYPE_TIME``time``TYPE_DATETIME``datetime``TYPE_BLOB``blob`### Database

[](#database)

`Database` (abstract) wraps PDO with:

- **Lazy connection** — connects on the first `query()` call
- **`#ClassName` substitution** — `#User` in SQL is replaced with `user` (outside string literals)
- **Parameterized queries** — all methods accept `params` arrays
- **Fetch helpers** — `fetch`, `fetchAll`, `fetchColumn`, `fetchOne` with optional class hydration
- **DML helpers** — `insert`, `update` with automatic name escaping
- **Transaction helpers** — `beginTransaction`, `commit`, `rollBack`, `runInTransaction`

`MariaDatabase` is the MySQL/MariaDB implementation. `PdoBuilder` is a fluent factory injected into `Database` to keep construction testable.

### EntityManager

[](#entitymanager)

Central registry. Key operations:

MethodDescription`save(Entity)`Insert or update; back-fills auto-increment PK; emits save events`findById(class, id)`Fetch by PK; marks entity not-new; takes a dirty-tracking snapshot`deleteById(class, id)`Delete single row by PK`deleteByIds(class, ids[])`Delete multiple rows by PK`insert(class, data[])`Raw insert; returns last insert ID`update(class, data[], cond, params)`Raw update with condition`fetchDataArray(Entity)`Extract column values from entity into an array`setByDataArray(Entity, data[])`Set entity properties from array and take snapshot### Query / QueryBuilder / QueryExecutor

[](#query--querybuilder--queryexecutor)

- **`Query`** — a plain data object describing a SELECT (fields, conditions, joins, group, order, limit)
- **`QueryBuilder`** — converts Query + EntityManager metadata to SQL strings; also generates DDL
- **`QueryExecutor`** — executes queries through Database and returns results

---

Foreign Keys
------------

[](#foreign-keys)

```
class Post extends Entity {

    #[Column(type: Column::TYPE_INT, primaryKey: true, autoIncrement: true, notNull: true)]
    public int $id = 0;

    #[Column(
        type: Column::TYPE_INT,
        notNull: true,
        foreignKey: [User::class, 'id'],
        onDelete: Column::ACTION_CASCADE
    )]
    public int $user_id = 0;
}
```

`createTable(Post::class)` generates the `FOREIGN KEY … REFERENCES …` clause automatically. Always create the referenced table first.

---

Composite Primary Keys
----------------------

[](#composite-primary-keys)

```
class UserRole extends Entity {

    #[Column(type: Column::TYPE_INT, primaryKey: true, notNull: true)]
    public int $user_id = 0;

    #[Column(type: Column::TYPE_INT, primaryKey: true, notNull: true)]
    public int $role_id = 0;
}
```

`EntityManager::primaryKey()` returns an array for composite keys. `save()`, `findById()`, and `deleteById()` all accept an array as the ID argument in this case.

---

Configuration Reference
-----------------------

[](#configuration-reference)

```
; Database connection (configName = "default")
database.default.dsn           = "mysql:host=localhost"
database.default.name          = mydb
database.default.username      = root
database.default.password      = secret
database.default.table_prefix  = app_

; Query builder
entities.query_builder.max_limit = 1000
```

Multiple database connections are supported by changing the `configName` property on a `Database` subclass, which shifts all config key reads to a different prefix (e.g. `database.reports.*`).

---

Testing
-------

[](#testing)

The test suite lives in the separate repository **[dynart-micro-entities-test](https://github.com/goph-R/dynart-micro-entities-test)**, which symlinks this library via a Composer path repository.

```
# Unit tests (no DB required)
php vendor/bin/phpunit --testsuite unit --stderr

# Integration tests (requires MariaDB)
php vendor/bin/phpunit --testsuite integration --stderr
```

See the test project's [README](https://github.com/goph-R/dynart-micro-entities-test/README.md) for setup instructions.

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance55

Moderate activity, may be stable

Popularity0

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity23

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/ac500fe52d548840264c1d6707210b0e36d90a9bd51a17a5438cbe57d89b8aee?d=identicon)[gopher](/maintainers/gopher)

---

Top Contributors

[![goph-R](https://avatars.githubusercontent.com/u/3341305?v=4)](https://github.com/goph-R "goph-R (35 commits)")

### Embed Badge

![Health badge](/badges/dynart-micro-entities/health.svg)

```
[![Health](https://phpackages.com/badges/dynart-micro-entities/health.svg)](https://phpackages.com/packages/dynart-micro-entities)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
