Skip to content

Loading…

DDC-173: Collection does not get turned into a PersistentCollection when loaded from a proxy #2379

Closed
doctrinebot opened this Issue · 7 comments

1 participant

@doctrinebot

Jira issue originally created by user ablock:

We have two entities, a BugReport and a Reporter. The BugReport holds a reference to a Reporter, and the Reporter holds a reference to all his bug reports:

/****
 * @Entity
 */
class Reporter {
    /****
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    public $id;

    /****
     * @OneToMany(targetEntity="BugReport", mappedBy="reporter")
     */
    public $bugReports;

    public function **construct() {
        $this->bugReports = new \Doctrine\Common\Collections\ArrayCollection;
    }
}


/****
 * @Entity
 */
class BugReport {
    /****
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    public $id;

    /****
     * @OneToOne(targetEntity="Reporter")
     * @JoinColumn(name="reporter_id", referencedColumnName="id")
     */
    public $reporter;
}

$tool = new \Doctrine\ORM\Tools\SchemaTool($em);
$classes = array(
  $em->getClassMetadata('Entities\Reporter'),
  $em->getClassMetadata('Entities\BugReport')
);
$tool->createSchema($classes);

$reporter = new Reporter();
$bug = new BugReport();
$bug->reporter = $reporter;

$bug2 = new BugReport();
$bug2->reporter = $reporter;

$em->persist($reporter);
$em->persist($bug);
$em->persist($bug2);

$em->flush();

$reporterId = $reporter->id;
$bug1Id = $bug->id;
$bug2Id = $bug2->id;

$em->clear();

$reporter = $em->find('Entities\Reporter', $reporterId);

echo get_class($reporter->bugReports);

$em->clear();

$bug = $em->find('Entities\BugReport', $bug1Id);

echo get_class($bug->reporter->bugReports);

This outputs "Doctrine\ORM\PersistentCollectionDoctrine\Common\Collections\ArrayCollection"

It seems that when the Reporter is loaded from a Proxy, the fields don't get turned into "managed fields"

@doctrinebot

Comment created by romanb:

I rather suspect that the $bug->reporter proxy is never initialized as $bug->reporter->bugReports accesses a public property and lazy loading can not work with public properties as there is no way for the proxy to intercept these calls.

What if you use "echo get_class($bug->reporter->getBugReports());", provided you add a method getBugReports() first ?

Or did you only make all these fields public for the contrived example shown here and in reality they're not?

@doctrinebot

Comment created by romanb:

Fixed formatting

@doctrinebot

Comment created by ablock:

What I'm asking is that in the case of loading the Reporter directly from the EntityManager, the bugReports property gets turned into a PersistentCollection, but if the Reporter is loaded through a proxy, the fields stay as they are. Can't the proxy, in its constructor convert all the fields into Proxies/PersistenCollections?

@doctrinebot

Comment created by romanb:

When loading the reporter the bugReports property gets wrapped in a PersistentCollection because it is directly referenced by a loaded/managed entity, the reporter.

When the bugreport is loaded directly in the second case only the fields of the bugreport are properly proxied. When you access a public property of the proxy, the proxy cant be initialized. Creating PersistentCollections in an uninitialized proxy as you suggest is a) unnecessary overhead and b) not really a solution to the fact that public properties circumvent lazy loading, i.e. while $bug->reporter->bugReports might work then, other fields, especially simple value fields (strings, integers,...) will remain null or at their default value because they're not loaded.

If you want to use public properties, you can, but you must be aware that they dont play nice with lazy-loading. In general, public properties in entities are discouraged and this is also stated in the manual: http://www.doctrine-project.org/documentation/manual/2_0/en/architecture:entities#persistent-fields

So my question comes down to: Does the issue you describe in this ticket here occur without public properties? If yes, it is a bug.

@doctrinebot

Comment created by ablock:

To answer your question, the example in real life is not using public properties, and works totally fine using a getter.

In this contrived example, I'm using public properties, but what I'm actually doing is writing a "Mapper" which converts my Domain entities into a different layer...similar to the Automapper project which is popular in .NET circles. Since I like to keep all my properties private, and only create a "getter" when absolutely necessary, and also for the sake of automation, I use reflection to get at these properties. This works fine, until I try to map an initialized proxy. So I'm not actually using public properties, but I'm using reflection to get at the properties.

@doctrinebot

Comment created by ablock:

I've taken a totally different approach to the issue. For some reason it didn't occur to me that fields in proxies would be scalars (and thus there is no proxy for them, forgetting about the fact that it doesn't make sense to lazy load simple values)

@doctrinebot

Issue was closed with resolution "Invalid"

@doctrinebot doctrinebot closed this
@doctrinebot doctrinebot added the Bug label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.