New Release: Doctrine ORM 2.9 with Attributes, Typed Properties, more

Posted on May 24, 2021 by Benjamin Eberlei


We have released a new minor version 2.9 of Doctrine ORM, the first version with support for using PHP 8 Attributes as a new driver for mapping entities and several other changes. See all changes and contributors in the Changelog on Github.

Attributes Mapping Driver

The following code example shows many of the mappings that are re-using the annotation classes for familiarity:

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping AS ORM;

#[ORM\Entity(repositoryClass: PostRepository::class)]
class Post
{
    #[ORM\Column(type: Types::INTEGER)]
    #[ORM\Id, ORM\GeneratedValue(strategy: 'AUTO')]
    private ?int $id;

    #[ORM\Column(type: Types::BOOLEAN)]
    private bool $published = false;

    #[ORM\Column(type: Types::SIMPLE_ARRAY)]
    private array $text = [];

    #[ORM\ManyToOne(targetEntity: User::class)]
    public $author;

    #[ORM\ManyToMany(targetEntity: Tag::class)]
    #[ORM\JoinTable(name: "post_tags")]
    #[ORM\JoinColumn(name: "post_id", referencedColumnName: "id")]
    #[ORM\InverseJoinColumn(name: "tag_id", referencedColumnName: "id")]
    public Collection $tags;
}

Typed Property Defaults

Since PHP 7.4 types can be declared on class properties and Doctrine now uses these type declarations to reduce amount of mapping boilerplate:

  • Columns don't need the type definitions
  • ManyToOne and OneToOne don't need target entity definitions

Example:

use Doctrine\ORM\Mapping AS ORM;

#[ORM\Entity(repositoryClass: UserRepository::class)]
class User
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    public ?int $id = null;

    #[ORM\Column]
    public \DateTime $created;

    #[ORM\ManyToOne]
    public Email $email;
}

Psalmified APIs

Improved the documentation to make sure static analysis tools and IDEs know about the right entity classes returned from EntityManager, EntityRepository and other public ORM APIs. This includes generics support when you extend EntityRepository.

use Doctrine\ORM\EntityRepository;
use App\Entity\User;

/**
 * @template-extends EntityRepository<User>
 */
class UserRepository extends EntityRepository
{
}

Query::HINT_READ_ONLY

A new query hint is added that allows hydrating entities through DQL that are marked as read only for the unit of work session, as long as they are not yet loaded as writeable:

$dql = 'SELECT u FROM ' . ReadOnlyEntity::class . ' u WHERE u.id = ?1';

$query = $entityManager->createQuery($dql);
$query->setParameter(1, $user->id);
$query->setHint(Query::HINT_READ_ONLY, true);

$user = $query->getSingleResult();

Index/UniqueConstraints using Field Names

Instead of specifying column names for an index or unique-constraint declaration you can now alternatively use field names.


use Doctrine\ORM\Mapping AS ORM;

#[ORM\Entity]
#[ORM\Index(fields: ["isPublished"])]
class Post
{
    #[ORM\Column]
    public bool $isPublished = false;
}

This simplifies mapping as the column names passed through the naming strategy do not need to be known.

INDEX BY Associations

Previously DQL INDEX BY was not possible for assocations, now you can:

$dql = 'SELECT p, u FROM Post INDEX BY p.author JOIN p.author u WHERE p.id = 3';

Deprecations

Doctrine ORM 2.9 rethinks deprecations and integrates with our new doctrine/deprecations library.

  • Undeprecate merge() and detach() as no replacements are available yet
  • Notify Persist Change Tracking: Use Explicit Change Tracking instead
  • DQL SELECT PARTIAL syntax, use Value Objects with SELECT NEW instead
  • EntityManager::flush() with arguments
  • EntityManager::clear() with arguments (use detach)
  • Named Queries in Mapping (use Repository)
  • cli-config.php for console command configuration, inject EntityManagerProvider instead.
  • Deprecate doctrine/cache for metadata caching, use PSR-6 cache instead

Cache Deprecations and PSR-6

Over the next versions we will deprecate use of doctrine/cache and replace it with PSR-6. If you are still using doctrine/cache code in your own application make sure to force the versions to "^1.10" in composer.json. Details

PHP 7.1 Support

ORM 2.9 reintroduces PHP 7.1 support, because it wasn't technically unsupported anyways. No changes were necessary to the code to allow it again except in the testsuite.

The PHP 7.1 support was re-added to allow a very broad approach to prepare for some of the deprecations that are introduced in ORM 2 and will be removed in version 3.0.

Coding Standard Support

Doctrine ORM 2.9 now supports and fully validates against Doctrine Coding Standard version 9.0+. This greatly improves automatic pull request checks as all new violations in a PR get caught and inlined into the PR as comments.