flytachi/winter-edo

Winter framework 'Entity of Database objects' component

Maintainers

Package info

github.com/Flytachi/winter-edo

Homepage

Documentation

pkg:composer/flytachi/winter-edo

Statistics

Installs: 238

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v3.0.0 2026-04-14 07:58 UTC

This package is auto-updated.

Last update: 2026-04-14 08:02:44 UTC


README

Latest Version on Packagist Software License

EDO (Entity of Database Objects) — the repository layer for the Winter framework. Wraps Winter CDO with a fluent SQL query builder, entity hydration, and ready-made CRUD and read-only stereotypes.

Full documentation: https://winterframe.net/docs/edo

Requirements

  • PHP >= 8.3
  • ext-pdo
  • flytachi/winter-base ^1.1
  • flytachi/winter-cdo ^2.0
  • flytachi/winter-edo-mapping ^1.0

Installation

composer require flytachi/winter-edo

Quick Start

1. Define an entity

use Flytachi\Winter\Edo\Entity\EntityInterface;

class UserEntity implements EntityInterface
{
    public int    $id;
    public string $name;
    public string $email;
    public string $status;

    public static function selection(): array
    {
        return [];  // use plain property names
    }
}

2. Define a repository

use Flytachi\Winter\Edo\Stereotype\Repository;

class UserRepository extends Repository
{
    protected string $dbConfigClassName = DbConfig::class;
    protected string $entityClassName   = UserEntity::class;
    public static string $table         = 'users';
}

3. Read data

use Flytachi\Winter\Cdo\Qb;

// By primary key:
$user = UserRepository::findById(42);            // UserEntity|null

// With a condition:
$user = UserRepository::findBy(Qb::eq('email', 'alice@example.com'));

// All matching:
$users = UserRepository::findAllBy(Qb::eq('status', 'active'));

// Fluent query builder:
$users = UserRepository::instance('u')
    ->joinLeft('orders o', 'u.id = o.user_id')
    ->where(Qb::eq('u.status', 'active'))
    ->orderBy('u.name ASC')
    ->limit(20)
    ->findAll();

// Throw if not found:
$user = UserRepository::findByIdOrThrow(42, message: 'User not found');

4. Write data

$repo = new UserRepository();

// Insert:
$id = $repo->insert(['name' => 'Alice', 'email' => 'alice@example.com', 'status' => 'active']);

// Update:
$repo->update(['status' => 'inactive'], Qb::eq('id', $id));

// Delete:
$repo->delete(Qb::eq('id', $id));

// Upsert:
$repo->upsert(
    ['email' => 'alice@example.com', 'name' => 'Alice Updated'],
    ['email'],
    ['name' => ':new']
);

Stereotypes

Class Use when
Repository Need both reads and writes (most common)
RepositoryView Read-only (views, reports, projections)
RepositoryCrud Write-only (import pipelines, event sinks)
CteRepo Ad-hoc query without a dedicated class
// Ad-hoc:
use Flytachi\Winter\Edo\Stereotype\CteRepo;

$rows = (new CteRepo(DbConfig::class))
    ->from('audit_log al')
    ->joinLeft('users u', 'al.user_id = u.id')
    ->where(Qb::gt('al.created_at', '2024-01-01'))
    ->select('al.action, u.name, al.created_at')
    ->orderBy('al.created_at DESC')
    ->limit(100)
    ->findAll();

Query Builder — Method Reference

Clause order

WITH [RECURSIVE] → SELECT → FROM → AS → JOIN → WHERE → GROUP BY → HAVING
→ UNION → ORDER BY → LIMIT / OFFSET → FOR
Method SQL clause Notes
with($name, $repo, $modifier) WITH … AS (…) CTE; chain for multiple
withRecursive($name, $repo) WITH RECURSIVE … AS (…) Recursive CTE
select($expr) SELECT $expr Overrides auto column list
from($source) FROM … String or repository subquery
as($alias) table alias Also sets column prefix
join($repo, $on) JOIN … ON(…)
joinInner($repo, $on) INNER JOIN … ON(…)
joinLeft($repo, $on) LEFT JOIN … ON(…)
joinRight($repo, $on) RIGHT JOIN … ON(…)
joinCross($repo) CROSS JOIN … No ON condition
where($qb) WHERE … Accepts null (no-op)
andWhere($qb) … AND … Acts as where() if empty
orWhere($qb) … OR … Acts as where() if empty
xorWhere($qb) … XOR … Acts as where() if empty
groupBy($expr) GROUP BY …
having($expr) HAVING …
union($repo) UNION …
unionAll($repo) UNION ALL …
orderBy($expr) ORDER BY …
limit($n, $offset) LIMIT … OFFSET … OFFSET omitted when 0
forBy($expr) FOR … Locking clause
binding($binds) Manual bind injection

Documentation

Full documentation is in the docs/ directory:

File Contents
00-overview.md Architecture, quick start, doc index
01-stereotypes.md Choosing the right base class
02-configuration.md Properties, EntityInterface
03-select-from.md select(), from(), as()
04-joins.md All JOIN types, subquery joins
05-where.md WHERE conditions
06-group-having.md groupBy(), having()
07-union.md union(), unionAll()
08-order-limit.md orderBy(), limit(), forBy()
09-with-cte.md CTEs, recursive queries
10-binds.md Manual bind injection
11-sql-management.md buildSql(), getSql(), cleanCache()
12-view-fetch.md find, findAll, count, exists, rawFetch
13-static-finders.md Static finders and *OrThrow variants
14-crud.md insert, update, delete, upsert
15-declaration.md Schema structure registry
16-advanced-examples.md Real-world examples

License

MIT — see LICENSE.