doctrine 2 - how to handle out-of-sync database / entities
I am working on an a PHP web application, and am hoping to use Doctrine 2.0 for ORM functionality. However, I am having significant trouble with one particular type of query.
Consider a simple domain model containing Foo and Bar Entities. A Foo Entity contains many Bars, and a Bar may be marked active.
/** @Entity */
class Foo
{
/** @OneToMany(targetEntity="Bar", mappedBy="foo") */
protected $bars;
/** Returns Bars marked as active. */
public function getActiveBars()
{
// XXX What goes here?
}
}
/** @Entity */
class Bar
{
/** @ManyToOne(targetEntity="Foo", inversedBy="bar") */
protected $foo;
/** @Column(type="boolean") */
protected $active;
/** ...setter omitted for clarity... */
}
My question is: what code should be placed in the getActiveBars
method?
The following code works correctly, but performance will be an issue when there are many inactive Bar objects in the database. Iterating over the Bar collection causes a large query against the database to be performed.
function getActiveBars()
{
$ret = array();
foreach($this->bars as $bar) {
if ($bar->isActive()) $ret[] = $bar;
}
return $ret;
}
Executing a DQL query such as SELECT b FROM Bar l WHERE b.active = true AND b.foo = :foo
within getActiveBa开发者_StackOverflow社区r() seems to be the solution, however such a query will not detect any outstanding changes to Bar made before EntityManager#flush() is called (since DQL is executed against the database).
It's probably not the end of the world. If Foo::bars is loaded, has been been modified, and hasn't been committed back to the database, your only option is to iterate.
If Foo::bars hasn't been loaded, then it certainly hasn't been modified, so you may as well query the database.
(Finally, if bars have been loaded, modified, and committed back, the size of the bars collection will decide whether fetching or iterating would be better, probably)
That makes me curious is there's a way for an entity to tell if a member that is a collection has been loaded or not.
The documentation seems to imply that a Proxied ArrayCollection (Foo:bars, in your example), will not pass an instanceof ArrayCollection
test. So you might try to get smart like:
class Foo {
public function getActiveBars(){
if ($this->bars instanceof \Doctrine\Common\Collection\ArrayCollection){
//get active bars by iterating over $this->bars
}else{
//get active bars by DQL, and avoid loading $this->bars (for now).
}
}
}
精彩评论