Quickstart
In five minutes you’ll go from credentials to a full round-trip: define a config, open a connection, insert a user and read back its id, update it, query it with a condition, and delete it. Every value is bound — you never concatenate SQL.
What you’ll build
A tiny lifecycle over a users table: connect → insert → update → query →
delete. We’ll use PostgreSQL, but the only line that changes for MySQL/MariaDB
is the config base class — the DML calls are identical.
Assume a table like:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INT
);Step 1 — Define a config
<?php
namespace App\Db;
use Flytachi\Winter\Cdo\Config\PgDbConfig;
class AppDb extends PgDbConfig
{
public function setUp(): void
{
$this->host = '127.0.0.1';
$this->port = 5432;
$this->database = 'myapp';
$this->username = 'postgres';
$this->password = 'secret';
}
}Step 2 — Get a connection
use Flytachi\Winter\Cdo\ConnectionPool;
use App\Db\AppDb;
$cdo = ConnectionPool::db(AppDb::class);ConnectionPool builds the config once and opens the PDO socket lazily on first
use. Call it again anywhere and you get the same connection back.
Step 3 — Insert and get the id
insert() returns the generated primary key. On PostgreSQL and MariaDB it uses
INSERT ... RETURNING; on MySQL it falls back to lastInsertId().
$id = $cdo->insert('users', [
'name' => 'Alice',
'email' => 'alice@example.com',
'age' => 30,
]);
echo $id; // e.g. 1NULL columns are skipped
Any key whose value is null is dropped from the INSERT, letting the column
take its database default (or auto-generated id). Pass only what you mean to set.
Step 4 — Update with a condition
The third argument is a Qb condition for the WHERE clause. update() returns
the number of affected rows.
use Flytachi\Winter\Cdo\Qb;
$affected = $cdo->update('users',
['name' => 'Alice Smith'],
Qb::eq('id', $id)
);
echo $affected; // 1Step 5 — Query with the underlying PDO
CDO is a PDO, so use prepare / query / fetchAll directly. Pair a
Qb fragment with its binds:
$where = Qb::and(
Qb::gte('age', 18),
Qb::like('email', '%@example.com'),
);
$stmt = $cdo->prepare("SELECT * FROM users WHERE " . $where->getQuery());
foreach ($where->getBinds() as $bind) {
$stmt->bindValue($bind->getName(), $bind->getValue());
}
$stmt->execute();
$rows = $stmt->fetchAll(); // default fetch mode is FETCH_ASSOCStep 6 — Delete
delete() also takes a Qb and returns the deleted row count:
$deleted = $cdo->delete('users', Qb::eq('id', $id));
echo $deleted; // 1The whole thing
<?php
require 'vendor/autoload.php';
use Flytachi\Winter\Cdo\ConnectionPool;
use Flytachi\Winter\Cdo\Qb;
use App\Db\AppDb;
$cdo = ConnectionPool::db(AppDb::class);
// create
$id = $cdo->insert('users', [
'name' => 'Alice',
'email' => 'alice@example.com',
'age' => 30,
]);
// update
$cdo->update('users', ['name' => 'Alice Smith'], Qb::eq('id', $id));
// read
$stmt = $cdo->prepare('SELECT * FROM users WHERE ' . Qb::eq('id', $id)->getQuery());
foreach (Qb::eq('id', $id)->getBinds() as $b) {
$stmt->bindValue($b->getName(), $b->getValue());
}
$stmt->execute();
$user = $stmt->fetch();
// delete
$cdo->delete('users', Qb::eq('id', $id));Next steps
- Mental model — the one idea that makes the rest obvious
- Inserting records — batch inserts and returned ids
- Building conditions — compose
Qbfilters safely - CDO API — every method, parameter, and return value