[DDC-3354] Replacing indexed item on association with indexBy cannot comply with unicity constraint Created: 17/Oct/14  Updated: 19/Oct/14

Status: Open
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.4.6
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Charles Bouchard-Légaré Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: collection, mapping, orm


 Description   

Using a bidirectional one-to-many relation having an 'indexBy' clause on the owning side and a unique constraint over the indexed field and the 'mappedBy' field of the inverse side, replaced indexed fields being inserted before removed violated the unique constraint.

As stated in the code examples below, if done without the unique constraint, when replacing an indexed field, the row is replaced in the database (new one created, old one deleted).

Here is an example:

Yaml Mapping for Person and Email
Person:
    type: entity
    id:
        id:
            type: integer
            generator:
                strategy: AUTO
    oneToMany:
        emailAddresses:
            targetEntity: Email
            indexBy: name
            mappedBy: owner
            cascade: [ all ]
            orphanRemoval: true

Email:
    type: entity
    id:
        id:
            type: integer
            generator:
                strategy: AUTO
    fields:
        name:
            type: string
            nullable: false
        address:
            type: string
            nullable: false
    uniqueConstraints:
        unique_named_person_email:
            columns: [ owner_id, name ]
    manyToOne:
        owner:
            targetEntity: Person
            inversedBy:   emailAddresses
            joinColumn:
                name: owner_id
                referencedColumnName: id
PHP definitions for Person and Email
class Person {
    protected $id;
    /**
     * @var Collection|Email[] $emailAddresses
     */
    protected $emailAddresses;

    /**
     * I would expect this to work.
     */
    public function setEmailAddress_expected($name, $emailAddress)
    {
        /**
         * Expected to work but throws UniqueConstraintViolationException
         * If done without the 'unique_named_person_email' constraint', it 
         * works properly creating a new Email and deleting the old one.
         */
        $this->emailAddresses->set(
            (string) $name,
            new Email($this, (string) $name, (string) $emailAddress)
        );
    }
    
    /**
     * I would expect this to work.
     */
    public function setEmailAddress_expected_too($name, $emailAddress)
    {
        /**
         * Expected to work but throws UniqueConstraintViolationException
         * If done without the 'unique_named_person_email' constraint, it 
         * works properly creating a new Email and deleting the old one.
         */
        $this->emailAddresses->remove((string) $name);
        $this->emailAddresses->set(
            (string) $name,
            new Email($this, (string) $name, (string) $emailAddress)
        );
    }

    /**
     * Works
     */
    public function setEmailAddress_works($name, $emailAddress)
    {
        $existing = $this->emailAddresses->get((string) $name);
        if ($existing) {
            $existing->setAddress((string) $emailAddress);
        } else {
            $this->emailAddresses->set(
                (string) $name,
                new Email($this, (string) $name, (string) $emailAddress)
            );
        }
    }
}

class Email {
    protected $id;
    protected $owner;
    protected $name;
    protected $address;
    public function __construct($owner, $name, $address)
    {
        $this->owner = $owner;
        $this->name = $name;
        $this->address = $address;
    }

    /**
     * I am forced to create this method.
     */
    public function setAddress($address)
    {
        $this->address = $address;
    }
}

IMO, Using 'indexBy' on a one-to-many relation should automatically generate a unique constraint for the combination of the indexed field and the 'mappedBy' field.

Documentation states that

Fields that are used for the index by feature HAVE to be unique in the database. The behavior for multiple entities with the same index-by field value is undefined.

which is unclear (especially the use of the word 'database'). I wonder why indexes used for association should be unique for a whole table.



 Comments   
Comment by Marco Pivetta [ 19/Oct/14 ]

I wonder why indexes used for association should be unique for a whole table.

That's because we can't ensure that indexes aren't overwritten when hydrating a collection: that is what the behavior "undefined" stands for.

As for replacing values in the collection, that's how the ORM works in any case, as it inserts data before removing any data to avoid removes from causing FK constraint failures (see https://github.com/doctrine/doctrine2/blob/d361ed904e5d56711304b755b5b2a8484d9a35b6/lib/Doctrine/ORM/UnitOfWork.php#L347-L379)





[DDC-3234] Empty properties when filtering collections Created: 30/Jul/14  Updated: 19/Oct/14  Resolved: 19/Oct/14

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.5, 2.4.2
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Diogo Domanski de Souza Assignee: Marco Pivetta
Resolution: Cannot Reproduce Votes: 0
Labels: Collection, Criteria, orm
Environment:

PHP 5.5.9, Nginx 1.4.6, PHP FPM, Zend Framework 2.3.1, Linux Ubuntu 14.04



 Description   

I'm facing some troubles when filtering an entity association (ArrayCollection) by using Criteria.

The scenario is the following: I have 3 entities:

User.php
<?php

namespace Entity;

use Doctrine\ORM\Mapping as ORM;
use \Doctrine\Common\Collections\Criteria;
use Zend\Stdlib\Hydrator;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Entity\UserRepository")
 * @ORM\HasLifecycleCallbacks
 * @author domanski
 */
class User implements \Serializable {
	/**
	 *
	 * @ORM\Id
	 * @ORM\Column(name="id", type="integer", nullable=false)
	 * @ORM\GeneratedValue(strategy="AUTO")
	 * @var integer
	 */
	private $id;

	/**
	 * @ORM\Column(name="delete_date", type="datetime")
	 * @var \DateTime
	 */
	private $deleteDate;
	
	public function __construct(array $options = array()) {
		if (!empty($options))
			$this->hydrate($options);
	}

	public function hydrate(array $options = array(), \Doctrine\ORM\EntityManager $em = null) {
		$hydrator = new Hydrator\ClassMethods();
		$hydrator->hydrate($options, $this);
	}

	/**
	 * 
	 * @return int
	 */
	public function getId() {
		return $this->id;
	}

	/**
	 * 
	 * @param int $id
	 * @return \Entity\User
	 */
	public function setId($id) {
		$this->id = $id;
		return $this;
	}

	/**
	 * 
	 * @return \DateTime
	 */
	public function getDeleteDate() {
		return $this->deleteDate;
	}

	/**
	 * @param \DateTime|null $deleteDate
	 * @return \Entity\User
	 */
	public function setDeleteDate($deleteDate = null) {
		$this->deleteDate = $deleteDate;
		return $this;
	}

	/**
	 * 
	 * @return array
	 */
	public function toArray() {
		$result = array(
			'id' => $this->getId(),
			'delete_date' => $this->getDeleteDate()
		);

		return $result;
	}

}
FieldWorker.php
<?php

namespace Entity;

use Doctrine\ORM\Mapping as ORM;
use \Doctrine\Common\Collections\Criteria;

use Zend\Stdlib\Hydrator;

/**
 * Description of FieldWorker
 *
 * @ORM\Entity(repositoryClass="Entity\FieldWorkerRepository")
 * @ORM\Table(name="field_worker")
 * @ORM\HasLifecycleCallbacks
 * @author domanski
 */
class FieldWorker {
	
	/**
	 * This attribute must exist so the inverse join with any other entity can work
	 * 
	 * @ORM\Id
	 * @ORM\Column(type="integer", name="user_id")
	 * @var string
	 */
	protected $id;
	
	/**
	 * 
	 * @ORM\OneToOne(targetEntity="Entity\User")
	 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
	 * @var \Entity\User
	 */
	protected $user;
	
	public function __construct($options = array()) {		
		if(!empty($options))
			$this->hydrate($options);
	}
	
	public function hydrate(array $options = array(), \Doctrine\ORM\EntityManager $em = null) {
		if(!empty($em)) {
			// user
			if(isset($options['user']))
				$options['user'] = $em->getReference('Entity\User', $options['user']);
			else if(isset($options['user_id']))
				$options['user'] = $em->getReference('Entity\User', $options['user_id']);
						
		}
		
		$hydrator = new Hydrator\ClassMethods();
		$hydrator->hydrate($options, $this);
	}

	/**
	 * 
	 * @return \Entity\User
	 */
	public function getUser() {
		return $this->user;
	}

	/**
	 * 
	 * @param \Entity\User $user
	 * @return \Entity\FieldWorker
	 */
	public function setUser(\Entity\User $user) {
		$this->user = $user;
		$this->id = $user->getId();
		return $this;
	}
	
	/**
	 * 
	 * @return array
	 */
	public function toArray() {
		return $this->getUser()->toArray();
	}
}
Stage.php
<?php

namespace Obra\Entity;

use Doctrine\ORM\Mapping as ORM;
use Zend\Stdlib\Hydrator;
use Doctrine\Common\Collections\Criteria;

/**
 * Description of Stage
 *
 * @ORM\Table(name="stage")
 * @ORM\Entity(repositoryClass="Entity\StageRepository")
 * @ORM\HasLifecycleCallbacks
 * @author domanski
 */
class Stage {

	/**
	 *
	 * @ORM\Id
	 * @ORM\Column(name="id", type="integer", nullable=false)
	 * @ORM\GeneratedValue(strategy="AUTO")
	 * @var integer
	 */
	private $id;

	/**
	 * @ORM\ManyToMany(targetEntity="Entity\FieldWorker")
	 * @ORM\JoinTable(name="stage_field_worker",
	 * 		joinColumns={@ORM\JoinColumn(name="stage_id", referencedColumnName="id")},
	 * 		inverseJoinColumns={@ORM\JoinColumn(name="field_worker_id", referencedColumnName="user_id")}
	 * 	)
	 * @var \Doctrine\Common\Collections\ArrayCollection
	 */
	private $fieldWorkers;

	public function __construct(array $options = array()) {
		$this->fieldWorkers = new \Doctrine\Common\Collections\ArrayCollection();

		if (!empty($options))
			$this->hydrate($options);
	}

	public function hydrate(array $options = array(), \Doctrine\ORM\EntityManager $em = null) {
		$hydrator = new Hydrator\ClassMethods();
		$hydrator->hydrate($options, $this);
	}

	/**
	 * 
	 * @return int
	 */
	public function getId() {
		return $this->id;
	}

	/**
	 * 
	 * @param int $id
	 * @return \Entity\Stage
	 */
	public function setId($id) {
		$this->id = $id;
		return $this;
	}

	/**
	 * 
	 * @return \Doctrine\Common\Collections\ArrayCollection
	 */
	public function getFieldWorkers() {
		$criteria = Criteria::create()
				->where(Criteria::expr()->isNull("user.deleteDate"))
				->orderBy(array("user.name" => Criteria::ASC));

		return $this->fieldWorkers->matching($criteria);
	}

	/**
	 * 
	 * @return \Entity\Stage
	 */
	public function clearFieldWorkers() {
		$this->fieldWorkers->clear();
		return $this;
	}

	/**
	 * 
	 * @param \Entity\FieldWorker $fieldWorker
	 * @return \Entity\Stage
	 */
	public function addFiscal(\Entity\FieldWorker $fieldWorker) {
		$this->fieldWorkers->add($fieldWorker);
		return $this;
	}

	/**
	 * 
	 * @return array
	 */
	public function toArray() {
		$result = array(
			"id" => $this->getId(),
			"field_workers" => array()
		);

		foreach ($this->getFieldWorkers() as $fieldWorker) {
			$result['field_workers'][] = $fieldWorker->toArray();
		}

		return $result;
	}
}

The problem is that whenever the Stage::getFieldWorkers() method is invoked, the list of associated field workers is returned correctly, however if I try to retrieve the respective user of any field worker, it is NULL.

For example:

$stageEntity = $em->getRerefence('Entity\Stage', 1);
$fieldWorkers = $stageEntity->getFieldWorkers();

foreach($fieldWorkers as $fieldWorker) {
   $userId = $fieldWorker->getUser()->getId(); // This throws an error message saying that the result of getUser() is NULL
}

Another example would be if I try to call $stageEntity->toArray() (because it does the same thing as show above).

The database tables are as following:

Database tables creation
CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `delete_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `field_worker` (
  `user_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`user_id`),
  CONSTRAINT `fk_field_worker_user1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `stage` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `stage_field_worker` (
  `stage_id` int(10) unsigned NOT NULL,
  `field_worker_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`stage_id`,`field_worker_id`),
  KEY `fk_stage_field_worker_stage_idx` (`stage_id`),
  KEY `fk_stage_field_worker_field_worker_idx` (`field_worker_id`),
  CONSTRAINT `fk_stage_field_worker_field_worker` FOREIGN KEY (`field_worker_id`) REFERENCES `field_worker` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_stage_field_worker_stage` FOREIGN KEY (`stage_id`) REFERENCES `stage` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user` SET `id` = 1;
INSERT INTO `field_worker` SET `user_id` = 1;
INSERT INTO `stage` SET `id` = 1;
INSERT INTO `stage_field_worker` SET `stage_id` = 1, `field_worker_id` = 1;

After hours trying to understand what was wrong, I decided to make a test and modify the Stage::getFieldWorkers() method by removing the filtering Criteria:

public function getFieldWorkers() {
    return $this->fieldWorkers;
}

By simply doing that, the getUser() method started to return an entity of type User (as expected).

I am not a Doctrine ORM expert, but there seems to be something wrong with ArrayCollection filtering (matching() method)



 Comments   
Comment by Diogo Domanski de Souza [ 30/Jul/14 ]

The same problem occurs with QueryBuilder. For example:

StageRepository.php
<?php

namespace Entity;

use Doctrine\ORM\EntityRepository;

/**
 * Description of StageRepository
 *
 * @author domanski
 */
class StageRepository extends EntityRepository 
{

	public function findByFieldWorkerId($fieldWorkerId) {
		$qb = $this->getEntityManager()->createQueryBuilder()
			->select('s')
			->from('Entity\Stage', 's')
			->innerJoin('s.fieldWorkers', 'f')
			->innerJoin('f.user', 'u', \Doctrine\ORM\Query\Expr\Join::WITH, "u.deleteDate IS NULL AND u.id = :field_worker_id")
				->setParameter('field_worker_id', $fieldWorkerId);

		return $qb->getQuery()->getResult();
	 }
}

If I try to iterate over the result of StageRepository::findByFieldWorkerId() and call toArray() of any element, I get the same error. I've even tried to remove the inner join with User entity from query:

	public function findByFieldWorkerId($fieldWorkerId) {
		$qb = $this->getEntityManager()->createQueryBuilder()
			->select('s')
			->from('Entity\Stage', 's')
			->innerJoin('s.fieldWorkers', 'f');

		return $qb->getQuery()->getResult();
	 }
Comment by Marco Pivetta [ 30/Jul/14 ]

I suspect that something is wrong in your mappings then...

Comment by Diogo Domanski de Souza [ 31/Jul/14 ]

Hi Marco,

The mappings are shown above. The only thing I did was to omit some entities/tables properties/columns that don't have to see with the associations - except the field worker (table and Entity) and stage_field_worker table, that are fully presented.

Maybe is important to notice that the field_worker table has only one column (user_id) that is primary key and foreign key (referencing user.id).

Thanks for you support

Comment by Marco Pivetta [ 31/Jul/14 ]

Diogo Domanski de Souza we can't debug this as it is. We'd need a functional test case to be added to https://github.com/doctrine/doctrine2/tree/0650bb954f5e8d05776f99abd04c81948413299f/tests/Doctrine/Tests/ORM/Functional/Ticket first, in order to see the failure

Comment by Diogo Domanski de Souza [ 31/Jul/14 ]

Marco Pivetta is there any documentation (tutorial, instructions, guide, etc) that I can use to learn how to write the functional test cases that I need?

Comment by Marco Pivetta [ 31/Jul/14 ]

Diogo Domanski de Souza you need to look at the existing ones.

For running the test suite:

git clone git@github.com:doctrine/doctrine2.git
cd doctrine2 
curl -s https://getcomposer.org/installer | php --
./composer.phar install
./vendor/bin/phpunit
Comment by Diogo Domanski de Souza [ 20/Aug/14 ]

I solved the problem by removing the property $id from FieldWorker entity and add annotation @ORM\Id to $user property (in this same entity).

I didn't understand why the previous definition of FieldWorker entity was not working. I have another similar relationship scenario, between 3 different entities, and the error does not occur.

Thanks to all for the support





[DDC-3183] Add JsonSerializable to Collections Created: 22/Jun/14  Updated: 22/Jun/14

Status: Open
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 3.0
Fix Version/s: 3.0
Security Level: All

Type: New Feature Priority: Major
Reporter: Gabriel Bull Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: collection, orm


 Description   

Implement this:

http://www.php.net/manual/fr/class.jsonserializable.php

Can't really make this claim if Doctrine is not implementing basic interfaces for collections:

> The missing (SPL) Collection/Array/OrderedMap interface.






[DDC-2826] Add support for mapping collections of embeddable objects Created: 28/Nov/13  Updated: 08/Feb/14

Status: Open
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: None
Fix Version/s: None
Security Level: All

Type: New Feature Priority: Major
Reporter: songoko songowan Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 1
Labels: collection, orm, value-objects


 Description   

In Hibernate we can do something like this:

    @Entity
    public class User {
       [...]
       public String getLastname() { ...}
    
       @ElementCollection
       @CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
       @AttributeOverrides({
          @AttributeOverride(name="street1", column=@Column(name="fld_street"))
       })
       public Set<Address> getAddresses() { ... } 
    }
    
    @Embeddable
    public class Address {
       public String getStreet1() {...}
       [...]
    }

Basically a collection of value objects is mapped to a new table. Currently Doctrine2 is on its way to support value objects

However, this implementation won't support mapping a collection of objects to a new table and the only way to circumvent this issue is to treat the address an an entity and use an one-to-many unidirectional relationship through a many-to-many join table



 Comments   
Comment by Doctrine Bot [ 08/Feb/14 ]

A related Github Pull-Request [GH-835] was closed:
https://github.com/doctrine/doctrine2/pull/835





[DDC-2552] [GH-722] Support for order by association (for 2.3 branch) Created: 15/Jul/13  Updated: 15/Jul/13  Resolved: 15/Jul/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.3
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Doctrine Bot Assignee: Benjamin Eberlei
Resolution: Invalid Votes: 0
Labels: collection, orm
Environment:

Symfony 2.3



 Description   

This issue is created automatically through a Github pull request on behalf of naitsirch:

Url: https://github.com/doctrine/doctrine2/pull/722

Message:

I need to `@OrderBy` an association, which does not work in `2.3` but I found the pull request https://github.com/doctrine/doctrine2/pull/549 by @FabioBatSilva who has implemented this in branch `2.4`.

I have copied the code, adjusted it for `2.3` and tested it in a real life application

The original pull request is related to Doctrine Issue #2241(http://www.doctrine-project.org/jira/browse/DDC-2241).
The ordering of associations has been implemented in Doctrine Issue #195(http://www.doctrine-project.org/jira/browse/DDC-195).

Please integrate this into `2.3` because `2.4` is not stable until now and thus it is not usable for many people.



 Comments   
Comment by Doctrine Bot [ 15/Jul/13 ]

A related Github Pull-Request [GH-722] was closed:
https://github.com/doctrine/doctrine2/pull/722

Comment by Christian S. [ 15/Jul/13 ]

Can be closed (see https://github.com/doctrine/doctrine2/pull/722#issuecomment-20960237)





[DDC-2303] @param wrong in Doctrine\ORM\PersistentCollection::__constructor Edit Created: 18/Feb/13  Updated: 26/Feb/13  Resolved: 26/Feb/13

Status: Closed
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: None
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Torsten Granek Assignee: Marco Pivetta
Resolution: Invalid Votes: 0
Labels: collection

Attachments: JPEG File screenshot-1.jpg    

 Description   

When i try to generate a new PersistentCollection like this:
###############################################
$collection = new ArrayCollection();
new \Doctrine\ORM\PersistentCollection(
$this->getEntityManager(),
new ClassMetadata(''),
$collection
);
###############################################
i get an typ hinting error like
"Expected array, got "Doctrine\Common\Collections\ArrayCollection"

This could be fixed by changing the type hinting for the Doctrine\ORM\PersistentCollection::__constructor
_From:_
###############################################
/**

  • Creates a new persistent collection.
    *
  • @param EntityManager $em The EntityManager the collection will be associated with.
  • @param ClassMetadata $class The class descriptor of the entity type of this collection.
  • @param array $coll The collection elements.
    */
    public function __construct(EntityManager $em, $class, $coll)
    {
                                                                                              1. _To:_
                                                                                                ###############################################
                                                                                                /**

  • Creates a new persistent collection.
    *
  • @param EntityManager $em The EntityManager the collection will be associated with.
  • @param ClassMetadata $class The class descriptor of the entity type of this collection.
  • @param \ArrayAccess $coll The collection elements.
    */
    public function __construct(EntityManager $em, $class, $coll)
    {
    ###############################################


 Comments   
Comment by Torsten Granek [ 18/Feb/13 ]

When i try to generate a new PersistentCollection like this:


     $collection = new ArrayCollection();
     new \Doctrine\ORM\PersistentCollection(
				$this->getEntityManager(),
				new ClassMetadata(''),
				 $collection
			);

I get an typ hinting error like
"Expected array, got "Doctrine\Common\Collections\ArrayCollection"

This could be fixed by changing the type hinting for the Doctrine\ORM\PersistentCollection::__constructor
_From:_

     /**
     * Creates a new persistent collection.
     *
     * @param EntityManager $em    The EntityManager the collection will be associated with.
     * @param ClassMetadata $class The class descriptor of the entity type of this collection.
     * @param array       $coll  The collection elements.
     */
    public function __construct(EntityManager $em, $class, $coll)
    {

_To:_

     /**
     * Creates a new persistent collection.
     *
     * @param EntityManager $em    The EntityManager the collection will be associated with.
     * @param ClassMetadata $class The class descriptor of the entity type of this collection.
     * @param \ArrayAccess $coll  The collection elements.
     */
    public function __construct(EntityManager $em, $class, $coll)
    {

Comment by Christophe Coevoet [ 18/Feb/13 ]

There is no typehint in the PersistentCollection constructor. So the issue cannot come from this place (the phpdoc is wrong btw, it expects a Collection, not an array)

Please give the full error, i.e. the message and the location so that we can know where it happens.

Comment by Torsten Granek [ 20/Feb/13 ]

There error is not in the function declaration, it is in the @param in the doc block of the constructor.

Using PHPStorm as IDE i got this error thrown by the IDE it self, not php. (Screenshot will be attached)

Using ZF2 the error is on line 121 at:
vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php

Comment by Torsten Granek [ 20/Feb/13 ]

Using PHPStorm as IDE i got "Expected array, got "Doctrine\Common\Collections\ArrayCollection" thrown by the IDE it self, not php.

Comment by Torsten Granek [ 20/Feb/13 ]

Using PHPStorm as IDE i got "Expected array, got "Doctrine\Common\Collections\ArrayCollection" thrown by the IDE it self, not php.

Comment by Marco Pivetta [ 26/Feb/13 ]

The correct type hint here is `Doctrine\Common\Collections\Collection`.

I'm closing this as invalid, since you shouldn't instantiate a persistent collection on your own. Consider opening a pull request at https://github.com/doctrine/doctrine2 instead if you want to fix the typehint.





[DDC-2255] [Doctrine-Bridge][Console] Entity, Getters and Setters Generating Bug detected in Symfony 2 Framework Created: 24/Jan/13  Updated: 24/Jan/13  Resolved: 24/Jan/13

Status: Closed
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: None
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Ala Eddine Khefifi Assignee: Marco Pivetta
Resolution: Invalid Votes: 0
Labels: collection


 Description   

Bug found when generation the Entity, Getters and Setters by using the Command:

> php app/console doctrine:generate:entities YourBundleBundle:YourEntity 

In the situation you have a OneToMany relation in the Entity and you did implement the __construct(), then the Console Wont generate the ArrayCollection() !
In the case you did not implement the __construct(), then everything will goes fine when generating them,
Example:

/**
 * @ORM\OneToMany(targetEntity=" YourBundleBundle \Entity\ YourEntity ", mappedBy=" YourEntity ")
 */
private $YourAttribut;

public function __construct()
{
  $this-> YourAttribut = new \Doctrine\Common\Collections\ArrayCollection();
} 
// But in the case you did implement the __construct() before using the Command, let say like this:

public function __construct()
{
  $this-> YourOtherAttribut = a_value;
} 

In this case, when using the Command to generate Entity, Getters and Setters, the Console Wont generate the ArrayCollection() of the OneToMany relations in the __construct() !



 Comments   
Comment by Marco Pivetta [ 24/Jan/13 ]

Ala Eddine Khefifi This is expected behavior, since the generator should not change already existing methods

Comment by Ala Eddine Khefifi [ 24/Jan/13 ]

but it could override them and add missing instruction that should be added within the code, otherwise it leads to a dis-function and non stable relations !!

Comment by Marco Pivetta [ 24/Jan/13 ]

No, that is not up to the generator. Entity generation and fixing your broken existing code are different things. You should not rely on the generator to handle this kind of problems, the generator just gives you a kick-start, but after that, you are on your own.





[DDC-2220] Add joins to Collection Filtering API Created: 03/Jan/13  Updated: 11/Sep/13  Resolved: 11/Sep/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: 2.3.1
Fix Version/s: None
Security Level: All

Type: Improvement Priority: Major
Reporter: Oleg Namaka Assignee: Benjamin Eberlei
Resolution: Won't Fix Votes: 2
Labels: api, collection, filtering


 Description   

The recently added collection filtering API only goes half way in achieving a full fledged solution to filter huge collections. It still lacks joins. Look at the next two snippets:

    $criteria = Criteria::create()
        ->where(Criteria::expr()->eq('storeId', $store->getId()))
        ->andWhere(Criteria::expr()->eq('Category', 20))
        ->orderBy(array('popularity' => 'DESC'));
    return $this->BrandCategories->matching($criteria);

This piece of code works but what if there is a need to filter the BrandCategories collection by Categories with some extra criteria:

    $criteria = Criteria::create()
        ->where(Criteria::expr()->eq('storeId', $store->getId()))
        ->andWhere(Criteria::expr()->eq('Category', 20))
        ->andWhere(Criteria::expr()->eq('Category.name', 'Electronics'))
        ->orderBy(array('popularity' => 'DESC'));
    return $this->BrandCategories->matching($criteria);

That would not work.

Ideally we should have a possibility to join other entities, the Category entity in our case here:

    $criteria = Criteria::create()
        ->where(Criteria::expr()->eq('storeId', $store->getId()))
        ->andWhere(Criteria::expr()->eq('Category', 20))
        ->innerJoin(Criteria::expr()->field('Category', 'Category'))
        ->andWhere(Criteria::expr()->eq('Category.name', 'Electronics'))
        ->orderBy(array('popularity' => 'DESC'));
    return $this->BrandCategories->matching($criteria);

What do you think about it, does it make sense to add such functionality?



 Comments   
Comment by Benjamin Eberlei [ 11/Sep/13 ]

This is not a good idea, because the API has to be small to allow many different implementations, for example the in memory implementation on ArrayCollection, or the implementaiton on MongoDB ODM.





[DDC-2217] Return a lazy collection from PersistentCollection::match($criteria) Created: 31/Dec/12  Updated: 16/May/14  Resolved: 16/May/14

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: None
Fix Version/s: 2.5
Security Level: All

Type: Improvement Priority: Major
Reporter: Christophe Coevoet Assignee: Guilherme Blanco
Resolution: Fixed Votes: 8
Labels: collection


 Description   

In 2.3, PersistentCollection::match() has been implemented by doing the query directly. But sometimes, the only meaningful information about the matched collection would be its length. In this case, it would be great to handle it in the same way than extra lazy collections are handled: the matched collection would be initialized lazily, and could do the count in an extra lazy way (if the original collection was extra lazy).
This would of course not change anything in the case where the original collection was already initialized.



 Comments   
Comment by Maciej Klemarczyk [ 20/Sep/13 ]

It will be very usefull, for example to compute count of rows.

In 300 rows, you do not want fetch data from database to compute length of this.

Function matching(Criteria $criteria) fetch too many data for just compute count of rows.

Comment by Christophe Coevoet [ 21/Sep/13 ]

This is exactly the use case I add in mind actually

Comment by Michaël Gallego [ 12/Oct/13 ]

+1 on this one! This is absolutely needed when we use the Selectable API as the abstraction for some libraries.





[DDC-2185] Better explain DQL "WITH" and implications for the collection filtering API Created: 04/Dec/12  Updated: 17/Dec/12

Status: Open
Project: Doctrine 2 - ORM
Component/s: Documentation, DQL
Affects Version/s: 2.2
Fix Version/s: None
Security Level: All

Type: Documentation Priority: Major
Reporter: Matthias Pigulla Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: collection, documentation, dql, filtering


 Description   

Available documentation is a bit thin regarding the "WITH" clause on JOIN expressions. Only a single example is provided in

http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html#dql-select-examples

WITH seems to allow to only "partially" load a collection, so the collection in memory does not fully represent the associations available in the database.

The resulting collection is marked as "initialized" and it seems there is no way to tell later on whether/how (with which expression) the collection has been initialized.

When using the collection filtering API, the "initialized" flag on the collection will lead to in-memory processing. If a collection has been loaded WITH a restricting clause and another filter is applied later, results may not be what one might expect.

I assume this is by design (no idea how the collection could be "partially" loaded and behave correctly under all conditions), so filing it as a documentation issue.



 Comments   
Comment by Matthias Pigulla [ 17/Dec/12 ]

An additional observation:

If you eager-load a collection using WITH, for the resulting entities that collection is marked as initialized as described above.

Should you happen to come across the same entity during hydration in another (later) context where you explicitly eager load the same association without the WITH restriction (or with another one), the collection on that (existing) entity won't be re-initialized and still contains the associated objects found during the first query.





Generated at Thu Oct 23 03:55:36 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.