Пакет · cdo

Модель работы

CDO — это не ORM и не новый слой базы данных. Это PDO, который уже умеет безопасно привязывать значения и говорить на диалекте вашего конкретного драйвера. Удержите эту идею — и каждый метод встанет на своё место.

Одна идея

Значение никогда не касается строки SQL. Оно едет рядом с ней, как привязка.

Всё, что добавляет CDO, — следствие этого. Qb не строит целый запрос — он строит фрагмент плюс его привязки. Методы DML не склеивают ваши данные — они превращают ключи в :плейсхолдеры и передают значения типизированному связывателю. Вы композируете фрагменты; библиотека держит значения отдельно всю дорогу вниз.

Две половины, встречающиеся на execute

У CDO две независимые половины, соединяющиеся только в момент выполнения:

text
  Qb (неизменяемый)              CDO (наследует PDO)
─────────────                  ────────────────
Qb::eq('id', 5)         →      insert / update / delete / upsert
  ├─ query:  "id = :iqb0"        │
  └─ binds:  [:iqb0 => 5]        └─ CDOStatement.bindTypedValue()
                                      └─ подбирает PDO::PARAM_* по типу PHP
  • Qb порождает строку getQuery() и список getBinds(). Он чистый — без соединения, без выполнения, полностью тестируемый сам по себе.
  • CDO владеет соединением и диалектом драйвера. Когда вы вызываете update() или delete(), он сшивает ваш фрагмент Qb в запрос и привязывает каждое значение с нужным типом через CDOStatement.

Следствия (что вытекает из идеи)

  • Qb неизменяем. Каждая фабрика возвращает новый экземпляр; and / or / clip объединяют без мутации. (Семейство addAnd / addOr — явное изменяемое исключение, см. построение условий.)
  • Плейсхолдеры именуются автоматически. Qb чеканит :iqb0, :iqb1, …, поэтому вы редко что-то называете. Когда хотите общее или читаемое имя — передайте CDOBind вместо сырого скаляра.
  • null означает разное в разных методах. В Qb::eq он становится IS NULL; в insert/upsert значение null полностью выбрасывается из списка колонок.
  • Типы выводятся, а не предполагаются. int привязывается как PARAM_INT, bool — как PARAM_BOOL, массив — как JSON, DateTimeInterface — как Y-m-d H:i:s. Вы не кастуете; связыватель читает gettype(). См. Привязку параметров.

CDO по-прежнему PDO

class CDO extends PDO. Это наследование намеренно: методы-удобства покрывают запись, но чтение — это чистый PDO. Здесь нет select() или findAll() — вы сами делаете prepare() / query() и fetch(), используя фрагмент Qb для WHERE. Транзакции, курсоры и любой атрибут PDO работают без изменений.

Когда за чем тянуться

Запись строк → insert / upsert / update / delete из CDO. Чтение строк → PDO prepare + Qb::…->getQuery() + getBinds(). Построение условия в изоляции (даже чтобы юнит-тестировать его) → один Qb, соединение не нужно.

Чем CDO намеренно не является

  • Не ORM — нет сущностей, нет identity map, нет ленивых связей. На вход — массивы и обычные объекты, на выход — массивы.
  • Не инструмент схемы — нет миграций и помощников DDL. Таблицы вы создаёте сами.
  • Не полноценный конструктор SELECTQb строит фрагменты вида WHERECASE), а не джойны, проекции или сортировки.

Знать границу — в этом весь смысл: CDO убирает рискованную повторяющуюся часть доступа к данным — привязку и диалект — и оставляет остальное PDO и SQL, который вы уже знаете.

Дальше