Doctrine2 / Symfony Cheat Sheet

Helpers / Make

  • php bin/console make:entity
  • php bin/console make:migration
  • php bin/console doctrine:schema:validate

Schema

Column Types

Date

Many to One / One to Many

One state has many towns. Town is the owning part with the native "state_id" column.

  • State.php
    • /**
       * @ORM\Entity(repositoryClass=StateRepository::class)
       */
      class State
      {
          /**
           * @ORM\Id
           * @ORM\GeneratedValue
           * @ORM\Column(type="integer")
           */
          private $id;
      
          /**
           * @ORM\OneToMany(targetEntity=Town::class, mappedBy="State")
           * This is the foreign "one" part of the relation. One state can
           * have many towns.
           */
          private $towns;
      
          public function __construct()
          {
              $this->towns = new ArrayCollection();
          }
      
          /**
           * @return Collection<int, Town>
           */
          public function getTowns(): Collection
          {
              return $this->towns;
          }
      
          public function addTown(Town $town): self
          {
              if (!$this->towns->contains($town)) {
                  $this->towns[] = $town;
                  $town->setState($this);
              }
      
              return $this;
          }
      
          public function removeTown(Town $town): self
          {
              if ($this->towns->removeElement($town)) {
                  // set the owning side to null (unless already changed)
                  if ($town->getState() === $this) {
                      $town->setState(null);
                  }
              }
      
              return $this;
          }
      }
  • Town.php

    • /**
       * @ORM\Entity(repositoryClass=TownRepository::class)
       * Giving the table name ("town") is optional with '@ORM\Table(name="studiengang")'
       */
      class Town
      {
          /**
           * @ORM\Id
           * @ORM\GeneratedValue
           * @ORM\Column(type="integer")
           */
          private $id;
      
          /**
           * @ORM\ManyToOne(targetEntity=State::class, inversedBy="towns")
           * @ORM\JoinColumn(nullable=false)
           * This is the owning "many" part of the relation as many towns can have one state
           * Native column: "state_id"
           * Seems like column name "state_id" is guessed from the relation
           * Other optional "@JoinColumn" options are: name="state_id", referencedColumnName="id"
           * Note that $State is upper case (Entity?) and "towns" collection is lowercase
           */
          private $State;
      
          public function getState(): ?State
          {
              return $this->State;
          }
      
          public function setState(?State $State): self
          {
              $this->State = $State;
      
              return $this;
          }
      }

Associations

Trigger Lazy Loading

  • $entity = ... get entity
    $towns = $entity->getTowns();  // is empty because of lazy loading.
    // Load relation data:
    $entity->getTowns()->initialize();

Create and persist

  • // In a symfony controller inject the entity manager:
    public function test(EntityManagerInterface $em) {     
  • $country = new Country();
    $country->setName('Foo');
    
    $em->persist($country);
    $em->flush();

 

Query Builder

Queries should be mainly in a repository.

  • // ExampleRepository.php
    
        public function findOneByFoo($foo)
        {
            // automatically knows e = "example" entity because we are in this repository
            $qb = $this->createQueryBuilder('e') 
                ->where('s.bar LIKE :foo')
                ->setParameter('foo', "%$foo%")
            ;
    
            return $qb->getQuery()->getOneOrNullResult();
        }

count

  • $qb = $this->createQueryBuilder('standort')
        ->select('count(standort.id)')
        ->where('standort.town=:town')
        ->setParameter('town', $town)
    ;
    
    $count = $qb->getQuery()->getSingleScalarResult();

where In

  • $qb->andWhere('user.name IN (:names)');
    $qb->setParameter('names', ['Karen', 'Kevin', 'Kim']);

offset / limit

  • $qb->setMaxResults($limit)
    $qb->setFirstResult($offset)
    

index by

  • $this->createQueryBuilder('c')
        ->indexBy('c', 'c.slug')
        ->getQuery()
        ->getArrayResult()
    

Use another repository in a repository

  • // in UserRepository.php
    $locations = $this->getEntityManager()->getRepository(Location::class) // without ...Repository!
        ->findDistinctLocations()

Migrations

Use PDO

  • $pdo = $this->connection->getNativeConnection();
    
    // Or in a repository:
    $pdo = $this->getEntityManager()->getConnection()->getNativeConnection();

Livecycle Events (preUpdate, postPersist, ...)

https://symfony.com/doc/5.4/doctrine/events.html

PostPersist = OnCreate

Use Listeners

  • src/EventListener/FooInvalidateCacheListener.php
    • <?php
      
      namespace App\EventListener;
      
      use App\Entity\Foo;
      use App\Service\HttpCacheUtils;
      use Doctrine\Persistence\Event\LifecycleEventArgs;
      
      class FooInvalidateCacheListener
      {
          private $httpCacheUtils;
      
          public function __construct(HttpCacheUtils $httpCacheUtils)
          {
              $this->httpCacheUtils = $httpCacheUtils;
          }
      
          public function postUpdate(Foo $Foo, LifecycleEventArgs $args)
          {
              $tag = 'b' . $Foo->getId();
      
              $this->httpCacheUtils->invalidateCacheByTags([$tag], get_class(), 'Update');
          }
      }
  • config/services.yaml

    • services:
      
        App\EventListener\BerufInvalidateCacheListener:
          tags:
            - { name: doctrine.orm.entity_listener, entity: App\Entity\Beruf, event: postUpdate }

 

Save changed values to the database (not done automatically)

  • // src/Entity/Foo.php
    
     * @ORM\HasLifecycleCallbacks()
     */
    class Foo
    {
        ...
    
        /**
         * @ORM\PostPersist()
         * @return void
         */
        public function myPostPersistFunction(LifecycleEventArgs $args): void
        {
            $this->setBar('Baz');
            $args->getEntityManager()->flush();
        }
    
    }

Auditable / Versionable

https://github.com/DamienHarper/auditor-bundle

Installation:

  • composer require damienharper/auditor-bundle
  • config/packages/dh_auditor.yaml
  • bin/console doctrine:schema:update --dump-sql
    • Take line for "example_audit" table and put it into migration
  • php bin/console doctrine:migrations:generate
    • $this->addSql("CREATE TABLE example_audit (id INT UNSIG....");
  • bin/console doctrine:migrations:migrate --no-interaction -vvv

 

Caching

$query->setResultCacheId('my_custom_id');
// or shorter notation with lifetime option
$query->useResultCache(true, 3600, 'my_custom_id');

// to delete cache
$cacheDriver = $entityManager->getConfiguration()->getResultCacheImpl();
$cacheDriver->delete('my_custom_id');
// to delete all cache entries
$cacheDriver->deleteAll();