Skip to content

Standalone core and CodeIgniter adapter

Version 6.0 provides two Composer packages maintained in one source repository:

Package Responsibility
daycry/doctrine Standalone ORM setup, DataTables, PSR-6 cache-aside, PSR-3 logging, named managers, diagnostics and Symfony Console.
daycry/doctrine-codeigniter4 CI4 configuration and services, database/cache translation, toolbar, filters, helpers and Spark commands.

The core has no CodeIgniter runtime dependency, framework globals or service lookup. Its development requirements include CI4 so this repository can test the adapter. Consumers do not inherit those development requirements.

Installation

The installation guide covers Packagist releases, local installation, package selection and deployment. For local use, configure Composer path repositories for the checkout and adapter:

{
    "repositories": [
        {
            "type": "path",
            "url": "../doctrine",
            "options": {"versions": {"daycry/doctrine": "6.0.0"}}
        },
        {
            "type": "path",
            "url": "../doctrine/packages/codeigniter4",
            "options": {"versions": {"daycry/doctrine-codeigniter4": "6.0.0"}}
        }
    ]
}

The version overrides are local testing aliases, not published releases. Install daycry/doctrine:^6.0 for standalone use or daycry/doctrine-codeigniter4:^6.0 for CI4. The adapter pulls in the core and framework itself.

Explicit configuration

use Daycry\Doctrine\Configuration;
use Daycry\Doctrine\Doctrine;

$password = getenv('DB_PASSWORD');
if ($password === false) {
    throw new RuntimeException('Set DB_PASSWORD in the application environment.');
}
$config = new Configuration();
$config->connection = ['driver' => 'pdo_mysql', 'host' => '127.0.0.1',
    'dbname' => 'app', 'user' => 'app', 'password' => $password];
$config->entities = [__DIR__ . '/src/Entity'];
$doctrine = new Doctrine($config);
$em = $doctrine->getEm();

Connection values use DBAL's parameter names directly. Attributes and XML, custom types, listeners, subscribers, enabled filters, DQL functions and native lazy objects remain configurable. The default DQL function registrations target MySQL; override those arrays when using database-specific functions elsewhere.

Caches and logging

CachePools accepts PSR-6 pools for query, result, metadata and secondLevel. Defaults are four independent, process-local ArrayAdapters. Inject Symfony file, Redis or Memcached adapters, or other PSR-6 implementations, for persistent storage. Give persistent pools distinct namespaces, including a manager-specific suffix when sharing a backend across managers.

use Daycry\Doctrine\Cache\CacheAside;
use Daycry\Doctrine\Cache\CachePools;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;

$pools = new CachePools(
    query: new PhpFilesAdapter('app_queries', 3600, __DIR__ . '/var/cache'),
    result: new FilesystemAdapter('app_results', 0, __DIR__ . '/var/cache'),
    metadata: new PhpFilesAdapter('app_metadata', 3600, __DIR__ . '/var/cache'),
);
$doctrine = new Doctrine($config, $pools);
$value = new CacheAside($pools->result)->get('dashboard:total', 60, fn () => $repository->count([]));

For CacheAside, a positive TTL expires the entry explicitly. Zero uses the pool's default lifetime; configure that default as zero for storage without expiry. Negative TTLs raise InvalidArgumentException. Passing a null pool runs the callback without caching. The adapter's namespaced helper delegates to this same class and uses its configured result pool. READ_WRITE SLC requires an explicit secondLevelCacheLockPath shared by processes that share regions.

To enable query logging, set queryLogging = true and pass a PSR-3 logger as the logger constructor argument. The core does not look up a framework logger.

Redis and Memcached result pools

Both backends can be supplied directly without CI4. Load the corresponding PHP extension and configure the client for the application's server:

use Daycry\Doctrine\Cache\CachePools;
use Symfony\Component\Cache\Adapter\RedisAdapter;

$client = new \Redis();
$client->connect('127.0.0.1', 6379);
$pools = new CachePools(result: new RedisAdapter($client, 'app_results', 0));

Or, with Memcached:

use Daycry\Doctrine\Cache\CachePools;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;

$client = new \Memcached();
$client->addServer('127.0.0.1', 11211);
$pools = new CachePools(result: new MemcachedAdapter($client, 'app_results', 0));

Pass $pools to the Doctrine constructor. These examples replace only the result pool; query/metadata/SLC pools keep their ArrayAdapter defaults. Reuse a client when constructing additional pools, assigning a distinct namespace to each. Configure authentication and connection options in the application before constructing pools.

Named managers and DataTables

ManagerRegistry accepts named lazy factories and an explicit default name:

use Daycry\Doctrine\ManagerRegistry;

$registry = new ManagerRegistry([
    'default' => fn () => new Doctrine($primaryConfig),
    'reporting' => fn () => new Doctrine($reportingConfig),
]);
$reporting = $registry->get('reporting');
$registry->reset('reporting'); // closes that manager/connection; next get recreates it

Names are case-sensitive. Each factory should return a fresh instance. DataTables continues to accept an ORM QueryBuilder and a request array; no HTTP framework object is required. See DataTables.

Standalone console

Create doctrine-bootstrap.php returning a Doctrine instance or ManagerRegistry:

php vendor/bin/daycry-doctrine doctrine-bootstrap.php orm:info
php vendor/bin/daycry-doctrine doctrine-bootstrap.php doctrine:doctor --json
php vendor/bin/daycry-doctrine doctrine-bootstrap.php doctrine:cache:warmup --em reporting

The console includes Doctrine's native ORM commands. Doctor and warmup do not modify the schema. The corresponding programmatic APIs are Tools\Diagnostics and Tools\CacheWarmer.

CI4 integration and migration

Install the adapter, publish with php spark doctrine:publish, and keep using service('doctrine'), framework cache backends and Spark. Application config now extends Daycry\Doctrine\CodeIgniter4\Config\Doctrine. Package-specific CI4 classes and helpers moved under Daycry\Doctrine\CodeIgniter4. See the migration guide; no aliases for previous namespaces are provided.

Development and release

Run the existing integration suite with composer test and the independent core suite with php vendor/bin/phpunit -c phpunit.standalone.xml.dist. scripts/prepare-isolation.php core prepares a consumer that installs no CI4; the standalone CI workflow runs its tests with current and minimum dependencies. The adapter consumer verifies installation, framework boot, SQLite and Spark discovery.

The root Composer archive excludes packages/, build/ and tests/. The adapter has its own composer.json, README and license. Its VCS distribution is daycry/doctrine-codeigniter4, produced from packages/codeigniter4 using git subtree split. See the release procedure for coordinated tags and Packagist registration.