PHPackages                             flytachi/winter-cdo - 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. [Database &amp; ORM](/categories/database)
4. /
5. flytachi/winter-cdo

ActiveLibrary[Database &amp; ORM](/categories/database)

flytachi/winter-cdo
===================

Extended, type-safe PDO wrapper for PostgreSQL, MySQL/MariaDB and Oracle with a composable, injection-safe query builder.

v3.0.9(2d ago)2501↓60%2MITPHPPHP &gt;=8.3

Since Dec 17Pushed 1mo agoCompare

[ Source](https://github.com/Flytachi/winter-cdo)[ Packagist](https://packagist.org/packages/flytachi/winter-cdo)[ Docs](https://winterframe.net)[ RSS](/packages/flytachi-winter-cdo/feed)WikiDiscussions main Synced yesterday

READMEChangelog (5)Dependencies (13)Versions (27)Used By (2)

Winter CDO
==========

[](#winter-cdo)

[![Latest Version on Packagist](https://camo.githubusercontent.com/95ad8f778997d419bf8b2344016b11ddd11c411c5e9898d1250cd152712bc69b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f666c7974616368692f77696e7465722d63646f2e737667)](https://packagist.org/packages/flytachi/winter-cdo)[![Software License](https://camo.githubusercontent.com/074b89bca64d3edc93a1db6c7e3b1636b874540ba91d66367c0e5e354c56d0ea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e737667)](LICENSE)

**CDO** (Connection Data Object) — an extended PDO wrapper for type-safe, parameterised database operations with a composable query builder.

**Full documentation:**

---

Requirements
------------

[](#requirements)

- PHP &gt;= 8.3
- ext-pdo
- psr/log ^3.0

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

[](#installation)

```
composer require flytachi/winter-cdo
```

Supported Databases
-------------------

[](#supported-databases)

DatabaseinsertinsertGroupupsertupsertGroupupdatedeletePostgreSQL✅✅✅✅✅✅MySQL / MariaDB✅✅✅✅✅✅Oracle⚠️✅❌❌✅✅---

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

[](#quick-start)

### 1. Define a configuration

[](#1-define-a-configuration)

Extend `MySqlDbConfig` or `PgDbConfig` and fill credentials in `setUp()`:

```
use Flytachi\Winter\Cdo\Config\PgDbConfig;

class AppDb extends PgDbConfig
{
    public function setUp(): void
    {
        $this->host     = env('DB_HOST', 'localhost');
        $this->port     = (int) env('DB_PORT', 5432);
        $this->database = env('DB_NAME', 'myapp');
        $this->username = env('DB_USER', 'postgres');
        $this->password = env('DB_PASS', '');
    }
}
```

For a one-off connection without a dedicated class, use the inline `PgDbCall` / `MySqlDbCall` / `DbCall` constructors — see [Configuration docs](docs/01-configuration.md).

### 2. Get a connection

[](#2-get-a-connection)

```
$cdo = ConnectionPool::db(AppDb::class);
```

### 3. Run operations

[](#3-run-operations)

```
use Flytachi\Winter\Cdo\Qb;

// Insert — returns the generated primary key:
$id = $cdo->insert('users', [
    'name'  => 'Alice',
    'email' => 'alice@example.com',
]);

// Update — returns affected row count:
$cdo->update('users',
    ['name' => 'Alice Smith'],
    Qb::eq('id', $id)
);

// Delete — returns deleted row count:
$cdo->delete('users', Qb::eq('id', $id));

// Batch insert:
$cdo->insertGroup('users', $usersArray, chunkSize: 500);

// Upsert (insert or update on conflict):
$cdo->upsert('products',
    ['sku' => 'ABC-001', 'price' => 9.99, 'stock' => 50],
    conflictColumns: ['sku'],
    updateColumns: ['price' => ':new', 'stock' => ':current + :new']
);
```

---

Qb — Query Builder
------------------

[](#qb--query-builder)

`Qb` builds safe, parameterised SQL `WHERE` fragments. Every value is bound via a named placeholder — no string interpolation, no injection risk.

```
// Simple condition:
Qb::eq('status', 'active')
// → status = :iqb0

// Compound condition:
$where = Qb::and(
    Qb::eq('status', 'active'),
    Qb::gte('age', 18),
    Qb::isNull('banned_at'),
);
// → status = :iqb0 AND age >= :iqb1 AND banned_at IS NULL
```

### Operator reference

[](#operator-reference)

CategoryMethodsSQL resultComparison`eq`, `neq`, `gt`, `gte`, `lt`, `lte``col = :x`, `col != :x`, …NULL`isNull`, `isNotNull``col IS NULL`, `col IS NOT NULL`NULL-safe`nsEq``col  :x` (MySQL/MariaDB)Set`in`, `notIn``col IN (:a, :b)`, `col NOT IN (…)`Pattern`like`, `notLike``col LIKE :x`, `col NOT LIKE :x`Range`between`, `notBetween``col BETWEEN :a AND :b`Range (inverted)`betweenBy`, `notBetweenBy``:x BETWEEN col1 AND col2`Logical`and`, `or`, `xor``a AND b`, `a OR b`, `a XOR b`Grouping`clip``(condition)`CASE`case``CASE WHEN … THEN … END`Raw`raw`verbatim SQL with optional binds### Operator precedence — always use `clip` with mixed AND/OR

[](#operator-precedence--always-use-clip-with-mixed-andor)

```
// ❌ Wrong — SQL reads as (published AND role='editor') OR role='admin':
Qb::and(
    Qb::eq('published', true),
    Qb::or(Qb::eq('role', 'editor'), Qb::eq('role', 'admin')),
)

// ✅ Correct — clip enforces the right grouping:
Qb::and(
    Qb::eq('published', true),
    Qb::clip(
        Qb::or(Qb::eq('role', 'editor'), Qb::eq('role', 'admin'))
    ),
)
// → published IS TRUE AND (role = :iqb0 OR role = :iqb1)
```

### Dynamic filters

[](#dynamic-filters)

```
// null conditions are silently skipped:
$where = Qb::and(
    Qb::eq('status', 'active'),
    $minAge  !== null ? Qb::gte('age', $minAge)   : null,
    $country !== null ? Qb::eq('country', $country) : null,
    Qb::in('tag_id', $tagIds),   // skipped when $tagIds is []
);
```

### Named binds — share one placeholder across conditions

[](#named-binds--share-one-placeholder-across-conditions)

```
$uid = new CDOBind('uid', $currentUserId);

$where = Qb::or(
    Qb::eq('author_id',   $uid),
    Qb::eq('reviewer_id', $uid),
    Qb::eq('assignee_id', $uid),
);
// → author_id = :uid OR reviewer_id = :uid OR assignee_id = :uid
```

---

Upsert Placeholders
-------------------

[](#upsert-placeholders)

TokenPostgreSQLMySQL / MariaDB`:new``EXCLUDED.column``VALUES(column)``:current``table.column``column````
$cdo->upsertGroup('inventory', $items,
    conflictColumns: ['warehouse_id', 'product_id'],
    updateColumns: [
        'cost'       => ':new',
        'quantity'   => ':current + :new',
        'updated_at' => 'NOW()',
    ]
);
```

---

Error Handling
--------------

[](#error-handling)

All failures throw `CDOException`, which wraps the original `PDOException` as its `$previous` cause (preserving SQLSTATE code and driver message):

```
use Flytachi\Winter\Cdo\Connection\CDOException;

try {
    $cdo->insert('users', $data);
} catch (CDOException $e) {
    $sqlstate = $e->getPrevious()?->getCode();  // e.g. "23505" (PG unique violation)
    // handle or re-throw
}
```

---

Documentation
-------------

[](#documentation)

Full reference documentation is at ****

Local docs in [`docs/`](docs/):

FileTopic[01-configuration.md](docs/01-configuration.md)Config classes, inline Call classes[02-connection-pool.md](docs/02-connection-pool.md)ConnectionPool, health checks[03-cdo.md](docs/03-cdo.md)All CDO DML methods[04-cdo-statement.md](docs/04-cdo-statement.md)Type binding, object serialisation[05-exceptions.md](docs/05-exceptions.md)CDOException, SQLSTATE reference[06-cdobind.md](docs/06-cdobind.md)CDOBind — named parameters[07-comparison-operators.md](docs/07-comparison-operators.md)eq, neq, gt, gte, lt, lte, nsEq[08-null-checks.md](docs/08-null-checks.md)isNull, isNotNull[09-set-operators.md](docs/09-set-operators.md)in, notIn[10-pattern-matching.md](docs/10-pattern-matching.md)like, notLike[11-range-operators.md](docs/11-range-operators.md)between, betweenBy, notBetween, notBetweenBy[12-logical-operators.md](docs/12-logical-operators.md)and, or, xor, clip[13-mutable-methods.md](docs/13-mutable-methods.md)addAnd, addOr, addXor[14-case-expression.md](docs/14-case-expression.md)CASE WHEN … END[15-special.md](docs/15-special.md)raw, empty[16-advanced-examples.md](docs/16-advanced-examples.md)Real-world combinations---

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE).

###  Health Score

50

—

FairBetter than 95% of packages

Maintenance96

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity60

Established project with proven stability

 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.

###  Release Activity

Cadence

Every ~8 days

Recently: every ~14 days

Total

26

Last Release

2d ago

Major Versions

v1.2.7 → v2.0.02026-04-06

v2.0.0 → v3.0.02026-04-14

### Community

Maintainers

![](https://www.gravatar.com/avatar/861b81dd97c8ddfa919522d2a4e17626120bd3e3d7464857cc03784676bc74a8?d=identicon)[Flytachi](/maintainers/Flytachi)

---

Top Contributors

[![Flytachi](https://avatars.githubusercontent.com/u/68924300?v=4)](https://github.com/Flytachi "Flytachi (33 commits)")

---

Tags

databasemysqlpostgresqlmariadbdbalsqlpdooraclequery builderupsertwinter

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/flytachi-winter-cdo/health.svg)

```
[![Health](https://phpackages.com/badges/flytachi-winter-cdo/health.svg)](https://phpackages.com/packages/flytachi-winter-cdo)
```

###  Alternatives

[doctrine/dbal

Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.

9.7k605.0M6.8k](/packages/doctrine-dbal)[tommyknocker/pdo-database-class

Framework-agnostic PHP database library with unified API for MySQL, MariaDB, PostgreSQL, SQLite, MSSQL, and Oracle. Query Builder, caching, sharding, window functions, CTEs, JSON, migrations, ActiveRecord, CLI tools, AI-powered analysis. Zero external dependencies.

846.1k](/packages/tommyknocker-pdo-database-class)[cycle/database

DBAL, schema introspection, migration and pagination

71777.8k53](/packages/cycle-database)

PHPackages © 2026

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