daycry/doctrine

Doctrine for Codeigniter 4

Maintainers

👁 daycry

Package info

github.com/daycry/doctrine

pkg:composer/daycry/doctrine

Statistics

Installs: 3 102

Dependents: 0

Suggesters: 0

Stars: 9

Open Issues: 0

v5.1.0 2026-06-06 19:30 UTC

Suggests

  • ext-memcached: Required for the Memcached cache backend
  • ext-redis: Required for the Redis cache backend
  • doctrine/migrations: Versioned schema migrations integration for the Doctrine ORM CLI (php cli-config.php / spark doctrine:*)
  • jms/serializer-bundle: Only if your entities use JMS Serializer annotations (e.g. when reverse-engineering mappings)

Provides

None

Conflicts

None

Replaces

None

MIT 3fe444e819235d811a518d3453ee81d6eb942aec

ormmysqldoctrinecodeigniterdatatablescodeigniter4


README

👁 Donate

Doctrine

Doctrine ORM 3 integration for CodeIgniter 4.

👁 PHPUnit
👁 PHPStan
👁 Psalm
👁 Rector
👁 Code Style
👁 Coverage Status
👁 Downloads
👁 Monthly Downloads
👁 GitHub release (latest by date)
👁 GitHub stars
👁 GitHub license
👁 Documentation

📖 Documentation: https://daycry.github.io/doctrine/

Features

  • ORM integration via \Daycry\Doctrine\Doctrine and \Config\Services::doctrine().
  • Server-side DataTables Builder with safe operator parsing, whitelisted columns, and [><] / [IN] / [OR] validation.
  • CodeIgniter Debug Toolbar collector with optional Second-Level Cache (SLC) statistics badge.
  • Doctrine Second-Level Cache wired to the framework cache backend (file, Redis, Memcached, array).
  • getFromCacheOrQuery() cache-aside helper backed by the configured PSR-6 result cache.
  • Multi-database group support — get a separate Doctrine instance per Config\Database group.
  • Extensible via config: custom DBAL Types, SQL Filters (soft-delete / multi-tenant), event listeners/subscribers, composable DBAL middlewares and a default repository class.
  • Production query logging (PSR-3) with an optional slow-query threshold — independent of the debug toolbar.
  • Spark commands (doctrine:cache:clear, doctrine:validate, doctrine:info, doctrine:schema:update) and a multi-group ORM CLI (--em=<group>).

Requirements

  • PHP ≥ 8.2
  • CodeIgniter ^4
  • Doctrine ORM ^3, DBAL ^4
  • Symfony Cache ^7

See composer.json for the complete dependency graph.

Documentation Index

📖 Full documentation site: https://daycry.github.io/doctrine/

Installation

composer require daycry/doctrine

Then publish the configuration:

php spark doctrine:publish

This copies Config/Doctrine.php into your app namespace and cli-config.php into the project root for use with the Doctrine ORM CLI.

Quick Start

As a service

$doctrine = \Config\Services::doctrine();
$user = $doctrine->em->getRepository(\App\Models\Entity\User::class)->find(1);

As a helper

Add doctrine_helper to your BaseController::$helpers:

protected $helpers = ['doctrine_helper'];
$doctrine = doctrine_instance(); // default DB group
$reporting = doctrine_instance('reporting'); // alternate DB group

Constructing manually

$doctrine = new \Daycry\Doctrine\Doctrine();
$user = $doctrine->em->getRepository(\App\Models\Entity\User::class)->find(1);

Manual Result Caching

getFromCacheOrQuery() is autoloaded as a global function (no use is needed beyond the function import). It looks up $cacheKey in the configured result cache pool and falls back to the closure on miss.

use function Daycry\Doctrine\Helpers\getFromCacheOrQuery;

$rows = getFromCacheOrQuery(
 cacheKey: 'projects_list_v1',
 ttl: 300,
 queryFn: fn () => $doctrine->em
 ->createQueryBuilder()
 ->select('p')
 ->from(\App\Models\Entity\Project::class, 'p')
 ->getQuery()
 ->getArrayResult(),
);

When the result cache is disabled (Config\Doctrine::$resultsCache = false) the closure runs every time. PSR-6 reserved characters ({}()/\@:) in the key are normalised automatically, so any key string is accepted.

See docs/usage.md for advanced API: getEm(), reOpen(), multi-database groups, Services::resetDoctrine(), and more.

Doctrine ORM CLI

Use the generated cli-config.php from the project root:

php cli-config.php orm:validate-schema # check mappings vs. database
php cli-config.php orm:schema-tool:update --dump-sql # preview schema changes
php cli-config.php orm:schema-tool:update --force # apply them
php cli-config.php orm:generate-proxies app/Models/Proxies
php cli-config.php orm:info # list mapped entities
php cli-config.php orm:run-dql "SELECT u FROM App\Models\Entity\User u"

Doctrine ORM 3 removed orm:convert-mapping and orm:generate-entities. Reverse-engineering an existing schema into entities is no longer part of the ORM toolchain; map your entities with PHP attributes (or XML) directly.

The same commands are also available through Spark — see docs/cli_commands.md.

DataTables

$datatables = (new \Daycry\Doctrine\DataTables\Builder())
 ->withColumnAliases([
 'id' => 'p.id',
 'name' => 'p.name',
 ])
 ->withSearchableColumns(['p.name'])
 ->withCaseInsensitive(true)
 ->withMaxFilterValues(500) // cap [IN] / [OR] value lists; default 500
 ->withMaxPageLength(200) // cap page size; clamps length=-1 ("All") — default 0 (no cap)
 ->withQueryBuilder(
 $this->doctrine->em->createQueryBuilder()
 ->select('p.id, p.name')
 ->from(\App\Models\Entity\Project::class, 'p'),
 )
 ->withRequestParams($this->request->getGet());

return $this->response->setJSON($datatables->getResponse());

If pagination throws "Not all identifier properties can be found in the ResultSetMapping", set ->setUseOutputWalkers(false) on the Builder.

Search modes

The Builder supports bracket-prefixed operators per column:

[%] (LIKE, default) [=] [!=] [>] [<] [IN] [OR] [><]

Synonyms [LIKE] and [%%] map to [%]. Unknown prefixes silently fall back to [%]. The DataTables regex: true flag is not supported — sending it raises InvalidArgumentException.

See docs/search_modes.md for the full operator matrix, validation rules, case-insensitivity behaviour and examples.

Debug Toolbar

A DoctrineCollector automatically captures every DBAL query so you can inspect them in the CodeIgniter Debug Toolbar.

  1. Register the collector in app/Config/Toolbar.php:

    public $collectors = [
     // ...
     \Daycry\Doctrine\Debug\Toolbar\Collectors\DoctrineCollector::class,
    ];
  2. Use Doctrine as usual — the middleware self-registers when you instantiate the service.

For long-running CLI workers you can cap the in-memory query log:

\Config\Services::doctrineCollector()->setMaxQueries(500); // FIFO; 0 disables the cap

See docs/debug_toolbar.md for the full collector API, the SLC stats badge, and the per-request reset filter.

Second-Level Cache (SLC)

Doctrine's Second-Level Cache reuses the framework cache backend (file / Redis / Memcached / array) and its ttl. Enable in app/Config/Doctrine.php:

public bool $secondLevelCache = true;
public bool $secondLevelCacheStatistics = true; // optional: hits/misses/puts badge
public ?int $secondLevelCacheTtl = null; // null = inherit Config\Cache::$ttl; 0 = no expiry

To reset SLC statistics at the start of every request (useful in development to read per-request hit ratios in the toolbar), register the filter:

// app/Config/Filters.php
public array $globals = [
 'before' => [
 \Daycry\Doctrine\Debug\Filters\DoctrineSlcReset::class,
 ],
];

The filter is a no-op unless secondLevelCacheStatistics is enabled.

See docs/second_level_cache.md and docs/second_level_cache_stats.md for full details.

Extending the EntityManager

Config\Doctrine exposes additive, backward-compatible hooks (all default to off) for the common Doctrine extension points:

// app/Config/Doctrine.php
public array $customTypes = ['uuid' => \Ramsey\Uuid\Doctrine\UuidType::class];
public array $sqlFilters = ['soft_delete' => \App\Doctrine\SoftDeleteFilter::class];
public array $enabledFilters = ['soft_delete'];
public array $eventListeners = ['onFlush' => [\App\Doctrine\AuditListener::class]];
public array $eventSubscribers = [\App\Doctrine\TimestampSubscriber::class];
public array $dbalMiddlewares = [\App\Doctrine\RetryMiddleware::class];
public ?string $defaultRepositoryClass = \App\Repositories\BaseRepository::class;

// Production query logging (PSR-3) — independent of the debug toolbar
public bool $queryLogging = true;
public float $slowQueryThreshold = 0.5; // log queries slower than 500 ms
public string $queryLogLevel = 'warning';

All are re-applied across Doctrine::reOpen(). See docs/configuration.md for the full reference.

Development

Available Composer scripts for contributors:

composer test # PHPUnit test suite
composer phpstan # PHPStan (level 6)
composer psalm # Psalm static analysis
composer rector # Rector dry-run
composer analyze # phpstan + psalm + rector
composer cs # PHP-CS-Fixer dry-run
composer cs-fix # PHP-CS-Fixer apply

License

MIT. Issues and PRs welcome at https://github.com/daycry/doctrine.