Skip to content

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:migrate
php
<?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 $fillable list; 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 database

As 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) ​

EloquentDoctrine
Active Record ($post->save())Entity + EntityManager
$fillable mass assignmentexplicit setters / forms
Quick CRUD feelMore ceremony, clearer boundaries

Neither is "wrong". If you know PDO, both are ergonomic layers.

Next: associations and the N+1 trap.

Best,

-- ł.

Continues after /lab/php-tutorial-for-fe-devs/