[DDC-1268] generate:entities creates bad method names with OneToMany Created: 11/Jul/11  Updated: 04/Dec/12  Resolved: 12/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.1
Fix Version/s: 2.1.1
Security Level: All

Type: Bug Priority: Minor
Reporter: Alessandro Desantis Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Ubuntu 11.04, PHP 5.3.5, Symfony 2.0 RC4



 Description   

Hi. I noticed a minor bug in the generate:entities console task. I've got a Region entity that had the following property:

/**
 * @ORM\OneToMany(targetEntity="District", mappedBy="region")
 */
private $districts;

public function __construct()
{
    $this->districts = new ArrayCollection();
}

When I ran the generate:entities task it created the following methods:

/**
 * Add districts
 *
 * @param Fenix\StudyBundle\Entity\District $districts
 */
public function addDistricts(\Fenix\StudyBundle\Entity\District $districts)
{
    $this->districts[] = $districts;
}

/**
 * Get districts
 *
 * @return Doctrine\Common\Collections\Collection 
 */
public function getDistricts()
{
    return $this->districts;
}

The second method's name is correct, but what about addDistricts? Since it allows me to add a single District entity it should be as follows:

/**
 * Add district
 *
 * @param Fenix\StudyBundle\Entity\District $district
 */
public function addDistrict(\Fenix\StudyBundle\Entity\District $district)
{
    $this->districts[] = $district;
}

In other words, the term after "add" (as well as the parameter's name) should be the entity's name, not the property's one.



 Comments   
Comment by Benjamin Eberlei [ 11/Jul/11 ]

The problem is that making singular out of a word is not an easy task and i will not try to make this work somehow. The Entity Generator result is a helper, not necessarily generating the final result of a class. This is one case where the Code Generation will not nmake you happy, so just change it.

Comment by Alessandro Desantis [ 11/Jul/11 ]

Couldn't you just use the target Entity's name (and not the property's one) in the "add" method?

Comment by Benjamin Eberlei [ 11/Jul/11 ]

The shortname you mean? Yeah that would be an option you are right, i reopen the ticket

Comment by Benjamin Eberlei [ 12/Jul/11 ]

Fixed.

This is sort of a BC break for 2.1, however since this is just a tool and generating code i think its acceptable

Comment by Alessandro Desantis [ 12/Jul/11 ]

Thanks a lot

Comment by Matthew Larson [ 23/Jul/11 ]

Another option for a semantic method name would be to use 'addTo' as the prefix, as in 'addToDistricts'.

The problem with using the target Entity's name (instead of the property's) is that sometimes you need to have multiple properties that map to the same target Entity.





[DDC-1098] Cascading delete is broken for all relationships other than inheritance relationships, at least on MySQL 5.0 Created: 04/Apr/11  Updated: 27/Sep/11  Resolved: 27/Sep/11

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

Type: Bug Priority: Critical
Reporter: Daniel Alvarez Arribas Assignee: Benjamin Eberlei
Resolution: Invalid Votes: 0
Labels: None
Environment:

Debian Linux 6.0, MySQL 5.0.51a



 Description   

The console tools do not generate the proper DDL statements for the constraints declared as part of the relationship annotations.

Take this entity field declaration, e.g. :

     /**
  * @OneToMany(targetEntity="\persistentData\model\core\invoiceCreator\AnalogOrderInvoiceLineItem", mappedBy="partialInvoice", cascade={"persist", "remove", "detach"})
  */

 protected $analogOrderInvoiceLineItems;
 

This declaration should generate some sort of ON DELETE CASCADE constraint, or at least actually cascade the delete, in whatever way. It does not. I reviewed the generated DDL statements with

doctrine orm:schema-tool:create --dump-sql

And it clearly showed that no "ON DELETE CASCADE" was generated. Neither did Doctrine 2 perform a cascading delete otherwise.

The delete simply does not cascade.

Subsequently, I end up getting errors like:

Exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails
(`invoiceCreatorDB/AnalogOrderInvoiceLineItem`, CONSTRAINT `AnalogOrderInvoiceLineItem_ibfk_1` FOREIGN KEY (`partialInvoice_dbID`) REFERENCES `PartialInvoice` (`dbID`))'

The query that caused the error is:

DELETE FROM PartialInvoice where dbID = '2';

(PartialInvoice is the entity containing the above field declaration).

It seems that the cascading delete feature is fundamentally broken.



 Comments   
Comment by Benjamin Eberlei [ 04/Apr/11 ]

This is expected, cascade remove is working with in memory instances of the to be deleted objects. This way lifecycle events are triggered.

If you want to perform this operation on the databse level you have to set onDelete="CASCADE" as an option of the join column:

http://www.doctrine-project.org/docs/orm/2.0/en/reference/annotations-reference.html#annref-joincolumn

This difference should be explained in the docs on Working with Associations, 8.6 Transitive persistence / Cascade Operations i guess.

Comment by Daniel Alvarez Arribas [ 05/Apr/11 ]

Good idea.

I believe that it is normal to expect that declared cascade behaviours refer to persistent state, and not only to in-memory object graphs.

That declared cascade behaviours are only applicable to objects in main memory is a fundamental limitation that

1) should be prominently documented, whereas, as of now, the information it is not visible at all

2) is IMO a highly questionable design in the first place, considering the principal focus of a ORM-solution as an interface to access persistent data

The current documentation states:

"Persisting, removing, detaching and merging individual entities can become pretty cumbersome, especially when a large object graph with collections is involved. Therefore Doctrine 2 provides a mechanism for transitive persistence through cascading of these operations. Each association to another entity or a collection of entities can be configured to automatically cascade certain operations. By default, no operations are cascaded.

The following cascade options exist:

persist : Cascades persist operations to the associated entities.
remove : Cascades remove operations to the associated entities.
merge : Cascades merge operations to the associated entities.
detach : Cascades detach operations to the associated entities.
all : Cascades persist, remove, merge and detach operations to associated entities."

How are users supposed to understand, that the "remove" option is limited to in-memory object graphs, if all the docs say is "Cascades remove operations to the associated entities.". From a user standpoint, the entities are associated, and even the more so being persistent, aren't they?

Comment by Daniel Alvarez Arribas [ 05/Apr/11 ]

Also, we obviously have a different understanding of the word "fixed".

Comment by Benjamin Eberlei [ 05/Apr/11 ]

Persist remove works, it fetches the association and deletes the objects, pulling them into memory.

As to the ticket, i wanted to mark it as invalid and must have missclicked.

Comment by Daniel Alvarez Arribas [ 05/Apr/11 ]

If it works, why is there an exception?

The situation was pretty isolated. The entity to be deleted was merge()d, and then remove()d. "remove" was declared as a cascade option.

Doctrine simply issued a "DELETE FROM \x where dbID = '1'", but did not remove the associated entity, instead violating the integrity constraint.

Comment by Daniel Alvarez Arribas [ 25/Sep/11 ]

I think we are referring to the same end result: After all, persistent state needs to be modified one way or the other. Otherwise it would not make any sense.

Still, the cascading delete does not work for me. It does not modify persistent state in any way.

E. g. I have a use case where the following association is declared within an entity class, say A.

/**

  • @OneToOne(targetEntity="B", inversedBy="a", cascade= {"persist", "remove", "detach", "merge"}

    )

  • @JoinColumn(name="b_dbID", referencedColumnName="dbID")
    */

protected $b;

Here, the cascade option "remove" is set.

Now, if I remove a persistent instance of this class, like so:

$doctrineEntityManager->merge($a);

$doctrineEntityManager->remove($a);

it will delete that particular entity instance from the database, but none of the entity instances associated with it through the association.

I made sure that also "merge" was activated for the association - just in case it matters to be able to pull the object graph into memory first.

Funnily enough, inheritance relationships declare a ON DELETE CASCADE at the database level. Just that all non-inheritance associations rely on that other in-memory mechanism, that, in the scenario above, does not work. I don't get it why the same mechanism cannot be used for both scenarios - after all its about deleting dependent database rows in both cases.

Am I doing it wrongly? How is this mechanism supposed to work? Could you please verify it does work? (For me, it does not, at least not as described above).

Thanks.

Comment by Daniel Alvarez Arribas [ 25/Sep/11 ]

Oh yes, I forgot to mention I am now using version 2.1.1 of Doctrine. The last post was tested using version 2.1.1.

Comment by Daniel Alvarez Arribas [ 25/Sep/11 ]

Updated the issue data to reflect the current environment.

Comment by Benjamin Eberlei [ 25/Sep/11 ]

Can you give an example domain for this bug? since it seems to have to do either with merge or inheritance.

following questions:

1. does it work for you without inheritance
2. does it work by grabbing an entity using find, then remove.

Comment by Daniel Alvarez Arribas [ 26/Sep/11 ]

Thanks for your quick reply.

I have now tested quite a number of different scenarios, both scenarios that involve inheritance, and scenarios that do not.

Inheritance was just the matter in the original issue because the initial observation was that, at the database level, only inheritance relationships would declare an ON DELETE CASCADE constraint. Inheritance does not really have an effect on the basic problem that delete operations are not cascaded.

As the application code I use involves a lot of inheritance, and since you explicitly mentioned it, I included inheritance in the tests, too, just to see if it made a difference. I performed the tests based on two different data models - first one where the target entity class of the association would inherit from another entity class, and a second one where it does not.

In the end, inheritance did not matter. The results were the same whether the target entity inherits from a base class or not.

What did matter is the way in which the delete was performed.

I have accidentally given you wrong information about the way I actually deleted the entity instances in my last post. Since calling the remove method is the regular way I delete objects in my application, I blindly assumed it would be the same in this scenario. Unfortunately, I overlooked a special case here. In that case, the delete is performed using a DQL DELETE query.

It seems that, for deletions performed using DQL queries, the cascade options are not respected. This is only logical, since - assuming that DQL gets compiled down straight to SQL and has no side effects on the in-memory objects - there will not be any in-memory object graph, and therefore any technique based purely on in-memory objects cannot possibly work for DQL.

A solution would be to declare ON DELETE CASCADE constraints at the database level too (as already done in case of inheritance relationships) for associations that declare "remove" as a cascade option, so that the SQL resulting from the compilation of DQL DELETE queries will have the expected semantics, and maybe the onDelete annotation would be mandatory here.

Here are the details of the tests I performed:

First, here are the entity models used for the test:

Here's the first, simple, model:

<?php

namespace persistentData\model;

/**
 * @Entity
 */

class Something {
   
   /**
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */
   
   public $dbID;
   
   
   /**
    * @OneToOne(targetEntity="persistentData\model\SomethingElse", inversedBy="thing", cascade={"persist", "remove", "detach", "merge"})
    * @JoinColumn(name="otherThing_dbID", referencedColumnName="dbID")
    */
   
   public $otherThing;
}


/**
 * @Entity
 */

class SomethingElse {
   
   /**
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */
   
   public $dbID;
   
   /**
    * @OneToOne(targetEntity="persistentData\model\Something", mappedBy="otherThing")
    */
   
   public $thing;
}

?>

There is an entity class "Something" which refers to an entity class "SomethingElse" with a bidirectional one-to-one association
The association declares a typical set of cascade options I use, including "remove". That's it.

Here is the second model, where the target entity class of the association inherits from another entitiy class.
Except for the inheritance, this second model is functionally identical to the first:

<?php

namespace persistentData\model;

/**
 * @Entity
 */

class Something {
   
   /**
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */
   
   public $dbID;
   
   
   /**
    * @OneToOne(targetEntity="persistentData\model\SomethingElse", inversedBy="thing", cascade={"persist", "remove", "detach", "merge"})
    * @JoinColumn(name="otherThing_dbID", referencedColumnName="dbID")
    */
   
   public $otherThing;
}


/**
 * @Entity
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="doctrineTypeDiscriminator", type="string", length=64)
 * @DiscriminatorMap({"baseClassForSomethingElse"   = "persistentData\model\BaseClassForSomethingElse",
 *                    "somethingElse"               = "persistentData\model\SomethingElse"})
 */

abstract class BaseClassForSomethingElse {
   
   /**
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */
   
   public $dbID;
}



/**
 * @Entity
 */

class SomethingElse extends BaseClassForSomethingElse {
   
   /**
    * @OneToOne(targetEntity="persistentData\model\Something", mappedBy="otherThing")
    */
   
   public $thing;
}

?>

I used the Doctrine command-line tools to generate the proxy classes and update the database model. Consequently, the following tables were generated:

For the simple model:

mysql> describe Something;
+-----------------+------------+------+-----+---------+----------------+
| Field           | Type       | Null | Key | Default | Extra          |
+-----------------+------------+------+-----+---------+----------------+
| dbID            | bigint(20) | NO   | PRI | NULL    | auto_increment |
| otherThing_dbID | bigint(20) | YES  | UNI | NULL    |                |
+-----------------+------------+------+-----+---------+----------------+


mysql> describe SomethingElse;
+-------+------------+------+-----+---------+----------------+
| Field | Type       | Null | Key | Default | Extra          |
+-------+------------+------+-----+---------+----------------+
| dbID  | bigint(20) | NO   | PRI | NULL    | auto_increment |
+-------+------------+------+-----+---------+----------------+

And for the model involving inheritance:

 
mysql> describe Something;
+-----------------+------------+------+-----+---------+----------------+
| Field           | Type       | Null | Key | Default | Extra          |
+-----------------+------------+------+-----+---------+----------------+
| dbID            | bigint(20) | NO   | PRI | NULL    | auto_increment |
| otherThing_dbID | bigint(20) | YES  | UNI | NULL    |                |
+-----------------+------------+------+-----+---------+----------------+


mysql> describe SomethingElse;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| dbID  | bigint(20) | NO   | PRI | NULL    |       |
+-------+------------+------+-----+---------+-------+


mysql> describe BaseClassForSomethingElse;
+---------------------------+-------------+------+-----+---------+----------------+
| Field                     | Type        | Null | Key | Default | Extra          |
+---------------------------+-------------+------+-----+---------+----------------+
| dbID                      | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| doctrineTypeDiscriminator | varchar(64) | NO   |     | NULL    |                |
+---------------------------+-------------+------+-----+---------+----------------+

Using doctrine, I then created some sample entity instances:

$something = new Something();


$somethingElse = new SomethingElse();

$doctrineEntityManager->persist($somethingElse);


$something->otherThing = $somethingElse;

$doctrineEntityManager->persist($something);

The database ends up with the following content:

For the simple model:

mysql> select * from Something;
+------+-----------------+
| dbID | otherThing_dbID |
+------+-----------------+
|    2 |               2 |
+------+-----------------+
1 row in set (0.00 sec)


mysql> select * from SomethingElse;
+------+
| dbID |
+------+
|    2 |
+------+
1 row in set (0.00 sec)

And for the model involving inheritance:

mysql> select * from Something;
+------+-----------------+
| dbID | otherThing_dbID |
+------+-----------------+
|    1 |               1 |
+------+-----------------+
1 row in set (0.00 sec)

mysql> select * from SomethingElse;
+------+
| dbID |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select * from BaseClassForSomethingElse;
+------+---------------------------+
| dbID | doctrineTypeDiscriminator |
+------+---------------------------+
|    1 | somethingElse             |
+------+---------------------------+
1 row in set (0.00 sec)

We now have an entity instance of class "Something", which refers to an entity instance of class "SomethingElse". The database with the model involving inheritance is identical, except for the implementation detail that the entity instance of class "SomethingElse" inherits from an entity instance of class "BaseClassForSomethingElse".

This is the setup. Now to the program code:

This code deletes both entity instances correctly, as expected, including the base class row in case of the second data model:

$query = $doctrineEntityManager->createQuery('SELECT something
                                                FROM \persistentData\model\Something something
                                               WHERE something.dbID = 2');

$something = $query->getSingleResult();

$doctrineEntityManager->remove($something);

This code will also work correctly, having the exact same effect as the code above:

$something = $doctrineEntityManager->find('persistentData\model\Something', 2);

$doctrineEntityManager->remove($something);

However, this code will not work:

$query = $doctrineEntityManager->createQuery('DELETE
                                                FROM \persistentData\model\Something something
                                               WHERE something.dbID = 2');

$something = $query->execute();

The code deletes the entity instance of class "Something", but will not delete the dependent data object of class "SomethingElse".

It seems like the cascade option for the cascading delete is completely ignored by any delete operations performed through DQL queries (as opposed to e. g. calling the remove method on the entity manager).

In the trivial example above, the query could simply be replaced by a call to the remove method, of course. Fortunately, in the application I am developing, this is possible, so I can resort to that.
But for queries involving more complex WHERE clauses, it will not be easily possible, except maybe by first performing a SELECT query to have the WHERE clause evaluated, and then iterating over the result object-by-object to delete them.

Is this behaviour (cascade options being bypassed by DQL queries) intended? At least it is logical according to your explanation of the in-memory object-graph approach.
It would be a pain to understand though, with no clear mention of this fundamental restriction being available in the docs.

However, I found a mention of an "onDelete" option in the "transitive persistence" section of the reference documentation

"[...] To rely on the database level cascade operations for the delete operation instead, you can configure each join column with the onDelete option. See the respective mapping driver chapters for more information."

Do I have to rely on that, even if cascade="remove" is declared?

The annotation reference explains the onDelete annotation as "onDelete: Cascade Action (Database-level)". However, this is misleading, because a cascade option is understood to be one of "persist"/"remove"/"detach"/"merge"/"all" throughout the annotation reference. Only the Doctrine 1.2 docs contain a mention of "database-level cascades" (http://www.doctrine-project.org/documentation/manual/1_1/ru/defining-models:transitive-persistence:database-level-cascades). Reading the source, I assume the syntax would be onDelete="cascade", but from the docs this is not evident.

Personally, I would opt for generally using ON DELETE CASCADE constraints at the database level - either exclusively, or in addition to anything that might happen in the object domain. This is IMO the only efficient way to make the declared cascade=

{"remove"}

constraint be generally effective.

Please clarify if the behaviour is intended, and if declaring onDelete="cascade" is mandatory to have DQL queries cascade delete operations.

Thanks.

Comment by Daniel Alvarez Arribas [ 26/Sep/11 ]

Wrapped the code in code blocks and the MySQL tables into noformat-blocks.

Comment by Benjamin Eberlei [ 27/Sep/11 ]

Ah ok, using DELETE makes all the difference. This is actually the exepcted behavior. Cascade Operations are purely in-memory operations and dont trigger on DELETE statements. That is waht the join column on-delete definitions are for.

This is documented in the DQL chapter, but should probably be documented in the cascade section aswell: http://www.doctrine-project.org/docs/orm/2.0/en/reference/dql-doctrine-query-language.html#delete-queries

Comment by Daniel Alvarez Arribas [ 27/Sep/11 ]

Thanks for the clarification. Doctrine 2 really is one complex beast to tame.

Maybe a note in the architecture section could point out that anything involving DQL belongs to the database domain as opposed to the object domain. As DQL is toutet as an object-query-language it is quite contrary to the principle of least astonishment that cascade constraints defined at the object-domain-level are ignored. Bypassing object-domain constraints basically makes DQL a mere SQL abstraction rather than a fully-blown object-query-language aware of the object domain constraints. This is a very fundamental architectural tenet.

I will close this issue as invalid, because it does work as intended by the creators, even if the behaviour IMO constitutes a questionable design choice, being highly counter-intuitive without compensating for it by giving adequate other benefits to the user that would necessarily depend on that design choice.

Comment by Daniel Alvarez Arribas [ 27/Sep/11 ]

Works as intended, though maybe not as to be expected.





[DDC-1333] doctrine-mapping.xsd allows only one <id> element Created: 16/Aug/11  Updated: 21/Aug/11  Resolved: 21/Aug/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.1
Fix Version/s: 2.1.1

Type: Bug Priority: Minor
Reporter: Andrew McConnell Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

The metadata XSD at http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd defines the <id> element on line 66 as <xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />. This allows for no more than one <id> element though the documentation at http://www.doctrine-project.org/docs/orm/2.1/en/reference/xml-mapping.html#defining-identity-and-generator-strategies states that more than one <id> element is allowed.



 Comments   
Comment by Benjamin Eberlei [ 21/Aug/11 ]

Fixed





[DDC-1340] Missing argument 3 for OptimisticLockException::lockFailedVersionMissmatch() Created: 20/Aug/11  Updated: 21/Aug/11  Resolved: 21/Aug/11

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

Type: Bug Priority: Minor
Reporter: Matti Niemelä Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Doctrine\ORM::VERSION = 2.1.0RC3



 Description   

UnitOfWork.php line 1409

throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);

OptimisticLockException.php line 54

public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion)

No 3rd param in call or default in method declaration.

Warning: Missing argument 3 for Doctrine\ORM\OptimisticLockException::lockFailedVersionMissmatch(), called in /mnt/hgfs/Sites/library/Doctrine2/lib/Doctrine/ORM/UnitOfWork.php on line 1409 and defined in /mnt/hgfs/Sites/library/Doctrine2/lib/Doctrine/ORM/OptimisticLockException.php on line 54



 Comments   
Comment by Benjamin Eberlei [ 21/Aug/11 ]

Fixed





[DDC-1021] Automatically infer type when DateTime is used as parameter in queries Created: 08/Feb/11  Updated: 16/Aug/11  Resolved: 16/Aug/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: DQL
Affects Version/s: Git Master
Fix Version/s: 2.1.1
Security Level: All

Type: Improvement Priority: Major
Reporter: Karsten Dambekalns Assignee: Guilherme Blanco
Resolution: Fixed Votes: 1
Labels: None


 Description   

Given $date instanceof \DateTime, instead of this:

$this->entityManager->createQuery(' ... p.date >= :pdate')
  ->setParameters(array('pdate' => $date), array('pdate' => 'datetime'))
  ->execute()

I would love to be able to use this:

$this->entityManager->createQuery(' ... p.date >= :pdate')
  ->execute(array('pdate' => $date))

Even when using setParameters() - which you need to do, e.g. when using getSingleResult(), it would be nice to be able to leave that out for DateTime instances - seems to be about the only thing that is not handled transparently.



 Comments   
Comment by Guilherme Blanco [ 16/Aug/11 ]

This was already fixed by ParameterTypeInferer released in 2.1





[DDC-1300] Can't fetch entities from IdentityMap when using a foreign keys as identifiers Created: 27/Jul/11  Updated: 06/Aug/11  Resolved: 06/Aug/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.1
Fix Version/s: 2.1.1

Type: Bug Priority: Critical
Reporter: Van Hauwaert Bert Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

Given the follow SQL structure:

CREATE TABLE `foo` (
  `fooID` int(11) NOT NULL AUTO_INCREMENT,
  `fooReference` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`fooID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='module=bug';

CREATE TABLE `fooLocale` (
  `fooID` int(11) NOT NULL,
  `locale` varchar(5) NOT NULL DEFAULT '',
  `title` varchar(150) DEFAULT NULL,
  PRIMARY KEY (`fooID`,`locale`),
  KEY `fk_foo2` (`fooID`),
  CONSTRAINT `fk_table1_foo2` FOREIGN KEY (`fooID`) REFERENCES `foo` (`fooID`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module=bug';

INSERT INTO `foo` (`fooID`, `fooReference`)
VALUES
	(1, 'foo1');

INSERT INTO `fooLocale` (`fooID`, `locale`, `title`)
VALUES
	(1, 'en_GB', 'Foo title test');

and the following models:

use Doctrine\ORM\Mapping as ORM;

/**
 * Model for foo
 *
 * @category Application
 * @package Bug
 * @subpackage Model
 * @ORM\Table(name="foo")
 * @ORM\Entity
 */
class Bug_Model_Foo
{
	/**
	 * @var integer fooID
	 * @ORM\Column(name="fooID", type="integer", nullable=false)
	 * @ORM\GeneratedValue(strategy="IDENTITY")
	 * @ORM\Id
	 */
	protected $_fooID = null;

	/**
	 * @var string fooReference
	 * @ORM\Column(name="fooReference", type="string", nullable=true, length=45)
	 */
	protected $_fooReference = null;

	/**
	 * @ORM\OneToMany(targetEntity="Bug_Model_FooLocale", mappedBy="_foo",
	 * cascade={"persist"})
	 */
	protected $_fooLocaleRefFoo = null;

	/**
	 * Constructor
	 *
	 * @param array|Zend_Config|null $options
	 * @return Bug_Model_Foo
	 */
	public function __construct($options = null)
	{
		$this->_fooLocaleRefFoo = new \Doctrine\Common\Collections\ArrayCollection();
	}

}
use Doctrine\ORM\Mapping as ORM;

/**
 * Model for fooLocale
 *
 * @category Application
 * @package Bug
 * @subpackage Model
 * @ORM\Table(name="fooLocale")
 * @ORM\Entity
 */
class Bug_Model_FooLocale
{
	/**
	 * @ORM\ManyToOne(targetEntity="Bug_Model_Foo")
	 * @ORM\JoinColumn(name="fooID", referencedColumnName="fooID")
	 * @ORM\Id
	 */
	protected $_foo = null;

	/**
	 * @var string locale
	 * @ORM\Column(name="locale", type="string", nullable=false, length=5)
	 * @ORM\Id
	 */
	protected $_locale = null;

	/**
	 * @var string title
	 * @ORM\Column(name="title", type="string", nullable=true, length=150)
	 */
	protected $_title = null;

}

Execute the following DQL 2 times:

$query = $entityManager->createQuery('
			SELECT 
				f, fl 
			FROM 
				Bug_Model_Foo f 
			JOIN 
				f._fooLocaleRefFoo fl');
		return $query->getResult();

The first time, you won't have any issues.
The second time, when the data will be fetched from the identityMap, you won't find it because there is something wrong with the identities and you will get the following warning

( ! ) Notice: Undefined index: _foo in /var/www/vhost/core/htdocs/externals/Doctrine/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 217
Call Stack
#	Time	Memory	Function	Location
1	0.0002	337752	{main}( )	../index.php:0
2	0.0848	7268724	Zend_Application->run( )	../index.php:26
3	0.0848	7268724	Zend_Application_Bootstrap_Bootstrap->run( )	../Application.php:366
4	0.0850	7268796	Zend_Controller_Front->dispatch( )	../Bootstrap.php:97
5	0.0908	7900416	Zend_Controller_Dispatcher_Standard->dispatch( )	../Front.php:954
6	0.0967	8209564	Zend_Controller_Action->dispatch( )	../Standard.php:295
7	0.0967	8213912	Bug_IndexController->indexAction( )	../Action.php:513
8	0.1936	11391900	Bug_Service_Bug->getFooLocaleList( )	../IndexController.php:35
9	0.1936	11394088	Doctrine\ORM\AbstractQuery->getResult( )	../Bug.php:41
10	0.1936	11394296	Doctrine\ORM\AbstractQuery->execute( )	../AbstractQuery.php:392
11	0.1945	11397960	Doctrine\ORM\Internal\Hydration\AbstractHydrator->hydrateAll( )	../AbstractQuery.php:594
12	0.1946	11398656	Doctrine\ORM\Internal\Hydration\ObjectHydrator->_hydrateAll( )	../AbstractHydrator.php:99
13	0.1946	11399876	Doctrine\ORM\Internal\Hydration\ObjectHydrator->_hydrateRow( )	../ObjectHydrator.php:140
14	0.1949	11405584	Doctrine\ORM\Internal\Hydration\ObjectHydrator->_getEntityFromIdentityMap( )	../ObjectHydrator.php:326

With the invalid key, it is not possible to get the entity from the identityMap.



 Comments   
Comment by Benjamin Eberlei [ 06/Aug/11 ]

I could reproduce this, working on a fix.

Comment by Benjamin Eberlei [ 06/Aug/11 ]

Fixed





[DDC-1313] DriverChain::getAllClassNames() is very inefficient in some scenarios Created: 01/Aug/11  Updated: 01/Aug/11  Resolved: 01/Aug/11

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

Type: Bug Priority: Major
Reporter: Benjamin Eberlei Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

In Symfony a driver gets registered multiple times. This leads to getAllClassNames() to be invoked multiple times on the same driver.



 Comments   
Comment by Benjamin Eberlei [ 01/Aug/11 ]

Fixed





[DDC-1282] getDateAddDaysExpression() casts $days to integer preventing column based add expressions. Created: 20/Jul/11  Updated: 31/Jul/11  Resolved: 31/Jul/11

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

Type: Bug Priority: Major
Reporter: Kyle Spraggs Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

MySQL



 Description   

Take the following:

$qb = $this->createQueryBuilder('a');
$qb->select('partial a.

{dsid,rid,activeStartDate}');
$qb->leftJoin('a.clinic', 'c');
$qb->leftJoin('a.patient', 'p');
$qb->where('c.autoAppointmentReminders = 1');
$qb->andWhere('a.enabled = 1');
$qb->andWhere(
$qb->expr()->eq(
'a.activeStartDate',
"DATE_ADD(CURRENT_DATE(), c.appointmentRemindersDaysInAdvance, 'day')"));

Which generates the DQL: SELECT partial a.{dsid,rid,activeStartDate}

FROM VetLogic\Entity\Appointment a LEFT JOIN a.clinic c LEFT JOIN a.patient p WHERE c.autoAppointmentReminders = 1 AND a.enabled = 1 AND a.activeStartDate = DATE_ADD(CURRENT_DATE(), c.appointmentRemindersDaysInAdvance, 'day')

And SQL: SELECT a0_.rid AS rid0, a0_.dsid AS dsid1, a0_.activeStartDate AS activeStartDate2, a0_.dsid AS dsid3, a0_.dsid AS dsid4, a0_.clinicRid AS clinicRid5, a0_.dsid AS dsid6, a0_.patientRid AS patientRid7, a0_.dsid AS dsid8, a0_.rid AS rid9 FROM appointment a0_ LEFT JOIN clinic c1_ ON a0_.dsid = c1_.dsid AND a0_.clinicRid = c1_.rid LEFT JOIN patient p2_ ON a0_.dsid = p2_.dsid AND a0_.patientRid = p2_.rid WHERE c1_.autoAppointmentReminders = 1 AND a0_.enabled = 1 AND a0_.activeStartDate = DATE_ADD(CURRENT_DATE, INTERVAL0 DAY)

The DATE_ADD(CURRENT_DATE, INTERVAL0 DAY) is generated incorrectly because getDateAddDaysExpression($date, $days) casts $days to an integer.

The fix is simply to remove (int) from return 'DATE_ADD(' . $date . ', INTERVAL ' . (int)$days . ' DAY)';

Note: I only checked the MySQL platform so I'm not sure if other platforms are affected.



 Comments   
Comment by Benjamin Eberlei [ 31/Jul/11 ]

Fixed





[DDC-1302] orphan-removal does not work in XML Created: 29/Jul/11  Updated: 31/Jul/11  Resolved: 31/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: 2.1
Fix Version/s: 2.0.7, 2.1.1
Security Level: All

Type: Bug Priority: Blocker
Reporter: Benjamin Eberlei Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

The check for $oneToManyElement->

{'orphan-removal'}

returns false, the right check would be $oneToManyElement['orphan-removal']



 Comments   
Comment by Benjamin Eberlei [ 31/Jul/11 ]

Fixed and merged into 2.0.x and 2.1.x branches.





[DDC-1301] EXTRA_LAZY collection count throws exception on OneToMany association Created: 28/Jul/11  Updated: 28/Jul/11  Resolved: 28/Jul/11

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

Type: Bug Priority: Major
Reporter: Alexander Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

Calling count() on an EXTRA_LAZY collection throws an exception.

Notice: Undefined index: iUserId in /../vendor/doctrine/lib/Doctrine/ORM/Persisters/OneToManyPersister.php line 142

I've found that this is caused by looking up the fieldName for iUserId on the targetEntity, while it should be looked up at the sourceEntity. The current tests pass because all entities have a mapping of `id' -> $id.

Tests + fix coming up.



 Comments   
Comment by Alexander [ 28/Jul/11 ]

PR with tests and fix: https://github.com/doctrine/doctrine2/pull/97





[DDC-1275] ResultSetMappingBuilder does not set foreign keys Created: 13/Jul/11  Updated: 28/Jul/11  Resolved: 28/Jul/11

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

Type: Bug Priority: Major
Reporter: Benjamin Eberlei Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

The ResultSetMappingBuilder has a fatal flaw: Its not setting foreign keys as of right now.






[DDC-1298] SqlWalker->walkSelectClause imploding empty strings results in invalid query Created: 27/Jul/11  Updated: 28/Jul/11  Resolved: 28/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: DQL
Affects Version/s: 2.1
Fix Version/s: 2.1.1
Security Level: All

Type: Bug Priority: Critical
Reporter: Van Hauwaert Bert Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

Given the following MySQL structure

CREATE TABLE `bar` (
  `barID` int(11) NOT NULL AUTO_INCREMENT,
  `barReference` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`barID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='module=bug';

CREATE TABLE `foo` (
  `fooID` int(11) NOT NULL AUTO_INCREMENT,
  `fooReference` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`fooID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='module=bug';

CREATE TABLE `fooBar` (
  `fooID` int(11) NOT NULL,
  `barID` int(11) NOT NULL,
  PRIMARY KEY (`fooID`,`barID`),
  KEY `fk_foo1` (`fooID`),
  KEY `fk_bar1` (`barID`),
  CONSTRAINT `fk_table1_foo1` FOREIGN KEY (`fooID`) REFERENCES `foo` (`fooID`) ON DELETE CASCADE ON UPDATE NO ACTION,
  CONSTRAINT `fk_table1_bar1` FOREIGN KEY (`barID`) REFERENCES `bar` (`barID`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='module=bug';

INSERT INTO `bar` (`barID`, `barReference`)
VALUES
	(1, 'bar1'),
	(2, 'bar2');

INSERT INTO `foo` (`fooID`, `fooReference`)
VALUES
	(1, 'foo1');

INSERT INTO `fooBar` (`fooID`, `barID`)
VALUES
	(1, 1);

the following models:

use Doctrine\ORM\Mapping as ORM;

/**
 * Model for foo
 *
 * @category Application
 * @package Bug
 * @subpackage Model
 * @ORM\Table(name="foo")
 * @ORM\Entity
 */
class Bug_Model_Foo
{
	/**
	 * @var integer fooID
	 * @ORM\Column(name="fooID", type="integer", nullable=false)
	 * @ORM\GeneratedValue(strategy="IDENTITY")
	 * @ORM\Id
	 */
	protected $_fooID = null;

	/**
	 * @var string fooReference
	 * @ORM\Column(name="fooReference", type="string", nullable=true, length=45)
	 */
	protected $_fooReference = null;

	/**
	 * @ORM\OneToMany(targetEntity="Bug_Model_FooBar", mappedBy="_foo",
	 * cascade={"persist"})
	 */
	protected $_fooBarRefFoo = null;

	/**
	 * Constructor
	 *
	 * @param array|Zend_Config|null $options
	 * @return Bug_Model_Foo
	 */
	public function __construct($options = null)
	{
		$this->_fooBarRefFoo = new \Doctrine\Common\Collections\ArrayCollection();
		parent::__construct($options);
	}

}
use Doctrine\ORM\Mapping as ORM;

/**
 * Model for bar
 *
 * @category Application
 * @package Bug
 * @subpackage Model
 * @ORM\Table(name="bar")
 * @ORM\Entity
 */
class Bug_Model_Bar
{
	/**
	 * @var integer barID
	 * @ORM\Column(name="barID", type="integer", nullable=false)
	 * @ORM\GeneratedValue(strategy="IDENTITY")
	 * @ORM\Id
	 */
	protected $_barID = null;

	/**
	 * @var string barReference
	 * @ORM\Column(name="barReference", type="string", nullable=true, length=45)
	 */
	protected $_barReference = null;

	/**
	 * @ORM\OneToMany(targetEntity="Bug_Model_FooBar", mappedBy="_bar",
	 * cascade={"persist"})
	 */
	protected $_fooBarRefBar = null;

	/**
	 * Constructor
	 *
	 * @param array|Zend_Config|null $options
	 * @return Bug_Model_Bar
	 */
	public function __construct($options = null)
	{
		$this->_fooBarRefBar = new \Doctrine\Common\Collections\ArrayCollection();
		parent::__construct($options);
	}
}

use Doctrine\ORM\Mapping as ORM;

/**
 * Model for fooBar
 *
 * @category Application
 * @package Bug
 * @subpackage Model
 * @ORM\Table(name="fooBar")
 * @ORM\Entity
 */
class Bug_Model_FooBar
{
	/**
	 * @ORM\ManyToOne(targetEntity="Bug_Model_Foo")
	 * @ORM\JoinColumn(name="fooID", referencedColumnName="fooID")
	 * @ORM\Id
	 */
	protected $_foo = null;

	/**
	 * @ORM\ManyToOne(targetEntity="Bug_Model_Bar")
	 * @ORM\JoinColumn(name="barID", referencedColumnName="barID")
	 * @ORM\Id
	 */
	protected $_bar = null;

}

and using the following DQL:

SELECT 
	f, b, fb 
FROM 
	Bug_Model_Foo f 
JOIN 
	f._fooBarRefFoo fb
JOIN
	fb._bar b

will result in the following sql query:

SELECT f0_.fooID AS fooID0, f0_.fooReference AS fooReference1, , b1_.barID AS barID2, b1_.barReference AS barReference3, f2_.fooID AS fooID4, f2_.barID AS barID5 FROM foo f0_ INNER JOIN fooBar f2_ ON f0_.fooID = f2_.fooID INNER JOIN bar b1_ ON f2_.barID = b1_.barID

there are 2 comma's between "f0_.fooReference AS fooReference1" and "b1_.barID AS barID2" resulting in an invalid query.

The first line of the walkSelectClause function in Doctrine/ORM/Query/SqlWalker.php will implode the result of the array_map.
But you receive an empty result from processing the data for the selectExpression "fb" explaining why you got the extra comma.

This worked in a previous version when 2.1 was still in development.



 Comments   
Comment by Van Hauwaert Bert [ 27/Jul/11 ]

I have fixed it and made a pull request: https://github.com/doctrine/doctrine2/pull/96

Comment by Benjamin Eberlei [ 28/Jul/11 ]

Fixed





[DDC-1215] doctrine:generate:entities creates entities incorrect visibility of properties for subclasses Created: 19/Jun/11  Updated: 26/Jul/11  Resolved: 26/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.1
Fix Version/s: 2.1.1
Security Level: All

Type: Bug Priority: Minor
Reporter: james brown Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Fedora 14, PHP 5.3.5



 Description   

I've created a symfony bundle with an entity that I want to use as a base across mutiple other entities:

/**
* @orm\Entity
* @orm\HasLifecycleCallbacks()
*/
class Base
{
/**
* @orm\Id
* @orm\Column(type="integer")
* @orm\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @orm\Column(type="string", unique="true", length="150")
*/
protected $name;
/**
* @orm\Column(type="string", unique="true", length="150")
*/
protected $username;
...
}

I have another entity in another bundle that extends this:

class Extended extends \Test\Entity\Base

When I run doctrine:generate:entites it incorrectly generates the extended class - it sets all inherited properties as 'private' which then causes PHP to raise an exception because properties on an extended class have more restrictive visibility than from the base class.

It probably shouldn't be auto-generating the properties in the extended class at all, or generating getters/setters since that then overrides my base implementation by default.



 Comments   
Comment by Benjamin Eberlei [ 26/Jul/11 ]

Should be fixed





[DDC-1280] Generated proxy classes have mixed line endings on Windows Created: 17/Jul/11  Updated: 26/Jul/11  Resolved: 26/Jul/11

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

Type: Bug Priority: Minor
Reporter: Stan Imbt Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

WinXP SP3, PHP 5.3.6



 Description   

When Doctrine\ORM\Proxy\ProxyFactory generates proxy classes on Windows, it mixes LF an CR/LF line endings. This is causing VCS issues in our mixed Windows/Linux dev team (we keep the proxies under version control for deployment purposes).

This seems to be a regression. It did not happen with Doctrine 2.0.x.

It is caused by the template string in the static property $_proxyClassTemplate using LF, while the string concatenations in the methods use the PHP_EOL constant, which resolves to CR/LF on Windows.

Other code generators like Doctrine\ORM\Tools\EntityGenerator use "\n" instead of PHP_EOL.

I propose to always use "\n" in favour of PHP_EOL. It's easier to implement in this case and every editor/IDE on Windows can handle Unix line endings nowadays.



 Comments   
Comment by Benjamin Eberlei [ 26/Jul/11 ]

Fixed





[DDC-1276] Merging persistent collections broken if managed and merged collection are the same object Created: 13/Jul/11  Updated: 26/Jul/11  Resolved: 26/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.1
Fix Version/s: 2.0.7, 2.1.1
Security Level: All

Type: Bug Priority: Major
Reporter: Lienhart Woitok Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

FLOW3



 Description   

When FLOW3 maps request arguments to entities, it works on clones of the changed entities while entities, which are not affected by the request arguments stay the original objects. If the entity has a persistent collection which is not changed and therefore not cloned, the call to $repository->update() (which internally calls $em->merge()) eventually clears the $managedCol in $uow->doMerge() (line 1460 in current master), but if the $mergeCol is the same object, this is obviously wrong behavior as no related entities exist anymore in the entity to merge.

Made up example code (not sure if this simple example works as I have no test setup I could use for it)

/**
 * @entity
 * @scope prototype
 */
class A {}

/**
 * @entity
 * @scope prototype
 */
class B {
    /**
     * @var \Doctrine\Common\Collection\ArrayCollection<A>
     * @ManyToMany
     */
    public $relation;
}

$b = $repository->find(); // assume $b has some A in relation
$anotherB = clone $b;
$em->merge($anotherB);

After this relation of $b is empty



 Comments   
Comment by Benjamin Eberlei [ 26/Jul/11 ]

Fixed





[DDC-1254] EntityGenerator does not respect Class Inheritance properly Created: 06/Jul/11  Updated: 14/Jul/11  Resolved: 12/Jul/11

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

Type: Bug Priority: Critical
Reporter: Daniel Reiche Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Symfony2 RC4, PHP 5.3., Fedora 15


Issue Links:
Dependency
is required for DDC-1243 Problem to generate code for subclass... Resolved

 Description   

Hi,
as mentioned here DDC-904, https://github.com/symfony/symfony/issues/1550# and https://github.com/stof/StofDoctrineExtensionsBundle/issues/47
The Doctrine2 EntityGenerator does not respect inheritance of Models correctly, especially when inheriting the primary field from an abstract base class as done by Symfony2's StofDoctrineExtensionsBundle.

This is extremly annoying since it renders the generator unusable when aforementioned bundle is active. It always fails with this message:

[Doctrine\ORM\Mapping\MappingException]

No identifier/primary key specified for Entity 'Stof\DoctrineExtensionsBundle\Entity\Translation'. Every Entity must have an
identifier/primary key.

DDC-904 mentioned this should be fixed, but this is not true: bug is still found in latest Symfony2 git.
The docs only refer to using a single mapping format inside a bundle, but this does not work. Stof... uses xml mappings and it does not matter if i use yml or annotiations in a different bundle - the above error is still present.



 Comments   
Comment by venimus [ 11/Jul/11 ]

I already reported this issue, but it was not fixed http://www.doctrine-project.org/jira/browse/DDC-1177

Comment by Benjamin Eberlei [ 12/Jul/11 ]

Fixed

@venimius: You didn't say a word about EntityGenerator in your ticket, which was the critical information that helped me solve this issue.

Comment by Daniel Reiche [ 13/Jul/11 ]

THANKS!

Comment by venimus [ 14/Jul/11 ]

@Benajmin, sorry but wasn't sure where is the problem i thought it might be me, that is why i simply stated that it is a schema-tool problem.





[DDC-1244] [regression] Entities generator fails to update entities without any namespace, always append the generated definitions to the end of the class Created: 01/Jul/11  Updated: 12/Jul/11  Resolved: 12/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.0.4, 2.0.5, 2.0.6, 2.0.7, 2.1
Fix Version/s: 2.1.1
Security Level: All

Type: Bug Priority: Major
Reporter: venimus Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Zend_Framework 1.11, Doctrine 2.x, YAML mapping



 Description   

If Entities have no namespace set (e.g. custom AutoLoader) schema tool fails to find the original methods and properties and always append the definitions to the end of the file, which leads to duplications of class definitions.

This is because in \Doctrine\ORM\Tools\EntitiesGenerator.php at line 441:

441: $lastSeenClass = $lastSeenNamespace . '\\' . $token[1];

$lastSeenClass is always prefixed with "\" when checking for existing definitions

it could be fixed as:

441: $lastSeenClass =  $lastSeenNamespace. ($lastSeenNamespace ? '\\' : '') . $token[1];

I had an environment where the classes without any specified namespace are autoloaded from Entities directory, which makes generate-entities fail to work properly since Doctrine 2.0.4



 Comments   
Comment by Benjamin Eberlei [ 12/Jul/11 ]

Fixed





[DDC-1243] Problem to generate code for subclasses without identifiers. Created: 01/Jul/11  Updated: 12/Jul/11  Resolved: 12/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM, Tools
Affects Version/s: Git Master
Fix Version/s: 2.1.1
Security Level: All

Type: Bug Priority: Major
Reporter: Asbjørn Sannes Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 1
Labels: None

Attachments: Text File check-inherited-parents-for-identifier.patch     Text File check-inherited-parents-for-identifier2.patch    
Issue Links:
Dependency
depends on DDC-1254 EntityGenerator does not respect Clas... Resolved

 Description   

When using inheritance with identifier in the superclass, results in an error trying to generate code.

Example:

/**
 * Demo\TestInheritanceBundle\Entity\TestBase
 *
 * @ORM\Table()
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"test" = "Test"})
 */
class TestBase
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
}

class Test
{
}

will throw an exception because the DisconnectedClassMetadataFactory disables the code that examines the parent classes.
I've created a simple function that can be used to figure out if a parent has defined an identifier that can be used by the subclass.

Patch attached.



 Comments   
Comment by venimus [ 01/Jul/11 ]

you should also check if there is a parentclass registered

19: if ( ! $this->driver->isTransient($parentClass) && !empty($this->loadedMetadata[$parentClass])) {
Comment by Asbjørn Sannes [ 01/Jul/11 ]

Updated patch with suggestion from venimus to check if the parent class is loaded before using it.

Comment by Benjamin Eberlei [ 12/Jul/11 ]

Fixed





[DDC-1240] Missing exception message Created: 01/Jul/11  Updated: 12/Jul/11  Resolved: 12/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.x
Fix Version/s: 2.0.7, 2.1.1
Security Level: All

Type: Bug Priority: Major
Reporter: Yaroslav Zenin Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

Message in constructor should be assigned to exception message

Index: OptimisticLockException.php
===================================================================
— OptimisticLockException.php (revision 5)
+++ OptimisticLockException.php (working copy)
@@ -33,6 +33,7 @@

public function __construct($msg, $entity)

{ + $this->message = $msg; $this->entity = $entity; }

 Comments   
Comment by Benjamin Eberlei [ 12/Jul/11 ]

Fixed





[DDC-1250] Ambiguous column in query using one-to-one self-referencing bidirectional association Created: 05/Jul/11  Updated: 09/Jul/11  Resolved: 09/Jul/11

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

Type: Bug Priority: Major
Reporter: Alexandr Torchenko Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

This example worked fine with 2.0.6. I suppose it is related to:
"Furthermore inverse OneToOne associations previously always executed an additional query, which is now replaced with a join."

When I try to get object using EntityRepository I receive following error:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'declined_clients_history_id' in where clause is ambiguous' in /usr/share/php/Doctrine/DBAL/Connection.php:613
Stack trace:
#0 /usr/share/php/Doctrine/DBAL/Connection.php(613): PDOStatement->execute()
#1 /usr/share/php/Doctrine/ORM/Persisters/BasicEntityPersister.php(569): Doctrine\DBAL\Connection->executeQuery('SELECT t0.id AS...', Array, Array)
#2 /usr/share/php/Doctrine/ORM/Persisters/BasicEntityPersister.php(644): Doctrine\ORM\Persisters\BasicEntityPersister->load(Array, NULL, Array)
#3 /usr/share/php/Doctrine/ORM/UnitOfWork.php(2015): Doctrine\ORM\Persisters\BasicEntityPersister->loadOneToOneEntity(Array, Object(Entities\ClientsHistory), NULL)
#4 /usr/share/php/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(208): Doctrine\ORM\UnitOfWork->createEntity('Entities\Client...', Array, Array)
#5 /usr/share/php/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(398): Doctrine\ORM\Internal\Hydration\Ob in /usr/share/php/Doctrine/DBAL/Connection.php</b> on line 613

Entity YAML:

Entities\ClientsHistory:
type: entity
table: clients_history
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
[...skiped...]
oneToOne:
declinedClientsHistory:
targetEntity: Entities\ClientsHistory
joinColumn:
name: declined_clients_history_id
referencedColumnName: id
inversedBy: declinedBy
declinedBy:
targetEntity: Entities\ClientsHistory
mappedBy: declinedClientsHistory
lifecycleCallbacks: { }
repositoryClass: Entities\ClientsHistoryRepository

Query which lead to error:
SELECT t0.id AS id1, t0.odate AS odate2, t0.oper AS oper3, t0.type AS type4, t0.transaction_sum AS transaction_sum5, t0.sum AS sum6, t0.balance AS balance7, t0.descr AS descr8, t0.admin AS admin9, t0.payment_id AS payment_id10, t0.balance_operation_id AS balance_operation_id11, t0.payment_id AS payment_id12, t0.declined_clients_history_id AS declined_clients_history_id13, t14.id AS id15, t14.odate AS odate16, t14.oper AS oper17, t14.type AS type18, t14.transaction_sum AS transaction_sum19, t14.sum AS sum20, t14.balance AS balance21, t14.descr AS descr22, t14.admin AS admin23, t14.payment_id AS payment_id24, t14.balance_operation_id AS balance_operation_id25, t14.payment_id AS payment_id26, t14.declined_clients_history_id AS declined_clients_history_id27, t14.id_clients AS id_clients28, t14.id_domains AS id_domains29, t0.id_clients AS id_clients30, t0.id_domains AS id_domains31 FROM clients_history t0 LEFT JOIN clients_history t14 ON t14.declined_clients_history_id = t0.id WHERE declined_clients_history_id = ?



 Comments   
Comment by Benjamin Eberlei [ 09/Jul/11 ]

What kind of operation are you using that leads to this error? $em->find ( parent) or something more complex?

Comment by Benjamin Eberlei [ 09/Jul/11 ]

I could reproduce it, it happens when you do a DQL without fetching the related One To One.

Comment by Benjamin Eberlei [ 09/Jul/11 ]

Fixed





[DDC-1257] Duplicate PHP methods generated from XML metadata lifecycle-callbacks Created: 08/Jul/11  Updated: 09/Jul/11  Resolved: 09/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.1
Fix Version/s: 2.0.7, 2.1.1, 2.2
Security Level: All

Type: Bug Priority: Minor
Reporter: Stephen Lang Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Zend Server 5.1.0, PHP 5.3.5, Apache 2.2.10, Linux (SUSE)



 Description   

If lifecycle callbacks are added to XML metadata like so:

	<lifecycle-callbacks>
		<lifecycle-callback type="prePersist" method="validate" />
		<lifecycle-callback type="preUpdate" method="validate" />
	</lifecycle-callbacks>

The validate() method will appear twice in an entity generated from this metadata.



 Comments   
Comment by Benjamin Eberlei [ 09/Jul/11 ]

Fixed





[DDC-1251] EntityGenerator uses non-existing constant T_AMPERSAND Created: 05/Jul/11  Updated: 09/Jul/11  Resolved: 09/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.1
Fix Version/s: 2.0.7, 2.1.1, 2.2
Security Level: All

Type: Bug Priority: Blocker
Reporter: Jordi Boggiano Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None
Environment:

Irrelevant



 Description   

As per http://www.php.net/manual/en/tokens.php - the T_AMPERSAND token does not exist. I'm not sure what was the intent, but the entity generator fails on the following code:

            $elems = array_map(function($el) {
                return $el;
            }, $elems);

Due to the way notices are handled in Symfony, this basically stops the execution of the generator entirely. Not so great.

  [ErrorException]                                                                                            
  Notice: Use of undefined constant T_AMPERSAND - assumed 'T_AMPERSAND' in ./vendor/doctrine/lib/Doctrine/ORM/Tools/EntityGenerator.php line 454

Commenting out the offending line makes it work fine though, so I think it's just choking while expecting a function that takes args by reference or something.



 Comments   
Comment by Benjamin Eberlei [ 09/Jul/11 ]

Fixed





[DDC-1022] Wakeup behavior difference between proxy and eager-loaded instance Created: 09/Feb/11  Updated: 09/Jul/11  Resolved: 09/Jul/11

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: Git Master
Fix Version/s: 2.0.7, 2.1.1, 2.2
Security Level: All

Type: Bug Priority: Major
Reporter: Karsten Dambekalns Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 0
Labels: None


 Description   

When an object is reconstituted from the persistent storage the unserialize trick is used. That means __wakeup() is called, which is useful to do further initialization as needed.

If the object is lazy loaded a proxy is generated. That proxy is generated with new and thus no _wakeup() is called. When the proxy is eventually initialized still no call to _wakeup() is done, thus initialization code that is "usually" executed is not called when an object is lazy-loaded.

That is a semantical error.



 Comments   
Comment by Benjamin Eberlei [ 26/Feb/11 ]

__wakeup() shouldn't be used as per definition of an entity, http://www.doctrine-project.org/docs/orm/2.0/en/cookbook/implementing-wakeup-or-clone.html

For post initialization "postLoad()" should be used.

Comment by Karsten Dambekalns [ 26/Feb/11 ]

You write shouldn't be used, but that seems somewhat strange, given that the page you point to explicitly says However, it is quite easy to make use of these methods in a safe way by guarding the custom wakeup or clone code with an entity identity check, as demonstrated in the following sections.

And still the issue remains that eager-loaded instances created with the unserialize trick do call wakeup() (of course) but this does not happen when being lazy-loaded. Inconsistency, it screams...

Comment by Benjamin Eberlei [ 09/Jul/11 ]

I changed my mind, this is valid

Comment by Benjamin Eberlei [ 09/Jul/11 ]

Fixed in 2.0.7, 2.1.1 and in master





Generated at Wed Oct 01 20:24:05 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.