Doctrine & migrations
Migrations are schema in git. Doctrine ORM is Data Mapper on top of DBAL/PDO - different philosophy from Eloquent's Active Record, same database underneath.
Entity + migration
bash
php bin/console make:entity Post
# answer prompts: title (string), body (text), …
php bin/console make:migration
php bin/console doctrine:migrations:migratephp
<?php
namespace App\Entity;
use App\Repository\PostRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PostRepository::class)]
class Post
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $body = null;
// getters / setters generated by Maker…
}Comments for the code above:
- Attributes map the class to a table - no magic
$fillablelist; you set properties via setters or constructor - Repository class is where custom queries live (
findPublished(), etc.) - Migrations are PHP classes under
migrations/- review the generated SQL before prod 😅
Persist / flush
php
<?php
$post = new Post();
$post->setTitle('Hello');
$post->setBody('First post');
$em->persist($post); // unit of work: schedule insert
$em->flush(); // hit the databaseAs you can see, Doctrine doesn't save on every setter - you flush deliberately. That batching is a feature once you get used to it.
Read
php
<?php
$post = $posts->find($id); // or findOrFail-ish via get/null check
$all = $posts->findBy([], ['id' => 'DESC']);
$one = $posts->findOneBy(['title' => 'Hello']);Custom DQL / QueryBuilder when findBy isn't enough:
php
<?php
return $this->createQueryBuilder('p')
->andWhere('p.title LIKE :q')
->setParameter('q', '%'.$q.'%')
->orderBy('p.id', 'DESC')
->getQuery()
->getResult();Eloquent vs Doctrine (honest)
| Eloquent | Doctrine |
|---|---|
Active Record ($post->save()) | Entity + EntityManager |
$fillable mass assignment | explicit setters / forms |
| Quick CRUD feel | More ceremony, clearer boundaries |
Neither is "wrong". If you know PDO, both are ergonomic layers.
Next: associations and the N+1 trap.
Best,
-- ł.