DDC-3696: flushing traversable objects #4538

Open
opened 2026-01-22 14:44:21 +01:00 by admin · 4 comments
Owner

Originally created by @doctrinebot on GitHub (Apr 17, 2015).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user mw-orm:

Hi,

https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L331

it would be easier if you could the method "flush" pass a traversable object. I've implement this behavior in my decorated entity manager. In my opinion this should be an official supported behavior.

The new code should be inside of Orm\UnitOfWork::commit($entity = null) and looks like the following code:

// Compute changes done since last commit.
if( $entity instanceof \Traversable ) {
    $entity = iterator*to*array( $entity );
}

By the way, the foreach part can be simplified to one line. This can be made possible by array_walk.
You can use:

} elseif (is_array($entity)) {
    array_walk($entity, array($this, 'computeSingleEntityChangeSet'));
}

Now, the new code in one piece:

// Compute changes done since last commit.
if( $entity instanceof \Traversable ) {
    $entity = iterator*to*array( $entity );
}

if ($entity === null) {
    $this->computeChangeSets();
} elseif (is_object($entity)) {
    $this->computeSingleEntityChangeSet($entity);
} elseif (is_array($entity)) {
    array_walk($entity, array($this, 'computeSingleEntityChangeSet'));
}

Thanks in advance. :-)

Originally created by @doctrinebot on GitHub (Apr 17, 2015). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user mw-orm: Hi, https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L331 it would be easier if you could the method "flush" pass a traversable object. I've implement this behavior in my decorated entity manager. In my opinion this should be an official supported behavior. The new code should be inside of Orm\UnitOfWork::commit($entity = null) and looks like the following code: ``` // Compute changes done since last commit. if( $entity instanceof \Traversable ) { $entity = iterator*to*array( $entity ); } ``` By the way, the foreach part can be simplified to one line. This can be made possible by array_walk. You can use: ``` } elseif (is_array($entity)) { array_walk($entity, array($this, 'computeSingleEntityChangeSet')); } ``` Now, the new code in one piece: ``` // Compute changes done since last commit. if( $entity instanceof \Traversable ) { $entity = iterator*to*array( $entity ); } if ($entity === null) { $this->computeChangeSets(); } elseif (is_object($entity)) { $this->computeSingleEntityChangeSet($entity); } elseif (is_array($entity)) { array_walk($entity, array($this, 'computeSingleEntityChangeSet')); } ``` Thanks in advance. :-)
admin added the New Feature label 2026-01-22 14:44:21 +01:00
Author
Owner

@doctrinebot commented on GitHub (Apr 17, 2015):

Comment created by @ocramius:

Two things:

  1. we are more inclined to remove the parameter from flush(), as it was exploited for purposes it wasn't designed for.
  2. what happens if you persist an entity that is a traversable?
@doctrinebot commented on GitHub (Apr 17, 2015): Comment created by @ocramius: Two things: 1) we are more inclined to remove the parameter from `flush()`, as it was exploited for purposes it wasn't designed for. 2) what happens if you persist an entity that is a traversable?
Author
Owner

@doctrinebot commented on GitHub (Apr 17, 2015):

Comment created by mw-orm:

1.Is that really planned? I guess this is a helpful feature and makes not a difference if you flush a small collection of entities before you flush the remaining entities without the parameter.

  1. The object will be treated as entity, because it's an object. This will throw an "Doctrine\ORM\Mapping\MappingException" exception with following message:
Class "SplObjectStorage" is not a valid entity or mapped super class.
Exception-Class: Doctrine\ORM\Mapping\MappingException 
Exception-Code: 0 
Exception-File: Composer/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php 
Exception-Line: 346

This simple senseless code shows you what you can do to test this behavior.

$em = Doctrine_Factory::getEntityManager();
$em->persist($entity);
$x = new SplObjectStorage();
$x->attach($entity);
$em->flush($x);
@doctrinebot commented on GitHub (Apr 17, 2015): Comment created by mw-orm: 1.Is that really planned? I guess this is a helpful feature and makes not a difference if you flush a small collection of entities before you flush the remaining entities without the parameter. 1. The object will be treated as entity, because it's an object. This will throw an "Doctrine\ORM\Mapping\MappingException" exception with following message: ``` Class "SplObjectStorage" is not a valid entity or mapped super class. Exception-Class: Doctrine\ORM\Mapping\MappingException Exception-Code: 0 Exception-File: Composer/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php Exception-Line: 346 ``` This simple senseless code shows you what you can do to test this behavior. ``` $em = Doctrine_Factory::getEntityManager(); $em->persist($entity); $x = new SplObjectStorage(); $x->attach($entity); $em->flush($x); ```
Author
Owner

@doctrinebot commented on GitHub (Apr 17, 2015):

Comment created by @ocramius:

You can implement the Traversable interface (as iterator or iteratoraggregate) on any entity.

As for "planned", it obviously won't happen in 2.x.

We are mainly interested in finding better ways to speed up flush() operations rather than delegating transactional boundaries knowledge to the user.

Anyway, so far, flush($entities) has been more of a source of bugs rather than an actual useful API.

@doctrinebot commented on GitHub (Apr 17, 2015): Comment created by @ocramius: You can implement the `Traversable` interface (as iterator or iteratoraggregate) on any entity. As for "planned", it obviously won't happen in 2.x. We are mainly interested in finding better ways to speed up `flush()` operations rather than delegating transactional boundaries knowledge to the user. Anyway, so far, `flush($entities)` has been more of a source of bugs rather than an actual useful API.
Author
Owner

@doctrinebot commented on GitHub (Apr 18, 2015):

Comment created by mw-orm:

The Traversable interface serves to detect whether a class is traversable using foreach. The flush parameter surrenders this parameter one-to-one to $this->unitOfWork->commit($entity).
What I want to do is to hand over an traversable object to the method EntityManager::flush with a collection of entities instead of an single entity object. It's also possible to pass a collection of entities as array. But it's not possible to pass a collection of entities as an object, because when passing an object UnitOfWork::commit assumes that it's an entity. To implement the Traversable interface on any entity will not help and is not the same intention. Because an Traversable object it means, that is a collection of entities like an array.

In my opinion there are not objections that speak against it to support traversable object (SplObjectStorage, ArrayIterator, ArrayObject).

There are two ways to do that.

The first option would be:

// Compute changes done since last commit.
if( $entity instanceof \Traversable ) {
    $entity = iterator*to*array( $entity );
}

if ($entity === null) {
    $this->computeChangeSets();
} elseif (is_object($entity)) {
    $this->computeSingleEntityChangeSet($entity);
} elseif (is_array($entity)) {
    array_walk($entity, array($this, 'computeSingleEntityChangeSet'));
}

The second option would be:

// Compute changes done since last commit.
if ($entity === null) {
    $this->computeChangeSets();
} elseif (is_array($entity) || $entity instanceof \Traversable) {
    foreach ($entity as $object) {
        $this->computeSingleEntityChangeSet($object);
    }
} elseif (is_object($entity)) {
    $this->computeSingleEntityChangeSet($entity);
}

By the way: the SplObjectStorage object has the benefit that every entity is present only once.

@doctrinebot commented on GitHub (Apr 18, 2015): Comment created by mw-orm: The Traversable interface serves to detect whether a class is traversable using foreach. The flush parameter surrenders this parameter one-to-one to $this->unitOfWork->commit($entity). What I want to do is to hand over an traversable object to the method EntityManager::flush with a collection of entities instead of an single entity object. It's also possible to pass a collection of entities as array. But it's not possible to pass a collection of entities as an object, because when passing an object UnitOfWork::commit assumes that it's an entity. To implement the Traversable interface on any entity will not help and is not the same intention. Because an Traversable object it means, that is a collection of entities like an array. In my opinion there are not objections that speak against it to support traversable object (SplObjectStorage, ArrayIterator, ArrayObject). There are two ways to do that. The first option would be: ``` // Compute changes done since last commit. if( $entity instanceof \Traversable ) { $entity = iterator*to*array( $entity ); } if ($entity === null) { $this->computeChangeSets(); } elseif (is_object($entity)) { $this->computeSingleEntityChangeSet($entity); } elseif (is_array($entity)) { array_walk($entity, array($this, 'computeSingleEntityChangeSet')); } ``` The second option would be: ``` // Compute changes done since last commit. if ($entity === null) { $this->computeChangeSets(); } elseif (is_array($entity) || $entity instanceof \Traversable) { foreach ($entity as $object) { $this->computeSingleEntityChangeSet($object); } } elseif (is_object($entity)) { $this->computeSingleEntityChangeSet($entity); } ``` By the way: the SplObjectStorage object has the benefit that every entity is present only once.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#4538