diablomedia/doctrine1-bundle
Symfony bundle for Doctrine1 ORM, adapted from DoctrineBundle, that lets you configure Doctrine1 via Symfony config and adds query details to the profiler and debug toolbar. Install via Composer; works with the recommended diablomedia/doctrine1 fork.
Check your PHP version first (1.2.0 change):
composer require diablomedia/doctrine1-bundle:^1.1.4 diablomedia/doctrine1
Installation:
composer require diablomedia/doctrine1-bundle diablomedia/doctrine1
doctrine1 fork is recommended for better PHP compatibility (and is what 8.2–8.5 support is tested against).Enable Bundle (this is a Symfony bundle — it registers console commands, a DI service, and config):
// config/bundles.php
DiabloMedia\Bundle\Doctrine1Bundle\Doctrine1Bundle::class => ['all' => true],
Configure Database — create config/packages/doctrine1.yaml:
doctrine1:
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
cache_class: 'Doctrine_Cache_Array'
enable_query_cache: true
enable_result_cache: true
First Use Case — inject the service and run a raw query:
use DiabloMedia\Bundle\Doctrine1Bundle\Doctrine1;
public function __construct(private Doctrine1 $doctrine) {}
public function index()
{
$rows = $this->doctrine->getConnection()
->fetchAll('SELECT * FROM users WHERE active = 1');
return view('users.index', ['users' => $rows]);
}
First thing to try in 1.2.0 — generate models from schema:
php bin/console list doctrine1 # confirm the exact command name/options on your install
php bin/console doctrine1:generate-models
The new command wraps Doctrine1's CLI generate-models-from-yaml, so a YAML schema in config/doctrine/ (or wherever your schema_path/models_path config points) produces Base_* + concrete * record classes. Run it once during onboarding to see exactly what the package is generating for your project — that's the fastest way to understand how the bundle expects you to work.
Where to look first:
vendor/diablomedia/doctrine1-bundle/src/Command/ — the new generate-models command and its options (output dir, connection, schema path).vendor/diablomedia/doctrine1-bundle/src/DependencyInjection/Configuration.php — the authoritative list of config keys; the docs lag behind.Doctrine1 service class — the small API surface you actually call day-to-day (getConnection(), named connections).Schema-first models (the mainstream path):
# config/doctrine/schema.yml
User:
tableName: users
columns:
id: { type: integer(8), primary: true, autoincrement: true }
email: { type: string(255), notnull: true, unique: true }
active:{ type: boolean, default: 1 }
php bin/console doctrine1:generate-models
Base_User / Base_* classes — they are regenerated. Put custom behaviour in the concrete subclass (User extends Base_User).Connection management:
$writer = $this->doctrine->getConnection('writer');
$reader = $this->doctrine->getConnection('reader');
Query building:
Doctrine_Query is the idiomatic option:
$q = Doctrine_Query::create()
->select('u.*')
->from('User u')
->where('u.active = ?', true);
$users = $q->execute();
Model/ActiveRecord access:
$user = Doctrine_Core::getTable('User')->findOneByEmail($email);
$user->active = false;
$user->save();
Caching per connection:
connections:
default:
cache_class: 'Doctrine_Cache_Redis'
redis_host: '%env(REDIS_HOST)%'
enable_query_cache: true
enable_result_cache: true
Schema changes:
Base_* classes alongside the YAML.Repository/service wrapper (keeps Doctrine1 out of controllers):
class UserRepository
{
public function __construct(private Doctrine1 $doctrine) {}
public function findActive(): array
{
return Doctrine_Query::create()
->from('User u')
->where('u.active = ?', true)
->execute()
->toArray();
}
}
Transactions:
$conn = $this->doctrine->getConnection();
$conn->beginTransaction();
try {
$conn->execute('UPDATE accounts SET balance = balance - 100 WHERE id = ?', [1]);
$conn->execute('UPDATE accounts SET balance = balance + 100 WHERE id = ?', [2]);
$conn->commit();
} catch (\Throwable $e) {
$conn->rollBack();
throw $e;
}
Lifecycle listeners:
$this->doctrine->getConnection()->listen('postInsert', function ($event) {
// audit / post-save side effects
});
Regenerate-models in CI:
doctrine1:generate-models in a CI job and git diff --exit-code afterwards. It catches schema changes that were never regenerated — the single most common source of "works locally" bugs with Doctrine1.Regenerating into a container-aware app:
models_path. Make sure that path is PSR-0/PSR-4 autoloadable (Doctrine1's generated class names are not namespaced by default) — add it to composer.json autoload.classmap and composer dump-autoload after the first generation.Testing:
protected function setUp(): void
{
$this->conn = $this->doctrine->getConnection();
$this->conn->beginTransaction();
}
protected function tearDown(): void
{
$this->conn->rollBack();
}
Performance:
Legacy interop:
Doctrine_Connection::fetchAll() / fetchRow() accept raw SQL, so existing PDO-era code can migrate incrementally to models.PHP version floor moved to 8.2:
composer update will silently keep you on 1.1.4 (or fail on a strict platform check). Bump CI images and composer.json require.php at the same time, and only then update.The generate-models command overwrites files:
Base_* classes wholesale. Any customisation placed in a Base_* file is lost. Also commit or stash first — an accidental run on a dirty tree makes the diff hard to review. The command name/options vary by install, so confirm with php bin/console list doctrine1 rather than copying a signature from a blog post.It's a Symfony bundle, not a Laravel package:
config/packages/*.yaml), and the new console command are all Symfony. In a Laravel app you get the Doctrine1 service and connection config; you do not get the bundle's profiler, toolbar, or console commands without a Symfony kernel. Don't plan Laravel work around php artisan.Profiler / debug toolbar is not a Laravel feature:
/_profiler, Profiler/doctrine1.html.twig, or config/packages/dev/doctrine1.yaml profiler flags applies to Symfony apps only. In Laravel, use DB::listen-style logging or enable Doctrine1's own SQL logging instead.No connection pooling:
Identifier quoting and reserved words:
No auto-discovery of models:
models_path.Transactions don't nest:
$conn->getTransactionLevel() and implement your own nesting helper instead of assuming beginTransaction() is re-entrant.SQL logging:
doctrine1:
connections:
default:
logging: true
Verify config actually loaded:
bin/console debug:config doctrine1 in Symfony) before debugging anything else.Connection failures:
url DSN format (mysql://user:pass@host/db), and check cache_class names — Doctrine1's Doctrine_Cache_* classes are not interchangeable with Doctrine\Common\Cache\*.Generated-model errors after upgrade:
composer dump-autoload. Stale Base_* classes plus new PHP 8.5 deprecation behaviour is the usual cause of "worked before the upgrade".Deprecation noise on PHP 8.4/8.5:
error_reporting(E_ALL & ~E_DEPRECATED)) to see real errors, but don't ship that to production — fix the callsites instead.Pin the version during the upgrade window:
"diablomedia/doctrine1-bundle": "^1.2" after verifying PHP 8.2+ everywhere; "^1.1.4" if any environment is still on older PHP.Treat generated models as build artefacts with intent:
Environment-specific config:
Upgrade checklist for 1.2.0:
composer update diablomedia/doctrine1-bundle diablomedia/doctrine1 → regenerate models → composer dump-autoload → run the test suite → check for new deprecations on the highest PHP version you support.How can I help you explore Laravel packages today?