Drivers

The Doctrine Driver

Introduction

The Doctrine driver provides a powerful and flexible way to interact with various databases using the Doctrine ORM (or DBAL).

It's based on the latest version 4.x of Doctrine and supports a wide range of database systems, including MySQL, PostgreSQL, SQLite, and more.

You can use it to configure multiple DBAL and Entity Manager connections in your API.

Installation

Install the driver via composer by running:

composer require kipchak/driver-doctrine

Initialise the Driver

Add the following line to your drivers/drivers.php file:

\Mamluk\Kipchak\Driver\Doctrine\Doctrine::iniitalise($container);

Handling Doctrine's Singleton

Doctrine's EntityManager runs as a singleton, which means that it is shared across all requests. This becomes problematic when you use an EntityManager in the Container to update and read entities. To correct this with FrankenPHP, please change the your html/index.php file to uncomment the two lines shown in this commit on the starter project: https://1x.ax/mamluk/kipchak/starter/~commits/c15a05433f0b33bfb7322a23650a3d2869d5ffed

Configuration

The configuration in kipchak.doctrine.php can be used to configure muliple DBAL and ORM connections:

return [
    'dbal' => [
        'enabled' => false,
        'connections' => [
            'primary' => [
                'dbname' => 'mydb',
                'user' => env('DB_USER', 'api'),
                'password' => env('DB_PASSWORD', 'api'),
                'host' => env('DB_HOST', 'mysql'),
                'driver' => 'pdo_mysql',
            ]
        ]
    ],
    'orm' => [
        'enabled' => false,
        'entity_managers' => [
            'primary' => [
                'dev_mode' => (bool) env('DEBUG', true),
                'metadata_dirs' => [
                    realpath(__DIR__ . '/../api/Entities/Doctrine/Primary')
                ],
                'connection' => 'primary',
            ],
        ]
    ]
];

Please see the configuration file in the starter project at https://1x.ax/mamluk/kipchak/starter/~files/master/config/kipchak.doctrine.php for full configuration details.

Usage

DBAL Usage

use Kipchak\Driver\Doctrine\Doctrine;

$dbal = Doctrine::get('dbal.primary'); //See config file below for DBAL definition

// Query builder
$queryBuilder = $dbal->createQueryBuilder();
$users = $queryBuilder
    ->select('*')
    ->from('users')
    ->where('status = ?')
    ->setParameter(0, 'active')
    ->executeQuery()
    ->fetchAllAssociative();

// Direct queries
$stmt = $dbal->executeQuery('SELECT * FROM users WHERE id = ?', [123]);
$user = $stmt->fetchAssociative();

ORM Usage

use Kipchak\Driver\Doctrine\Doctrine;

$em = Doctrine::get('entitymanager.primary'); // See config file below for ORM EM definition

// Find entity
$user = $em->find(User::class, 123);

// Create entity
$newUser = new User();
$newUser->setName('John Doe');
$newUser->setEmail('john@example.com');
$em->persist($newUser);
$em->flush();

// Query
$users = $em->getRepository(User::class)
    ->findBy(['status' => 'active']);

Learn more about Doctrine DBAL and ORM at https://www.doctrine-project.org/projects.html.

Doctrine CLI

The Kipchak Doctrine Driver ships with a Doctrine cli which is available at vendor/bin/doctrine.

Because the driver supports multiple connections, to use it with a specific Entity Manager, pass the --em flag with then name.

Examples:

php vendor/bin/doctrine --em=primary orm:info
php vendor/bin/doctrine migrations:up --em=primary  --file=migrations/database/filename.sql

Migrations

The Doctrine driver includes a custom migration system that uses plain SQL files with support for both "up" and "down" migrations.

Migration File Location

By default, migrations are stored in:

<your-project-root>/migrations/database

This path is resolved from your project root (not from the vendor directory).

Migration File Format

Migration files should be SQL files (.sql extension) with the following structure:

-- Up migration statements go here
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE
);

-- Down
-- Rollback statements go here
DROP TABLE users;

Key points:

  • Everything before -- Down is considered the "up" migration
  • Everything after -- Down is considered the "down" migration (rollback)
  • The -- Down comment acts as a separator between up and down migrations

Migration Tracking

The system automatically creates a schema_migrations table to track which migrations have been applied:

CREATE TABLE schema_migrations (
    filename VARCHAR(255) PRIMARY KEY,
    applied_at DATETIME NOT NULL
)

Migration Naming Convention

While not strictly enforced, it's recommended to name migration files with a timestamp prefix for proper ordering:

20250101120000_create_users_table.sql
20250102140000_add_status_to_users.sql

Migration files are sorted alphabetically by filename, so timestamp prefixes ensure they run in the correct order.

Running Migrations

Apply Migrations (migrations:up)

Apply all pending migrations:

php vendor/bin/doctrine migrations:up --em=primary

Apply a specific migration file:

php vendor/bin/doctrine migrations:up --em=primary --file=20250101120000_create_users_table.sql

Apply all migrations in a specific directory:

php vendor/bin/doctrine migrations:up --em=primary --file=subdirectory/

How --file works:

  • Can be a single SQL file path (absolute or relative to current working directory)
  • Can be a directory path (runs all .sql files recursively)
  • Can be a file or directory relative to migrations/database
  • If omitted, runs all pending migrations in migrations/database

Migration behavior:

  • Each migration is executed only once
  • Already-applied migrations are automatically skipped
  • Migrations run in alphabetical order by filename
  • On PostgreSQL, SQLite, and SQL Server, migrations run in transactions (automatic rollback on failure)
  • Gracefully handles certain errors like "table already exists" or "duplicate column"

Revert Migrations (migrations:down)

Revert the most recently applied migration:

php vendor/bin/doctrine migrations:down --em=primary

Revert a specific migration:

php vendor/bin/doctrine migrations:down --em=primary --file=20250101120000_create_users_table.sql

How --file works:

  • Can be a single SQL file path (absolute or relative to current working directory)
  • Can be a file relative to migrations/database
  • Directory paths are not supported for migrations:down
  • If omitted, reverts the last applied migration

Rollback behavior:

  • Executes the "down" section of the migration file (after -- Down comment)
  • Removes the entry from schema_migrations table
  • Runs in a transaction on supported platforms
  • Fails if no "down" section is found in the migration file

Transaction Support

The migration system automatically uses transactions when the database platform supports DDL transactions:

  • PostgreSQL: ✅ Supports DDL transactions
  • SQLite: ✅ Supports DDL transactions
  • SQL Server: ✅ Supports DDL transactions
  • MySQL: ❌ Does not support DDL transactions (runs statements individually)

When transactions are enabled:

  • All migration statements are wrapped in a single transaction
  • If any statement fails, the entire migration is rolled back
  • Transaction control statements (BEGIN, COMMIT, ROLLBACK) in migration files are automatically skipped

Error Handling

The migration system includes graceful error handling for common scenarios:

Graceful failures (logged as warnings but don't stop execution):

  • "Duplicate column name"
  • "Table already exists"
  • "Duplicate key name"
  • "Unknown column" (on down migrations)
  • Other "already exists" errors

Hard failures (stop execution and rollback):

  • Syntax errors
  • Permission errors
  • Other database errors not in the graceful list

Best Practices

  1. Always include down migrations for reversibility
  2. Use timestamp prefixes in filenames for proper ordering
  3. Test migrations on a development database before production
  4. Keep migrations small and focused on a single change
  5. Don't modify already-applied migration files (create new ones instead)

Git Repository

The source code for this driver is available on 1x.ax at https://1x.ax/mamluk/kipchak/drivers/doctrine.

Previous
Filecache