doctrine/doctrine-bundle
Symfony bundle integrating Doctrine DBAL and ORM. Provides database abstraction, schema tools, and an object-relational mapper with DQL for powerful queries, plus configuration and tooling that fits the Symfony ecosystem.
Installation:
composer require doctrine/doctrine-bundle
Add to config/bundles.php:
return [
// ...
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
];
Configuration:
Edit config/packages/doctrine.yaml:
dbal:
url: '%env(DATABASE_URL)%'
# or driver-specific config:
# driver: 'pdo_mysql'
# server_version: '8.0'
# charset: 'utf8mb4'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
First Use Case:
Create an entity (src/Entity/User.php):
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $name;
// getters/setters...
}
Run migrations:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
Entity Management:
$user = $entityManager->find(User::class, 1);
$entityManager->persist($newUser);
$entityManager->flush();
$users = $entityManager->getRepository(User::class)->findBy(['name' => 'John']);
Querying:
$query = $entityManager->createQuery('SELECT u FROM App\Entity\User u WHERE u.name = :name')
->setParameter('name', 'John');
$qb = $entityManager->createQueryBuilder();
$qb->select('u')
->from(User::class, 'u')
->where('u.name = :name')
->setParameter('name', 'John');
Migrations:
php bin/console make:migration
php bin/console doctrine:migrations:migrate
php bin/console doctrine:migrations:rollback
Event Listeners/Subscribers:
config/packages/doctrine.yaml:
orm:
entity_listeners:
App\EventListener\UserListener: ~
namespace App\EventListener;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
#[AsEntityListener]
class UserListener
{
public function prePersist(LifecycleEventArgs $args): void
{
$user = $args->getObject();
$user->setCreatedAt(new \DateTime());
}
}
Custom DQL Functions:
config/packages/doctrine.yaml:
orm:
dql:
string_functions:
CONCAT_WS: DoctrineExtensions\Query\Mysql\StringFunctions\ConcatWs
Symfony Forms:
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
$builder->add('user', EntityType::class, [
'class' => User::class,
'choice_label' => 'name',
]);
API Platform:
# config/packages/api_platform.yaml
api_platform:
formats:
jsonld:
mime_types: ['application/ld+json']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]
Testing:
use Doctrine\ORM\Tools\SchemaTool;
public function setUp(): void
{
$schemaTool = new SchemaTool($this->entityManager);
$schemaTool->dropDatabase();
$schemaTool->createSchema($this->entityManager->getMetadataFactory()->getAllMetadata());
}
Caching:
orm:
metadata_cache_driver: apcu
query_cache_driver: apcu
result_cache_driver: apcu
Proxy Classes:
php bin/console cache:clear
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:cache:clear-query
php bin/console doctrine:cache:clear-result
auto_generate_proxy_classes: true in development and set to false in production with manual generation.Naming Conflicts:
orm:
naming_strategy: doctrine.orm.naming_strategy.underscore
Lazy Loading:
fetch="EAGER" or DQL joins:
$query = $entityManager->createQuery('SELECT u, a FROM App\Entity\User u LEFT JOIN u.addresses a');
Transactions:
$entityManager->beginTransaction();
try {
// Operations...
$entityManager->commit();
} catch (\Exception $e) {
$entityManager->rollback();
}
Schema Updates:
--complete flag:
php bin/console doctrine:schema:update --complete
Enable SQL Logging:
dbal:
logging: true
profiling: true
View logs in var/log/dev.log.
Query Profiling:
$profiler = $entityManager->getConnection()->getConfiguration()->getSQLLogger();
$profiler->startQueryLogging();
// ... execute queries ...
$queries = $profiler->getQueries();
Entity Metadata:
$metadata = $entityManager->getClassMetadata(User::class);
dump($metadata->getFieldNames());
Event Debugging:
orm:
eventmanager: doctrine.eventmanager
Then inject EventManager and log events:
$eventManager->addEventListener([LifecycleEventArgs::class], function ($args) {
error_log('Event triggered: ' . get_class($args->getObject()));
});
Deprecated Options:
auto_mapping: true (deprecated). Use explicit mappings:
orm:
auto_mapping: false
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
Connection Handling:
dbal:
connections:
default:
url: '%env(DATABASE_URL)%'
read_replica:
url: '%env(READ_DATABASE_URL)%'
wrapper_class: Doctrine\DBAL\Connection
Custom Entity Managers:
services.yaml:
services:
App\Doctrine\CustomEntityManager:
alias: doctrine.orm.entity_manager
public: true
namespace App\Doctrine\DBAL\Types;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class JsonType extends Type
{
public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{
return 'JSON';
}
// ... other methods ...
}
Register in config/packages/doctrine.yaml:
dbal:
types:
json: App\Doctrine\DBAL\Types\JsonType
How can I help you explore Laravel packages today?