Skip to content

Unable to use readonly modifier for Collection properties #10660

@janedbal

Description

@janedbal

Bug Report

Doctrine fails to replace ArrayCollection with PersistentCollection for readonly properties. I assume this should be documented or bypassed (cannot imagine how).

Current docs states it should be possible.:

An entity class must not be final nor read-only but it may contain final methods or read-only properties.

Q A
BC Break no
Version 2.14.2

Current behavior

It throws exception, callstack:

LogicException : Attempting to change readonly property App\User::$accounts.
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionReadonlyProperty.php:46
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:697
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:528
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:863
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:377
 /opt/project/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:403

How to reproduce

Test:

<?php declare(strict_types = 1);

namespace App;

use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;

class UseIndexSqlWalkerTest extends TestCase
{
    public function createEntityManager(): EntityManagerInterface
    {
        $config = new Configuration();
        $config->setProxyDir(sys_get_temp_dir());
        $config->setProxyNamespace('MyProxies');
        $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver([__DIR__], false));

        return EntityManager::create(['driver' => 'pdo_sqlite', 'memory' => true], $config);
    }

    public function testReadonlyCollection(): void {
        $account = new Account();
        $user = new User($account);
        $em = $this->createEntityManager();
        $em->persist($account);
        $em->persist($user);
        $em->flush();
    }

}

Entities:

<?php declare(strict_types = 1);

namespace App;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class User
{

    /**
     * @ORM\Id
     * @ORM\Column(type="string", nullable=false)
     * @ORM\GeneratedValue
     */
    public ?int $id;

    /**
     * @ORM\ManyToMany(targetEntity=Account::class)
     */
    private readonly Collection $accounts;

    public function __construct(Account $account)
    {
        $this->accounts = new ArrayCollection([$account]);
    }

}
<?php declare(strict_types = 1);

namespace App;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Account
{

    /**
     * @ORM\Id
     * @ORM\Column(type="string", nullable=false)
     * @ORM\GeneratedValue
     */
    public ?int $id;

}

Expected behavior

No failure

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions