[DDC-2332] [UnitOfWork::doPersist()] The spl_objact_hash() generate not unique hash! Created: 05/Mar/13  Updated: 30/May/13

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

Type: Bug Priority: Critical
Reporter: Krisztián Ferenczi Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Symfony 2.1.8, php 5.4.7 and php 5.4.12, Windows 7


Attachments: Text File hashlogs.txt    

 Description   

I created fixtures and some data was inserted many times without calling the Task entity PrePersist event listener.

I printed the used and generated hash and I saw a Proxies_CG_\Asitly\ProjectManagementBundle\Entity\User hash equal a Task entity hash!



 Comments   
Comment by Marco Pivetta [ 05/Mar/13 ]

Please provide either a code example or a test case. As it stands, this issue is incomplete

Comment by Benjamin Eberlei [ 05/Mar/13 ]

Are you calling EntityManager#clear() inbetween? Because PHP reuses the hashes. The ORM accounts for this.

Comment by Benjamin Eberlei [ 05/Mar/13 ]

This is not a reproduce case, i don't want to execute your whole project.

I want to know, what is the actual bug that you see? Can you just print a list of all the hashes? Because the hashes dont differ at the end, bu tjust somewhere in the middle.

Comment by Krisztián Ferenczi [ 05/Mar/13 ]

I attached a hashlogs.txt file. The last Task class hash is 0000000050ab4aba0000000058e1cb12 ( line 3 129 )

This is not unique, view the line 2 760 . The Task is not being saved and the program don't call the prePersist listener. The "UnitOfWork" believe the entity has been saved because the isset($this->entityStates[$oid]) is true. But it is an other entity.

Comment by Krisztián Ferenczi [ 06/Mar/13 ]

The EntityManager::clear() fix the problem, but this is not "good" and "beautiful" solution. Shows no sign of that conflicts were and this is causing the problem. I was looking for the problem 7 hours.





[DDC-2995] Joined Inheritance Mapping and Filters Created: 22/Feb/14  Updated: 23/Mar/14

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.4.2
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Bojidar Hristov Assignee: Marco Pivetta
Resolution: Unresolved Votes: 0
Labels: orm


 Description   

Is there any reason why Inheritance Mapping IS LEFT JOINED not INNER JOINED?

When there is a filter and it's left joined it happens a record might not have parent table record. For example

Class B extends Class A.
Class A have column is_active | and filter activated with is_active = 1 condition.

Final query: LEFT JOIN parent_table s1_ ON p2_.id = s1_.id AND (s1_.is_active = '1')

Record 1 have is_active = false, so result set looks like this:

table_b | table_a
id | id is_active discriminatorColumn
1 | null null null

and occurs exception: The discriminator column ... is missing for ....



 Comments   
Comment by Marco Pivetta [ 22/Feb/14 ]

JTI MUST be left-joined, since not every subclass of the root of the inheritance causes inserts on all of the inheritance tables.

What is the DQL query that is causing the problem?

Comment by Bojidar Hristov [ 22/Feb/14 ]

Hello Marco, I don't query from parent table, but from child. No sense to be LEFT JOINED.

DQL is something like that:

SELECT class1, class2 FROM Class1 class1 JOIN class1.class2 class2;

(Class2 extends Class3)
Class3 have is_active column.

And following filter:

public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) {
if (!$targetEntity->reflClass->implementsInterface('ActivateableInterface'))

{ return ""; }

return $targetTableAlias . '.is_active = ' . $this->getParameter('isActive');
}

Filter condition is added in LEFT JOINED parent_table. Full example SQL:
SELECT ....
FROM class1 c1
INNER JOIN class2 c2 ON c1.c2_id = c2.id
LEFT JOIN table3 c3 ON c2.id = c3.id AND (c3.is_active = '1')

Comment by Marco Pivetta [ 26/Feb/14 ]

There are two possible solutions here:

  • verify if the selected entity is a leaf of a JTI, and use INNER JOIN on that if possible (probably not optimal, since the selection may be part of a LEFT JOIN itself)
  • add filtering on the discriminator column

Bojidar Hristov can you provide a failing test case to make it easier to work on this?

Comment by Bojidar Hristov [ 26/Feb/14 ]

Yes. Here is it sample failing test.

http://pastebin.com/xi8uUNX5 - Parent Class
http://pastebin.com/XRZLbyGX - Child Class
http://pastebin.com/HuPFb98u - Assoc Class
http://pastebin.com/hGqRk74e - TestCase

Here it should found 0 records because ot JOIN and is_active = true filter.

Comment by Benjamin Eberlei [ 23/Mar/14 ]

Since filters are only available on the root, i guess inner join is really necessary here.





[DDC-2954] Paginator loses items Created: 05/Feb/14  Updated: 12/Feb/14

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.3.4, 2.4.1
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Mariusz Jaskółka Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: Pagination, Paginator
Environment:

Linux and Windows, PHP 5, Oracle(OCI8)


Attachments: File LimitSubqueryOutputWalker.php     File LimitSubqueryOutputWalker_bugfix.php    

 Description   

Sometimes when when I use Paginator (Doctrine\ORM\Tools\Pagination\Paginator)

  • with query contains orderBy
  • with $fetchJoinCollection = true
  • lot of joins with toMany associations

there are too few items in result (no, it is not the end of list). There two situations:
1. I have four items on page 1 and two items on page 2 (pageLimit is 5). It is no so bad
2. I have four items on page 1 and there is no page 2 (while there should be 5 all items). This is very bad because I lose one item.

------------------------------------------------------------------
EDIT:
In function Paginator::getIterator there is variable $ids. In my situation it contains five numbers [34,26,34,15,12]. There is duplicated value 34 but ids of top-level entities should be distinct (as far as we do not use CROSS JOIN, or maybe I am wrong).

The Paginator::count function works correctly, it does not count duplicated values twice.

Statement that gets $ids is like following:
SELECT a.* FROM (SELECT DISTINCT ID2, BEGINTIME70 FROM (...) dctrn_result ORDER BY BEGINTIME70 DESC) a WHERE ROWNUM <= 5

------------------------------------------------------------------
EDIT 2 - Bugfix description:
The result items of the query is not unique because of
"SELECT DISTINCT col_with_id, order_by_column (...) ORDER BY order_by_column".
If we had items like following:
(1,A)
(1,B)
(1,B)
(2,C)

After DISTINCT operation the result would be:
(1,A)
(1,B)
(2,C)

But we want to have unique firs column, not pairs. That's why we should do "ORDER BY" before "DISTINCT" - not in the same time.
Please confirm if the solution is correct.



 Comments   
Comment by Marco Pivetta [ 05/Feb/14 ]

Mariusz Jaskółka this needs more details

Comment by Mariusz Jaskółka [ 06/Feb/14 ]

OK, I will try to find out where the problem is.

Comment by Mariusz Jaskółka [ 06/Feb/14 ]

I have edited description, maybe additional information will help.

Comment by Mariusz Jaskółka [ 07/Feb/14 ]

I send the bugfix in attachment. I will describe it soon.

Comment by Mariusz Jaskółka [ 07/Feb/14 ]

Bugfix version 2 - previously I did not notice that oracle loses order after DISTINCT operation. Thus I used GROUP BY.

Comment by Mariusz Jaskółka [ 11/Feb/14 ]

I do not know if I can change status of this issue from "Awaiting Feedback". I can not see such option





[DDC-2879] Persisting collections with Composite Primary Keys composed of 2 Foreign Keys and one metadata field Created: 31/Dec/13  Updated: 07/Jan/14

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

Type: Bug Priority: Major
Reporter: Dylan Johnson Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: composite, identity, mapping, orm, postgresql
Environment:

Ubuntu 13.04 and PostgresSQL 9.1.0 on Vagrant Virtual Machine running an application with Symfony2 backend and JavaScript client



 Description   

SYNOPSIS

Bug prevents persisting a collection of entities in a join table with a Composite Primary
Key made up of 2 Foreign Keys and one metadata field. From these mapping instructions:
http://docs.doctrine-project.org/en/latest/tutorials/composite-primary-keys.html#use-case-3-join-table-with-metadata

ISSUE DETAILS

SUCCESS: When FOREIGN KEY 1 is the same across items in a collection to be persisted, and FOREIGN KEY 2 is greater than FOREIGN KEY 2 in any existing PRIMARY KEY, the entity and collection are persisted correctly

    • Example: GPA "add val below" exists and has assessment value
      {"grade_point_average_id":1,"assessment_id":1,"value":4}

      We will try to add a new assessment value where assessment_id > that of any existing assessment value for GPA "add val below"

    • Request Payload:
      {"name":"Add Val Below","courses":[],"assessmentValues":[{"assessment":1,"value":4},{"assessment":3,"value":2}]}
    • Debug Log:
      [2014-01-07 11:48:01] app.INFO: START GRADE_POINT_AVERAGE_REPOSITORY #SAVE [GPA #GET_NAME =ADD VAL BELOW] [] []
      [2014-01-07 11:48:01] app.INFO:   BEGIN OUTPUT FOR GRADE POINT AVERAGE Add Val Below - ASSESSMENT VALUE 1 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_GRADE_POINT_AVERAGE #GET_ID: 1 [] []
      [2014-01-07 11:48:01] app.INFO:         GRADE_POINT_AVERAGE #GET_ID: 1 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_ASSESSMENT #GET_ID: 1 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_VALUE: 4 [] []
      [2014-01-07 11:48:01] app.INFO:         MANAGED? 1 [] []
      [2014-01-07 11:48:01] app.INFO:   END OUTPUT FOR GRADE POINT AVERAGE Add Val Below - ASSESSMENT VALUE 2 [] []
      [2014-01-07 11:48:01] app.INFO:   BEGIN OUTPUT FOR GRADE POINT AVERAGE Add Val Below - ASSESSMENT VALUE 2 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_GRADE_POINT_AVERAGE #GET_ID: 1 [] []
      [2014-01-07 11:48:01] app.INFO:         GRADE_POINT_AVERAGE #GET_ID: 1 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_ASSESSMENT #GET_ID: 3 [] []
      [2014-01-07 11:48:01] app.INFO:         ASSESSMENT_VALUE #GET_VALUE: 2 [] []
      [2014-01-07 11:48:01] app.INFO:         MANAGED?  [] []
      [2014-01-07 11:48:01] app.INFO:   END OUTPUT FOR GRADE POINT AVERAGE Add Val Below - ASSESSMENT VALUE 3 [] []
      [2014-01-07 11:48:01] app.INFO: END GRADE_POINT_AVERAGE_REPOSITORY #SAVE [GPA #GET_NAME =ADD VAL BELOW] [] []
      [2014-01-07 11:48:01] doctrine.DEBUG: "START TRANSACTION" [] []
      [2014-01-07 11:48:01] doctrine.DEBUG: INSERT INTO gpa_assessment_value (point_value, grade_point_average_id, assessment_id) VALUES (?, ?, ?) {"1":2,"2":"1","3":"3"} []
      [2014-01-07 11:48:01] doctrine.DEBUG: UPDATE gpa_assessment_value SET point_value = ? WHERE grade_point_average_id = ? AND assessment_id = ? [4,1,1] []
      [2014-01-07 11:48:01] doctrine.DEBUG: "COMMIT" [] []

FAILURE: When FOREIGN KEY 1 is the same across items in a collection, and FOREIGN KEY 2 is less than any existing FOREIGN KEY 2, the unit of work tries to INSERT existing entity and does not operate on new entity. The EntityManager thinks it contains() the new entity, but not the old one

    • Example: GPA "add val above" exists and has assessment value
      {"assessment":3,"value":2}

      We will try to add a new assessment value where assessment_id < that of any existing assessment value for GPA "add val above"

    • Request Payload:
      {"name":"Add Val Above","courses":[],"assessmentValues":[{"assessment":1,"value":4},{"assessment":3,"value":2}]}
    • Debug log:
      [2014-01-07 11:47:09] app.INFO: START GRADE_POINT_AVERAGE_REPOSITORY #SAVE [GPA #GET_NAME =ADD VAL ABOVE] [] []
      [2014-01-07 11:47:09] app.INFO:   BEGIN OUTPUT FOR GRADE POINT AVERAGE Add Val Above - ASSESSMENT VALUE 1 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_GRADE_POINT_AVERAGE #GET_ID: 2 [] []
      [2014-01-07 11:47:09] app.INFO:         GRADE_POINT_AVERAGE #GET_ID: 2 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_ASSESSMENT #GET_ID: 1 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_VALUE: 4 [] []
      [2014-01-07 11:47:09] app.INFO:         MANAGED? 1 [] []
      [2014-01-07 11:47:09] app.INFO:   END OUTPUT FOR GRADE POINT AVERAGE Add Val Above - ASSESSMENT VALUE 2 [] []
      [2014-01-07 11:47:09] app.INFO:   BEGIN OUTPUT FOR GRADE POINT AVERAGE Add Val Above - ASSESSMENT VALUE 2 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_GRADE_POINT_AVERAGE #GET_ID: 2 [] []
      [2014-01-07 11:47:09] app.INFO:         GRADE_POINT_AVERAGE #GET_ID: 2 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_ASSESSMENT #GET_ID: 3 [] []
      [2014-01-07 11:47:09] app.INFO:         ASSESSMENT_VALUE #GET_VALUE: 2 [] []
      [2014-01-07 11:47:09] app.INFO:         MANAGED?  [] []
      [2014-01-07 11:47:09] app.INFO:   END OUTPUT FOR GRADE POINT AVERAGE Add Val Above - ASSESSMENT VALUE 3 [] []
      [2014-01-07 11:47:09] app.INFO: END GRADE_POINT_AVERAGE_REPOSITORY #SAVE [GPA #GET_NAME =ADD VAL ABOVE] [] []
      [2014-01-07 11:47:09] doctrine.DEBUG: "START TRANSACTION" [] []
      [2014-01-07 11:47:09] doctrine.DEBUG: INSERT INTO gpa_assessment_value (point_value, grade_point_average_id, assessment_id) VALUES (?, ?, ?) {"1":2,"2":"2","3":"3"} []
      [2014-01-07 11:47:09] doctrine.DEBUG: "ROLLBACK" [] []
      [2014-01-07 11:47:09] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\Security\Http\Firewall\ExceptionListener::onKernelException". [] []
      [2014-01-07 11:47:09] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelException". [] []
      [2014-01-07 11:47:09] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\HttpKernel\EventListener\ExceptionListener::onKernelException". [] []
      [2014-01-07 11:47:09] request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\DBALException: "An exception occurred while executing 'INSERT INTO gpa_assessment_value (point_value, grade_point_average_id, assessment_id) VALUES (?, ?, ?)' with params [2, "2", "3"]:
      
      SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "gpa_assessment_value_pkey"
      DETAIL:  Key (grade_point_average_id, assessment_id)=(2, 3) already exists." at /vagrant/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php line 47 {"exception":"[object] (Doctrine\\DBAL\\DBALException: An exception occurred while executing 'INSERT INTO gpa_assessment_value (point_value, grade_point_average_id, assessment_id) VALUES (?, ?, ?)' with params [2, \"2\", \"3\"]:\n\nSQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint \"gpa_assessment_value_pkey\"\nDETAIL:  Key (grade_point_average_id, assessment_id)=(2, 3) already exists. at /vagrant/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:47, PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint \"gpa_assessment_value_pkey\"\nDETAIL:  Key (grade_point_average_id, assessment_id)=(2, 3) already exists. at /vagrant/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:138)"} []
         

CODE

migration.sql
CREATE TABLE assessment
(
    id       bigserial NOT NULL,
    scale_id bigint    NOT NULL,
    title    varchar   NOT NULL,
    passing  boolean   NOT NULL,
    rank     int,

    PRIMARY KEY (id)
);

CREATE TABLE assessment_scale
(
    id   bigserial NOT NULL,
    name varchar   NOT NULL,

    PRIMARY KEY (id)
);

-- ...

CREATE TABLE grade_point_average
(
    id                         bigserial       NOT NULL,
    name                       varchar         NOT NULL,
    additional_credit_allowance numeric(4, 2),

    PRIMARY KEY (id)
);

-- ...

CREATE TABLE gpa_assessment_value
(
    grade_point_average_id bigint        NOT NULL,
    assessment_id          bigint        NOT NULL,
    point_value            numeric(4, 2) NOT NULL,

    PRIMARY KEY (assessment_id, grade_point_average_id),
    FOREIGN KEY (assessment_id) REFERENCES assessment,
    FOREIGN KEY (grade_point_average_id) REFERENCES grade_point_average
);
GradePointAverage.php
<?php
namespace LGSConnect\Model;

use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Column;
//...
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use LGSConnect\Util\ConstructorArgs;
use LGSConnect\Model\GradePointAverage\AssessmentValue;
// ...

/**
 * @Entity("LGSConnect\Repository\GradePointAverageRepository")
 */
class GradePointAverage
{
    // GradePointAverage Model (owning side): a tool for evaluating a student's performance 
    // by dividing the total points earned by total credits attempted.

    use ConstructorArgs;

    /**
     * @Id
     * @GeneratedValue
     * @Column(type="bigint")
     *
     * @var int
     */
    private $id;

    // ...

    /**
     * @OneToMany(targetEntity="LGSConnect\Model\GradePointAverage\AssessmentValue", mappedBy="gradePointAverage", cascade="persist")
     *
     * @var Collection
     */
    private $assessmentValues;
    
    // ...

    /**
     * @param array $args
     */
    public function __construct(array $args = [])
    {
        $this->assessmentValues = new ArrayCollection;
        // ...
        $this->handleArgs($args);
    }
    
    // ...
    
    /**
     * @return Collection
     */
    public function getAssessmentValues()
    {
        return $this->assessmentValues;
    }

    /**
     * @param ArrayCollection $assessmentValues
     */
    public function setAssessmentValues(ArrayCollection $assessmentValues)
    {
        $this->assessmentValues = $assessmentValues;
    }

    /**
     * @param AssessmentValue $assessmentValue
     */
    public function addAssessmentValue(AssessmentValue $assessmentValue)
    {
        $this->assessmentValues->add($assessmentValue);
    }

    /**
     * @param AssessmentValue $assessmentValue
     */
    public function removeAssessmentValue(AssessmentValue $assessmentValue)
    {
        $this->assessmentValues->removeElement($assessmentValue);
    }
    
    // ...
}
AssessmentValue.php
<?php
namespace LGSConnect\Model\GradePointAverage;

use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\JoinColumn;
use LGSConnect\Model\GradePointAverage;
use LGSConnect\Model\Assessment;
use LGSConnect\Util\ConstructorArgs;

/**
 * @Entity("LGSConnect\Repository\GradePointAverage\AssessmentValueRepository")
 * @Table("gpa_assessment_value")
 */
class AssessmentValue
{
    // AssessmentValue Model (inverse side): a number of points assigned 
    // to an Assessment by a Grade Point Average

    use ConstructorArgs;

    /**
     * @Id
     * @ManyToOne(targetEntity="LGSConnect\Model\GradePointAverage")
     */
    private $gradePointAverage;

    /**
     * @Id
     * @ManyToOne(targetEntity="LGSConnect\Model\Assessment")
     */
    private $assessment;

    /**
     * @Column("point_value")
     *
     * @var float
     */
    private $value;

    /**
     * @param array $args
     */
    public function __construct(array $args = [])
    {
        $this->handleArgs($args);
    }

    /**
     * @return GradePointAverage
     */
    public function getGradePointAverage()
    {
        return $this->gradePointAverage;
    }

    /**
     * @param GradePointAverage $gradePointAverage
     */
    public function setGradePointAverage(GradePointAverage $gradePointAverage)
    {
        $this->gradePointAverage = $gradePointAverage;
    }

    /**
     * @return Assessment
     */
    public function getAssessment()
    {
        return $this->assessment;
    }

    /**
     * @param Assessment $assessment
     */
    public function setAssessment(Assessment $assessment)
    {
        $this->assessment = $assessment;
    }

    /**
     * @return float
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param float $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * @return AssessmentScale
     */
    public function getAssessmentScale()
    {
        return $this->assessment->getScale();
    }
}
Assessment.php
<?php
namespace LGSConnect\Model;

use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\ManyToOne;
use LGSConnect\Model\Assessment\Scale;
use LGSConnect\Util\ConstructorArgs;

/**
 * @Entity("LGSConnect\Repository\AssessmentRepository")
 */
class Assessment
{
    // Assessment (related, but unmapped): A "grade" assigned to a student 
    // for attending a course section

    use ConstructorArgs;

    /**
     * @Id
     * @GeneratedValue
     * @Column(type="bigint")
     *
     * @var int
     */
    private $id;
    
    // ...

    /**
     * @param array $args
     */
    public function __construct(array $args = [])
    {
        $this->handleArgs($args);
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
    
    // ...
}
GradePointAverageRepository.php
<?php
namespace LGSConnect\Repository;

use Doctrine\ORM\EntityRepository;
// ...
use LGSConnect\Model\GradePointAverage;

class GradePointAverageRepository extends BaseRepository implements GradePointAverageRepositoryInterface
{
    // ...

    /**
     * @param GradePointAverage $gradePointAverage
     */
    public function save(GradePointAverage $gradePointAverage)
    {
        $this->getEntityManager()->persist($gradePointAverage);
        $this->getEntityManager()->flush();
    }
}
AssessmentValueRepository.php
<?php
namespace LGSConnect\Repository\GradePointAverage;

use Doctrine\ORM\EntityRepository;
use LGSConnect\Model\GradePointAverage\AssessmentValue;

class AssessmentValueRepository extends EntityRepository
{
    /**
     * @param AssessmentValue $assessmentValue
     */
    public function save(AssessmentValue $assessmentValue)
    {
        $this->getEntityManager()->persist($assessmentValue);
        $this->getEntityManager()->flush();
    }
}
GradePointAverageManager.php
<?php
namespace LGSConnect\Manager;

use InvalidArgumentException;
use Symfony\Component\Validator\ValidatorInterface;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
use Knp\Component\Pager\Pagination\PaginationInterface;
use LGSConnect\Repository\GradePointAverageRepository;
use LGSConnect\PaginationFactory\GradePointAveragePaginationFactoryInterface;
use LGSConnect\Model\GradePointAverage;

/**
 * @Service("grade_point_average_manager")
 */
class GradePointAverageManager
{
    /**
     * @var GradePointAverageRepository
     */
    private $gradePointAverageRepository;

    /**
     * @var GradePointAveragePaginationFactoryInterface
     */
    private $gradePointAveragePaginationFactory;

    /**
     * @var ValidatorInterface
     */
    private $validator;

    /**
     * @InjectParams
     *
     * @param GradePointAverageRepository $gradePointAverageRepository
     * @param GradePointAveragePaginationFactoryInterface $gradePointAveragePaginationFactory
     * @param ValidatorInterface $validator
     */
    public function __construct(
        GradePointAverageRepository $gradePointAverageRepository,
        GradePointAveragePaginationFactoryInterface $gradePointAveragePaginationFactory,
        ValidatorInterface $validator
    )
    {
        $this->gradePointAverageRepository = $gradePointAverageRepository;
        $this->gradePointAveragePaginationFactory = $gradePointAveragePaginationFactory;
        $this->validator = $validator;
    }

    /**
     * @PreAuthorize("isAllowedToManageTheGradePointAverage(#gradePointAverage)")
     * @param GradePointAverage $gradePointAverage
     * @throws InvalidArgumentException
     */
    public function save(GradePointAverage $gradePointAverage)
    {
        $violationList = $this->validator->validate($gradePointAverage);
        if ($violationList->count()) {
            throw new InvalidArgumentException;
        }

        $this->gradePointAverageRepository->save($gradePointAverage);
    }
}
GradePointAverageController.php
<?php
namespace LGSConnect\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Doctrine\Common\Collections\ArrayCollection;
use FOS\RestBundle\View\View;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\SecurityExtraBundle\Annotation\PreAuthorize;
use Knp\Component\Pager\Pagination\PaginationInterface;
use LGSConnect\Manager\GradePointAverageManager;
use LGSConnect\Model\GradePointAverage;
use LGSConnect\Model\GradePointAverage\AssessmentValue;

/**
 * @Service("grade_point_average_controller", parent="lgs.controller.abstract")
 * @Route("/gpa", service="grade_point_average_controller")
 */
class GradePointAverageController extends BaseController
{
    /**
     * @var GradePointAverageManager
     */
    private $gradePointAverageManager;

    private $logger;

    /**
     * @InjectParams
     *
     * @param GradePointAverageManager $gradePointAverageManager
     * @param LoggerInterface $logger
     */
    public function __construct(GradePointAverageManager $gradePointAverageManager, LoggerInterface $logger)
    {
        $this->gradePointAverageManager = $gradePointAverageManager;
        $this->logger = $logger;
    }
    
    // ...

    /**
     * @Route("/{id}", name="gpa.edit", requirements={"id" = "\d+"})
     * @Method("PUT")
     *
     * @param Request $request
     * @param GradePointAverage $gpa
     * @return View
     */
    public function editAction(Request $request, GradePointAverage $gpa)
    {
        $form = $this->formFactory->createNamed(null, 'gpa', $gpa, [
            'method' => 'PUT',
        ]);
        $form->handleRequest($request);

        foreach ($gpa->getAssessmentValues() as $av) {
            $this->logger->info('GPA ID PREVALIDATE IN CONTROLLER:'.$gpa->getId());
            $this->logger->info('PREVALIDATE IN CONTROLLER ASSESSMENT VAL ASSESSMENT ID:'.$av->getAssessment()->getId());
            $this->logger->info('PREVALIDATE IN CONTROLLER ASSESSMENT VAL POINTS:'.$av->getValue());
        }

        /*
        // try reversing the order of the collection to see if that helps
        $assessmentVals = $gpa->getAssessmentValues()->toArray();
        $reversed = array_reverse($assessmentVals);
        $reversedColl = new ArrayCollection($reversed);
        $gpa->setAssessmentValues($reversedColl);
        */

        if ($form->isValid()) {
            foreach ($gpa->getAssessmentValues() as $av) {
                $this->logger->info('GPA ID PRESAVE IN CONTROLLER:'.$gpa->getId());
                $this->logger->info('PRESAVE IN CONTROLLER ASSESSMENT VAL ASSESSMENT ID:'.$av->getAssessment()->getId());
                $this->logger->info('PRESAVE IN CONTROLLER ASSESSMENT VAL POINTS:'.$av->getValue());
            }
            $this->gradePointAverageManager->save($gpa);

            return new View($gpa, 204);
        }

        return new View($form);
    }

    // ...
}
GradePointAverageType.php
<?php
namespace LGSConnect\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use JMS\DiExtraBundle\Annotation\FormType;

/**
 * @FormType
 */
class GradePointAverageType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('courses', 'entity', [
                'class' => 'Model:Course',
                'multiple' => true
            ])
            ->add('assessmentValues', 'collection', [
                'type' => 'gpa_assessment_value',
                'allow_add' => true,
                'by_reference' => false,
            ])
        ;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'gpa';
    }
}
AssessmentValueType.php
<?php
namespace LGSConnect\Form\Type\GradePointAverage;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use JMS\DiExtraBundle\Annotation\FormType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * @FormType("gpa_assessment_value")
 */
class AssessmentValueType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('assessment', 'entity', [
                'class' => 'Model:Assessment',
            ])
            ->add('value', 'number', [
                'precision' => 2,
            ])
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'LGSConnect\Model\GradePointAverage\AssessmentValue',
        ]);
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'gpa_assessment_value';
    }
}





[DDC-2870] Doctrine error when using SUM(a.id=1) as `ìdentifier`: Expected Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got '=' Created: 22/Dec/13  Updated: 06/Jan/14

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

Type: Bug Priority: Major
Reporter: Maxim Geerinck Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: dql
Environment:

Symfony2 bundle



 Description   

Doctrine error when using SUM(a.id=1) as `ìdentifier`: Expected Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got '='

I am trying to execute a query in doctrine that contains something like this

SUM(a.id = 1) as `1`
for some reasons it always gives me the following error:

[Syntax Error] line 0, col 15: Error: Expected Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got '='
This is the code i am using

$result = $em->getRepository('MyBundle:PlayerAction')
->createQueryBuilder('pa')
->select(array(
'SUM(a.id=1) as `1`,
SUM(a.id=2) as `2`,
SUM(a.id=3) as `3`,
p.playerName,
pa.timestamp'
))
->innerJoin('pa.action', 'a')
->innerJoin('pa.player', 'p')
->where('pa.timestamp > ?1')
->groupBy('p')
->setParameter(1, time() - $time)
->orderBy('p.playerName', 'ASC');






[DDC-2746] When generating DQL query entities with "Class Table Inheritance" is a SQL generated inconsistent Created: 16/Oct/13  Updated: 18/Oct/13

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: DQL
Affects Version/s: 2.4
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Hugo Henrique Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File DDC2746Test.php     File DDC2746Test_original.php    

 Description   

When I run the query DQL involving entities "Class Table Inheritance" is a SQL generated inconsistent see this gist: https://gist.github.com/hugohenrique/b322e8d998c870265177



 Comments   
Comment by Hugo Henrique [ 17/Oct/13 ]

An example of incoherent generation of SQL:
https://gist.github.com/hugohenrique/b322e8d998c870265177





[DDC-2675] WITH (NOLOCK) failing when using JOIN Created: 12/Sep/13  Updated: 31/Jan/14

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

Type: Bug Priority: Major
Reporter: Flip Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 2
Labels: None
Environment:

MSSQL 2008 R2


Issue Links:
Duplicate
is duplicated by DDC-2310 Recent changes to DBAL SQL Server pla... Resolved
Reference
is referenced by DDC-2919 LockMode::NONE evaluation inconsisten... Resolved

 Description   

I ran the doctrine test suite and there are a lot of tests failing with

[SQL Server]Incorrect syntax near the keyword 'with'

List of failing test because of this issue:
2) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testUnnamedScalarResultsAreOneBased
3) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testOrderByResultVariableCollectionSize
4) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testIsNullAssociation
5) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testSelectSubselect
6) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testInSubselect
7) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testGroupByMultipleFields
8) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testUpdateAs
9) Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest::testDeleteAs
10) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testCRUD
11) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testSelfReferencingOneToOne
12) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testSelfReferencingManyToMany
13) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testLazyLoading2
14) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testBulkUpdateIssueDDC368
15) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testBulkUpdateNonScalarParameterDDC1341
16) Doctrine\Tests\ORM\Functional\ClassTableInheritanceTest::testQueryForInheritedSingleValuedAssociation
17) Doctrine\Tests\ORM\Functional\Locking\OptimisticTest::testJoinedChildFailureThrowsException
18) Doctrine\Tests\ORM\Functional\Locking\OptimisticTest::testJoinedParentFailureThrowsException
19) Doctrine\Tests\ORM\Functional\OrderedJoinedTableInheritanceCollectionTest::testOrderdOneToManyCollection
20) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testAggregateSum
21) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testAggregateAvg
22) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testAggregateMin
23) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testAggregateMax
24) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testAggregateCount
25) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionAbs
26) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionConcat
27) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionLength
28) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionLocate
29) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionLower
30) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionMod
31) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionSqrt
32) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionUpper
33) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionSubstring
34) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testFunctionTrim
35) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testOperatorAdd
36) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testOperatorSub
37) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testOperatorMultiply
38) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testOperatorDiv
39) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testConcatFunction
40) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testDateDiff
41) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testDateAdd
42) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testDateSub
43) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testBitOrComparison
44) Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testBitAndComparison
45) Doctrine\Tests\ORM\Functional\SQLFilterTest::testJoinSubclassPersister_FilterOnlyOnRootTableWhenFetchingSubEntity
46) Doctrine\Tests\ORM\Functional\SQLFilterTest::testJoinSubclassPersister_FilterOnlyOnRootTableWhenFetchingRootEntity
47) Doctrine\Tests\ORM\Functional\Ticket\DDC163Test::testQueryWithOrConditionUsingTwoRelationOnSameEntity
48) Doctrine\Tests\ORM\Functional\Ticket\DDC168Test::testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray
49) Doctrine\Tests\ORM\Functional\Ticket\DDC1995Test::testIssue
50) Doctrine\Tests\ORM\Functional\Ticket\DDC1995Test::testQueryCache
51) Doctrine\Tests\ORM\Functional\Ticket\DDC2090Test::testIssue
53) Doctrine\Tests\ORM\Functional\Ticket\DDC279Test::testDDC279
54) Doctrine\Tests\ORM\Functional\Ticket\DDC933Test::testLockCTIClass

One example, test 20)
Generated SQL:

SELECT SUM(c0_.salary) AS sclr0
FROM company_managers c1_
INNER JOIN company_employees c0_ ON c1_.id = c0_.id
INNER JOIN company_persons c2_ ON c1_.id = c2_.id
WITH (NOLOCK)

Solution:
Placing WITH (NOLOCK) after the table(s), instead of after ON clause. Depending on the wanted result it should be placed several times when using JOIN. See this StackOverflow Post for more information:
http://stackoverflow.com/questions/3783525/sql-server-nolock-and-joins



 Comments   
Comment by Steve Müller [ 15/Jan/14 ]

Please refer to DDC-2310 as it describes exactly the same issue.

Comment by Doctrine Bot [ 31/Jan/14 ]

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





[DDC-2631] Replacing object in a OneToOne with OrphanRemoval=true isn't working as expected Created: 23/Aug/13  Updated: 26/Nov/13

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

Type: Bug Priority: Major
Reporter: Felipe Guaycuru Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: orm
Environment:

PHP 5.4



 Description   

So I have a class defined like this:

class PhoneSettings {
[...]

/**

  • @OneToOne(targetEntity="Medium", cascade= {"persist", "remove"}

    , orphanRemoval=true)

  • @JoinColumn(name="medium_id", referencedColumnName="medium_id", nullable=true, onDelete="SET NULL")
    **/
    protected $medium = null;

[...]
}
And class Medium has no reference to the class Settings.

Now suppose I have a $Settings object that is already persisted and has been correctly loaded. Also suppose that the $Settings object has a $medium (that is, $Settings->medium = $OldMedium)

Now suppose I do:

$Settings->medium = $NewMedium;
Where $NewMedium is a different Medium object.

When I persist $Settings, Doctrine does delete $OldMedium from the DB, but the problem is that it also deletes $NewMedium ...

I have tried removing onDelete="SET NULL", but then I receive a "cannot delete, constraint failed" error...






[DDC-2590] Class inheritance - left join between child and parent entities Created: 06/Aug/13  Updated: 25/Mar/14

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.3.1, 2.3.2, 2.3.3, 2.3.4
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Tomáš Ďuračka Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: inheritance, joins, orm, sql-walker


 Description   

The piece of code given under creates wrong sql to me.

Module is parent entity for BusinessModule entity. Category is joined with BusinessModule.

Module entity is only left joined to its child entity and that's the problem because it contains a field "name" used for filtering. So even if there is no module having the name, categories are still included.

I need the parent entity to be inner joined to child entity not left joined.

File doctrine2/lib/Doctrine/ORM/Query/SqlWalker.php line 353:

// If this is a joined association we must use left joins to preserve the correct result.
$sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
$qb->select('c')
->from('Category', 'c')
->join('c.module', 'm', 'WITH', 'm.name = :moduleName')
->setParameter('moduleName', $moduleName);
SELECT c0_.category_id AS category_id0, c0_.title AS title1, c0_.h1 AS h12, c0_.alias AS alias3,
c0_.insertion_fee AS insertion_fee4, c0_.description AS description5, c0_.parent_category_id AS
parent_category_id6, c0_.module_id AS module_id7 
FROM category c0_ 
INNER JOIN business_module b1_ ON c0_.module_id = b1_.module_id 
LEFT JOIN module m2_ ON b1_.module_id = m2_.module_id AND (m2_.name = ?)


 Comments   
Comment by Marek Štípek [ 06/Nov/13 ]

I am experiencing the same issue. The workarround could be to use LEFT JOIN with IS NOT NULL condition... But it also doesnt work after this commit
https://github.com/doctrine/doctrine2/commit/d9c1782a4f6d46f66e9deb2c375830f9192d4482 (i had to revert to dev-master#13c1efb240dd0af25ad0abe230df98ec895892c7)





[DDC-2575] Hydration bug Created: 27/Jul/13  Updated: 19/Dec/13

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

Type: Bug Priority: Major
Reporter: Nicolas Bottarini Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 1
Labels: dql, orm


 Description   

I have the following class mappings:

class A
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;
    
    /**
     * @Column(type="string", length=100, nullable=FALSE)
     */    
    protected $sampleField;
    
    /**
     * @OneToOne(targetEntity="B", mappedBy="aRelation")
     **/     
    protected $bRelation;
}
class B
{
    /**
     * @Id
     * @OneToOne(targetEntity="A", inversedBy="bRelation")
     * @JoinColumn(name="a_id", referencedColumnName="id", nullable=FALSE, onDelete="CASCADE")
     */
    protected $aRelation;

    /**
     * @ManyToOne(targetEntity="C")
     * @JoinColumn(name="c_id", referencedColumnName="id", nullable=FALSE, onDelete="CASCADE")
     */
    protected $cRelation;

}
class C
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;
    
    /**
     * @Column(type="string", length=100, nullable=FALSE)
     */    
    protected $sampleField;
}

Then I make the following query:

$qb = $em->createQueryBuilder();
$qb = $qb->select('a, b, c')
      ->from('A','a')
      ->leftJoin('a.bRelation', 'b')
      ->leftJoin('b.cRelation', 'c');
      
$result = $qb->getQuery()->getResult();

The result contains a collection of instances of class A with the a.bRelation field populated and the a.bRelation.cRelation field populated in all rows except the last one.

The problem is an hydration problem. The Parser constructs the select statement with the fields of class A, then the fields of class C and last the fields of class B. The hydrator don't work correctly because when it's hydrating class C it doesn't find the class B (because it appears last in the select statement).
I think the problem is because class B only contains associations. If i put an extra field (an string for example) in class B it works as expected.



 Comments   
Comment by Popy [ 28/Oct/13 ]

I have the same kind of bug : i have a OneToMany relations which stays to null if I request both entities in one query, and miss an entity if I preload the related entities in a second query. It seems to occur on the last entity of the list.

If I remember well the hydrator code, there's (in the hydratation loop) something like "If we find a new root entity (or maybe on each row, i'm not sure), link the entities we didn't link". Maybe this thing is not done AFTER the loop for remaining entities.

I'll try to dig again into the hydrator code tomorrow to check this hypothesis.

Comment by Popy [ 29/Oct/13 ]

This bug seems more severe :

I made a test on a query with 3 entities (root, root->a, root->b, a and b relations are OneToMany, so no connections on this side), and there's the process I witness :

  • First result row
  • The hydrator finds the linked entities before the root... and just does nothing (line 359)
  • The hydrator finds the root entity, and hydrate it
  • Other result rows
  • The hydrator finds the linked entities before the root... and associate them with the previously found root entity, which is the root entity fetched on the first row
  • The hydrator finds the root entity, so trash the previous, and hydrate (without related entities, as they were linked to previous root entity)

To finish, the last row has no related entities, as its related entities were given to the previous row.

Comment by Popy [ 29/Oct/13 ]

Bug confirmed in a small Symfony app and Doctrine 2.3. I managed to reproduce the bug with 3 entities :

  • A (id autoincrement)
  • B (id autoincrement)
  • Root (composite id a,b which are ManyToOne relations to A and B entities)

Can provide the app to ease things.

Comment by Popy [ 29/Oct/13 ]

Possible workaround : declaring integer fields as ID (with the same field name as relation fields) makes the thing working again (at the price of thoose two useless properties and a prePersist method to fill them with related entity ids)

Comment by Benjamin Eberlei [ 14/Dec/13 ]

First step here: Try to reproduce this issue with the given entities above in a Testcase

Comment by Karol Horowski [ 14/Dec/13 ]

I created test for this issue, but I can't reproduce id. My pull request is here https://github.com/doctrine/doctrine2/pull/878

Comment by Popy [ 15/Dec/13 ]

You should maybe call $this->_em->clear() at the end of your setUp method.

I still have a Symfony Bundle reproducing the bug, how can i hand it to you ?

Comment by Benjamin Eberlei [ 15/Dec/13 ]

Popy you can create a branch of that symfony standard edition, and push it to a fork of symfony-standard on your Github account. Then you can comment a link to your branch on Github.

Comment by Karol Horowski [ 17/Dec/13 ]

After Popy's suggestion I added $this->_em->clear() and now I have failing test. My fault with this quick "everything is ok".

I tried to search what's happening in ObjectHydrator but something strange is going on in hydrateRowData method.

Comment by Popy [ 17/Dec/13 ]

Be carefull, headache come fast while reading this method

As far as I know, the problem could be solved if the hydrator started by hydrating the root entity first. Maybe.

Comment by Karol Horowski [ 19/Dec/13 ]

In select statement fields are in this order Root, B, A. Relations in my test are Root 1:1 A *:1 B.
Hydrator first gets Root data and next B. But here (line 407 in ObjectHydrator) it doesn't find A parent in resultPointers.

For now I don't have any idea how to add new logic for this.





[DDC-2449] Amazon Redshift Support Created: 15/May/13  Updated: 15/May/13

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

Type: New Feature Priority: Major
Reporter: Kirill Fuchs Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Amazon Redshift



 Description   

It would be nice to get doctrine compatible with Amazon Redshift. It uses a Postgres connector but there are some differences. I'm currently facing an issue with the primary id, in Redshift the generation of an id is different from Postgres and so I'm getting errors associated with generating an id.

Here are some references that might be useful:
node-orm faced the same issue and seems like they figured it out: https://github.com/dresende/node-orm2/issues/39

Amazon Manual:
http://awsdocs.s3.amazonaws.com/redshift/latest/redshift-dg.pdf






[DDC-2424] Removing an inherited entity via a delete cascade constraint does not remove the parent row Created: 02/May/13  Updated: 06/May/13

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: 2.3.3
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Bruno Jacquet Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Mysql 5.1.66 / Symfony 2.2.1



 Description   

For a parent class:

/**
 * @ORM\Entity
 * @ORM\Table(name="Base")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"child1" = "Child1", "child2" = "Child2"})
 */

and simple Child1 & Child2 entities.

With another entity (let's call it ExternalEntity) having a bidirectional OneToOne relation owned by Child1:

class Child1 extends Base
{
  /**
   * @ORM\OneToOne(targetEntity="ExternalEntity", inversedBy="xxx")
   * @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
   */
   private theForeignKey;
}

Enough for the context.
The symptoms:

$em->remove(instanceOfExternalEntity);

removes the ExternalEntity row and the Child1 row. But a dangling row in the Base table is still there for the now inexistent Child1 instance.

Though, a manual delete of either the associated Child1 OR Base row and then the ExternalEntity works.

The problem with the cascading deletion of the parent seems to be only present when deleting through a MYSQL cascading delete from another row which has a foreign key on a child. (Not tested with a foreign key on the parent though)



 Comments   
Comment by Benjamin Eberlei [ 04/May/13 ]

Can you show the CREATE TABLE and FOREIGN KEY statements of all the tables involved? It seems the cascade of the foreign keys is not propagated between multiple tables?

Comment by Bruno Jacquet [ 06/May/13 ]

CREATE TABLE Base (id INT AUTO_INCREMENT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE Child1 (id INT NOT NULL, foreignKey INT NOT NULL, UNIQUE INDEX UNIQ_179B6E88E992F5A (foreignKey), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

ALTER TABLE Child1 ADD CONSTRAINT FK_179B6E88E992F5A FOREIGN KEY (foreignKey) REFERENCES ExternalEntity (id) ON DELETE CASCADE;
ALTER TABLE Child1 ADD CONSTRAINT FK_179B6E8BF396750 FOREIGN KEY (id) REFERENCES Base (id) ON DELETE CASCADE;

Comment by Bruno Jacquet [ 06/May/13 ]

The problem is that, the SQL model never explicitely tells the DB to delete the corresponding Base when Child1 gets removed. It looks like it is handled by the doctrine entity manager layer and not the actual DB engine (Base has no on delete cascade nor foreign key to its children).
So only doctrine can add the logic here because it knows the entity schema. But in this case, when it is deleted from another table, it looks like the special treatment is not triggered.

Comment by Bruno Jacquet [ 06/May/13 ]

Maybe using

cascade={"remove"}

, instead of

onDelete="CASCADE"

to force the cascading process to be handled by doctrine would workaround the bug... But I prefer to have my DB do the logic work as much as possible.





[DDC-2406] Merging of new detached entities with PrePersist lifecycle callback breaks Created: 19/Apr/13  Updated: 28/Nov/13

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

Type: Bug Priority: Major
Reporter: Oleg Namaka Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: merge,, prePersist


 Description   

Merging of new detached entities with PrePersist lifecycle callback breaks:

Code snippet:

    class A
    {
       /**
        *  @ORM\ManyToOne(targetEntity= ...
        *  @ORM\JoinColumn(name=" ...
        */
        protected $b;
        
        public function getB()
        {
            return $this->b;
        }
        
        public function setB($b)
        {
            $this->b = $b;
        }
        
        /**
         *
         * @ORM\PrePersist
         *
         * @return void
         */
        public function onPrePersist()
        {
           if ($this->getB() === null) {
                throw new \Exception('B instance must be defined);
           }
           ....
        }
    }
    
    class B 
    {
    }
    
    $a = new A();
    $b = $em->find('B', 1);
    $a->setB($b);
    $em->persist($a); // works fine as B instance is set
    $em->detach($a);
    
    $a = $em->merge($a) // breaks in onPrePersist

The reason it happens is that the merge operation is trying to persist a new entity created by uow::newInstance($class) without populating its properties first:

 // If there is no ID, it is actually NEW.
    ....
    if ( ! $id) {
        $managedCopy = $this->newInstance($class);

        $this->persistNew($class, $managedCopy);
    } else {
	....

This should happen first for the $managedCopy:

    // Merge state of $entity into existing (managed) entity
    foreach ($class->reflClass->getProperties() as $prop) {
        ....


 Comments   
Comment by Fabio B. Silva [ 28/Apr/13 ]

Benjamin Eberlei, Is this an expected behavior ?

I mean.. This issue is about dispatch the event before copy the original values into the managed instance.
But overall, should $em->detach() trigger @PrePersist events ?

Comment by Benjamin Eberlei [ 01/May/13 ]

Fabio B. Silva he talks about $em->merge() on a detached entity calling pre persist. This should only happen on a NEW entity, not on a DETACHED one.

Comment by Oleg Namaka [ 01/May/13 ]

I tend to disagree with the statement above about pre persist that should not happen on a detached entity being merged back in. If this event handler contains a business logic that this entity needs to be checked against and the detached entity was modified before the merge operation in a way that invalidates it in the prePersist than I will end up with the invalid entity in the identity map. If the merge operation calls persist it must run the prePersist event handler as well for consistency.

If there is a logic that prevents persisting invalid entities why should it bypassed in the merge operation?

Comment by Cory Close [ 28/Nov/13 ]

I can confirm that this bug has not been fixed while using doctrine 2.4

Exactly as Oleg Namaka has described, my organization is trying to use @PrePersist callbacks to enforce validation on new entities.

However, we use an extensive client side framework that sends json back to our server. Our workflow is then:

deserializeJson into detached entity
merge detached entity to get it managed (this will apply our edits to an existing entity, or create a new one if this one is new)
persist

However, some entities run into the above problem while using this workflow, so our validation is not run. I can provide more code samples if required.





[DDC-2387] convert-mapping not working correctly with composite primary keys/foreign keys in 2.4.0-RC1 Created: 03/Apr/13  Updated: 01/Jul/13

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.3, 2.3.1, 2.3.2, 2.3.3, 2.3.4
Fix Version/s: 2.3.4
Security Level: All

Type: Bug Priority: Major
Reporter: Nicholas Van Dusen Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 4
Labels: None
Environment:

Symfony 2.2.0, MySQL 5.1



 Description   

(Apologies if this is somehow a Symfony-specific issue)

I updated my application via Composer yesterday, and received Doctrine 2.4.0-RC1. After this update, generating entities has been problematic under certain circumstances.

Here is an example table in MySQL:

CREATE TABLE `user_email` (
  `user_id` int(10) unsigned NOT NULL COMMENT 'FK to user',
  `email` varchar(254) NOT NULL,
  `email_datasource` smallint(1) unsigned NOT NULL COMMENT 'FK to datasource_code',
  `insert_date` datetime NOT NULL,
  PRIMARY KEY (`user_id`,`email`,`email_datasource`),
  KEY `FK_UserEmail_DataSourceCode` (`email_datasource`),
  CONSTRAINT `FK_UserEmail_User` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

In Doctrine 2.3, the mapping works correctly, and you end up with a 3-part primary key, with a user property mapped to the User entity, and a datasourceCode property mapped to the DatasourceCode entity. All good.

In 2.4, the following error is given: Single id is not allowed on composite primary key in entity UserEmail.

Removing one of the foreign keys in the table (either to User or DatasourceCode) but keeping the primary key set to all 3 columns allows the mapping to work. But, if you then remove one of the columns from the primary key (say, email_datasource) it fails again.



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

Can you provide the full stack trace to the exception please?

$e->getTraceAsString();
Comment by Nicholas Van Dusen [ 11/Apr/13 ]

Benjamin, I'm not sure how to get the trace for you, since I'm running from inside the Symfony2 doctrine:mapping:import command line item.

Comment by Christophe Coevoet [ 11/Apr/13 ]

Use the --verbose option when running the command

Comment by Nicholas Van Dusen [ 11/Apr/13 ]

I was able to get a trace for you:

#0 /var/www/html/voxrepublic/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php(1571): Doctrine\ORM\Mapping\MappingException::singleIdNotAllowedOnCompositePrimaryKey('UserEmail')
#1 /var/www/html/voxrepublic/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(422): Doctrine\ORM\Mapping\ClassMetadataInfo->getSingleIdentifierFieldName()
#2 /var/www/html/voxrepublic/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(136): Doctrine\ORM\Mapping\ClassMetadataFactory->completeIdGeneratorMapping(Object(Doctrine\ORM\Mapping\ClassMetadata))
#3 /var/www/html/voxrepublic/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php(302): Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata(Object(Doctrine\ORM\Mapping\ClassMetadata), NULL, false, Array)
#4 /var/www/html/voxrepublic/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php(212): Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata('UserEmail')
#5 /var/www/html/voxrepublic/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php(112): Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor('UserEmail')
#6 /var/www/html/voxrepublic/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php(108): Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getAllMetadata()
#7 /var/www/html/voxrepublic/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php(240): Doctrine\Bundle\DoctrineBundle\Command\ImportMappingDoctrineCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /var/www/html/voxrepublic/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(195): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /var/www/html/voxrepublic/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(78): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /var/www/html/voxrepublic/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(106): Symfony\Bundle\FrameworkBundle\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /var/www/html/voxrepublic/app/console(22): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput))
#12 {main}
Comment by Benjamin Eberlei [ 14/Apr/13 ]

The problem is that Doctrine seems to detect that you only have on id field on this entity and then a new function of 2.4 throws this error. I will try to reproduce this with your table. Until then could you show me the var_dump() of the $class variable in Doctrine\ORM\ClassMetadataFactory#completeIdGeneratorMapping()? This would help very much already.

Comment by Maximilian Beck [ 15/Apr/13 ]

I have the same error when using "doctrine:mapping:import"

CREATE  TABLE IF NOT EXISTS `dev_Recipe`.`step` (

  `recipe_id` INT NOT NULL ,

  `step_number` INT NOT NULL ,

  `description` TEXT NULL ,

  `timer` INT NULL ,

  `image` VARCHAR(100) NULL ,

  PRIMARY KEY (`recipe_id`, `step_number`) ,

  INDEX `recipe_id_idx` (`recipe_id` ASC) ,

  INDEX `step_number` (`step_number` ASC) ,

  CONSTRAINT `step_recipe_id`

    FOREIGN KEY (`recipe_id` )

    REFERENCES `dev_Recipe`.`recipe` (`recipe_id` )

    ON DELETE NO ACTION

    ON UPDATE NO ACTION)

ENGINE = InnoDB;
Exception trace:
 () at \htdocs\SF2\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\MappingException.php:258
 Doctrine\ORM\Mapping\MappingException::singleIdNotAllowedOnCompositePrimaryKey() at \htdocs\SF2\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\ClassMetadataInfo.php:1571
 Doctrine\ORM\Mapping\ClassMetadataInfo->getSingleIdentifierFieldName() at \htdocs\SF2\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\ClassMetadataFactory.php:422
 Doctrine\ORM\Mapping\ClassMetadataFactory->completeIdGeneratorMapping() at \htdocs\SF2\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\ClassMetadataFactory.php:136
 Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata() at \htdocs\SF2\vendor\doctrine\common\lib\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory.php:302
 Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata() at \htdocs\SF2\vendor\doctrine\common\lib\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory.php:212
 Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor() at \htdocs\SF2\vendor\doctrine\common\lib\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory.php:112
 Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getAllMetadata() at \htdocs\SF2\vendor\doctrine\orm\lib\Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand.php:126
 Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand->execute() at \htdocs\SF2\vendor\doctrine\doctrine-bundle\Doctrine\Bundle\DoctrineBundle\Command\Proxy\ConvertMappingDoctrineCommand.php:59
 Doctrine\Bundle\DoctrineBundle\Command\Proxy\ConvertMappingDoctrineCommand->execute() at \htdocs\SF2\vendor\symfony\symfony\src\Symfony\Component\Console\Command\Command.php:240
 Symfony\Component\Console\Command\Command->run() at \htdocs\SF2\vendor\symfony\symfony\src\Symfony\Component\Console\Application.php:193
 Symfony\Component\Console\Application->doRun() at \htdocs\SF2\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Console\Application.php:78
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at \htdocs\SF2\vendor\symfony\symfony\src\Symfony\Component\Console\Application.php:106
 Symfony\Component\Console\Application->run() at \htdocs\SF2\app\console:22

"var_dump($class);" returns:

object(Doctrine\ORM\Mapping\ClassMetadata)#470 (36) {
  ["name"]=>
  string(10) "Ingredient"
  ["namespace"]=>
  string(0) ""
  ["rootEntityName"]=>
  string(10) "Ingredient"
  ["customGeneratorDefinition"]=>
  NULL
  ["customRepositoryClassName"]=>
  NULL
  ["isMappedSuperclass"]=>
  bool(false)
  ["parentClasses"]=>
  array(0) {
  }
  ["subClasses"]=>
  array(0) {
  }
  ["namedQueries"]=>
  array(0) {
  }
  ["namedNativeQueries"]=>
  array(0) {
  }
  ["sqlResultSetMappings"]=>
  array(0) {
  }
  ["identifier"]=>
  array(1) {
    [0]=>
    string(12) "ingredientId"
  }
  ["inheritanceType"]=>
  int(1)
  ["generatorType"]=>
  int(1)
  ["fieldMappings"]=>
  array(4) {
    ["ingredientId"]=>
    array(6) {
      ["id"]=>
      bool(true)
      ["fieldName"]=>
      string(12) "ingredientId"
      ["columnName"]=>
      string(13) "ingredient_id"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["name"]=>
    array(6) {
      ["fieldName"]=>
      string(4) "name"
      ["columnName"]=>
      string(4) "name"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(45)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["color"]=>
    array(5) {
      ["fieldName"]=>
      string(5) "color"
      ["columnName"]=>
      string(5) "color"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["img"]=>
    array(6) {
      ["fieldName"]=>
      string(3) "img"
      ["columnName"]=>
      string(3) "img"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(45)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(true)
    }
  }
  ["fieldNames"]=>
  array(4) {
    ["ingredient_id"]=>
    string(12) "ingredientId"
    ["name"]=>
    string(4) "name"
    ["color"]=>
    string(5) "color"
    ["img"]=>
    string(3) "img"
  }
  ["columnNames"]=>
  array(4) {
    ["ingredientId"]=>
    string(13) "ingredient_id"
    ["name"]=>
    string(4) "name"
    ["color"]=>
    string(5) "color"
    ["img"]=>
    string(3) "img"
  }
  ["discriminatorValue"]=>
  NULL
  ["discriminatorMap"]=>
  array(0) {
  }
  ["discriminatorColumn"]=>
  NULL
  ["table"]=>
  array(1) {
    ["name"]=>
    string(10) "ingredient"
  }
  ["lifecycleCallbacks"]=>
  array(0) {
  }
  ["associationMappings"]=>
  array(1) {
    ["ingredientCategory"]=>
    array(15) {
      ["fieldName"]=>
      string(18) "ingredientCategory"
      ["targetEntity"]=>
      string(18) "IngredientCategory"
      ["mappedBy"]=>
      string(10) "ingredient"
      ["type"]=>
      int(8)
      ["inversedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(false)
      ["sourceEntity"]=>
      string(10) "Ingredient"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["orphanRemoval"]=>
      bool(false)
    }
  }
  ["isIdentifierComposite"]=>
  bool(false)
  ["containsForeignIdentifier"]=>
  bool(false)
  ["idGenerator"]=>
  NULL
  ["sequenceGeneratorDefinition"]=>
  NULL
  ["tableGeneratorDefinition"]=>
  NULL
  ["changeTrackingPolicy"]=>
  int(1)
  ["isVersioned"]=>
  NULL
  ["versionField"]=>
  NULL
  ["reflClass"]=>
  NULL
  ["isReadOnly"]=>
  bool(false)
  ["namingStrategy":protected]=>
  object(Doctrine\ORM\Mapping\DefaultNamingStrategy)#258 (0) {
  }
  ["reflFields"]=>
  array(0) {
  }
  ["_prototype":"Doctrine\ORM\Mapping\ClassMetadataInfo":private]=>
  NULL
}
object(Doctrine\ORM\Mapping\ClassMetadata)#472 (36) {
  ["name"]=>
  string(18) "IngredientCategory"
  ["namespace"]=>
  string(0) ""
  ["rootEntityName"]=>
  string(18) "IngredientCategory"
  ["customGeneratorDefinition"]=>
  NULL
  ["customRepositoryClassName"]=>
  NULL
  ["isMappedSuperclass"]=>
  bool(false)
  ["parentClasses"]=>
  array(0) {
  }
  ["subClasses"]=>
  array(0) {
  }
  ["namedQueries"]=>
  array(0) {
  }
  ["namedNativeQueries"]=>
  array(0) {
  }
  ["sqlResultSetMappings"]=>
  array(0) {
  }
  ["identifier"]=>
  array(1) {
    [0]=>
    string(2) "id"
  }
  ["inheritanceType"]=>
  int(1)
  ["generatorType"]=>
  int(1)
  ["fieldMappings"]=>
  array(3) {
    ["id"]=>
    array(6) {
      ["id"]=>
      bool(true)
      ["fieldName"]=>
      string(2) "id"
      ["columnName"]=>
      string(2) "id"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["name"]=>
    array(6) {
      ["fieldName"]=>
      string(4) "name"
      ["columnName"]=>
      string(4) "name"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(255)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["description"]=>
    array(4) {
      ["fieldName"]=>
      string(11) "description"
      ["columnName"]=>
      string(11) "description"
      ["type"]=>
      string(4) "text"
      ["nullable"]=>
      bool(true)
    }
  }
  ["fieldNames"]=>
  array(3) {
    ["id"]=>
    string(2) "id"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["columnNames"]=>
  array(3) {
    ["id"]=>
    string(2) "id"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["discriminatorValue"]=>
  NULL
  ["discriminatorMap"]=>
  array(0) {
  }
  ["discriminatorColumn"]=>
  NULL
  ["table"]=>
  array(1) {
    ["name"]=>
    string(19) "ingredient_category"
  }
  ["lifecycleCallbacks"]=>
  array(0) {
  }
  ["associationMappings"]=>
  array(2) {
    ["ingredient"]=>
    array(19) {
      ["fieldName"]=>
      string(10) "ingredient"
      ["targetEntity"]=>
      string(10) "Ingredient"
      ["inversedBy"]=>
      string(18) "ingredientCategory"
      ["joinTable"]=>
      array(3) {
        ["name"]=>
        string(30) "ingredient_category_ingredient"
        ["joinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(22) "ingredient_category_id"
            ["referencedColumnName"]=>
            string(2) "id"
          }
        }
        ["inverseJoinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(13) "ingredient_id"
            ["referencedColumnName"]=>
            string(13) "ingredient_id"
          }
        }
      }
      ["type"]=>
      int(8)
      ["mappedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(18) "IngredientCategory"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["joinTableColumns"]=>
      array(2) {
        [0]=>
        string(22) "ingredient_category_id"
        [1]=>
        string(13) "ingredient_id"
      }
      ["relationToSourceKeyColumns"]=>
      array(1) {
        ["ingredient_category_id"]=>
        string(2) "id"
      }
      ["relationToTargetKeyColumns"]=>
      array(1) {
        ["ingredient_id"]=>
        string(13) "ingredient_id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
    ["parent"]=>
    array(19) {
      ["fieldName"]=>
      string(6) "parent"
      ["targetEntity"]=>
      string(18) "IngredientCategory"
      ["joinColumns"]=>
      array(1) {
        [0]=>
        array(2) {
          ["name"]=>
          string(9) "parent_id"
          ["referencedColumnName"]=>
          string(2) "id"
        }
      }
      ["type"]=>
      int(2)
      ["mappedBy"]=>
      NULL
      ["inversedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(18) "IngredientCategory"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["sourceToTargetKeyColumns"]=>
      array(1) {
        ["parent_id"]=>
        string(2) "id"
      }
      ["joinColumnFieldNames"]=>
      array(1) {
        ["parent_id"]=>
        string(9) "parent_id"
      }
      ["targetToSourceKeyColumns"]=>
      array(1) {
        ["id"]=>
        string(9) "parent_id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
  }
  ["isIdentifierComposite"]=>
  bool(false)
  ["containsForeignIdentifier"]=>
  bool(false)
  ["idGenerator"]=>
  NULL
  ["sequenceGeneratorDefinition"]=>
  NULL
  ["tableGeneratorDefinition"]=>
  NULL
  ["changeTrackingPolicy"]=>
  int(1)
  ["isVersioned"]=>
  NULL
  ["versionField"]=>
  NULL
  ["reflClass"]=>
  NULL
  ["isReadOnly"]=>
  bool(false)
  ["namingStrategy":protected]=>
  object(Doctrine\ORM\Mapping\DefaultNamingStrategy)#258 (0) {
  }
  ["reflFields"]=>
  array(0) {
  }
  ["_prototype":"Doctrine\ORM\Mapping\ClassMetadataInfo":private]=>
  NULL
}
object(Doctrine\ORM\Mapping\ClassMetadata)#474 (36) {
  ["name"]=>
  string(6) "Recipe"
  ["namespace"]=>
  string(0) ""
  ["rootEntityName"]=>
  string(6) "Recipe"
  ["customGeneratorDefinition"]=>
  NULL
  ["customRepositoryClassName"]=>
  NULL
  ["isMappedSuperclass"]=>
  bool(false)
  ["parentClasses"]=>
  array(0) {
  }
  ["subClasses"]=>
  array(0) {
  }
  ["namedQueries"]=>
  array(0) {
  }
  ["namedNativeQueries"]=>
  array(0) {
  }
  ["sqlResultSetMappings"]=>
  array(0) {
  }
  ["identifier"]=>
  array(1) {
    [0]=>
    string(8) "recipeId"
  }
  ["inheritanceType"]=>
  int(1)
  ["generatorType"]=>
  int(1)
  ["fieldMappings"]=>
  array(3) {
    ["recipeId"]=>
    array(6) {
      ["id"]=>
      bool(true)
      ["fieldName"]=>
      string(8) "recipeId"
      ["columnName"]=>
      string(9) "recipe_id"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["name"]=>
    array(6) {
      ["fieldName"]=>
      string(4) "name"
      ["columnName"]=>
      string(4) "name"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(255)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["description"]=>
    array(4) {
      ["fieldName"]=>
      string(11) "description"
      ["columnName"]=>
      string(11) "description"
      ["type"]=>
      string(4) "text"
      ["nullable"]=>
      bool(true)
    }
  }
  ["fieldNames"]=>
  array(3) {
    ["recipe_id"]=>
    string(8) "recipeId"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["columnNames"]=>
  array(3) {
    ["recipeId"]=>
    string(9) "recipe_id"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["discriminatorValue"]=>
  NULL
  ["discriminatorMap"]=>
  array(0) {
  }
  ["discriminatorColumn"]=>
  NULL
  ["table"]=>
  array(1) {
    ["name"]=>
    string(6) "recipe"
  }
  ["lifecycleCallbacks"]=>
  array(0) {
  }
  ["associationMappings"]=>
  array(2) {
    ["recipeCategory"]=>
    array(15) {
      ["fieldName"]=>
      string(14) "recipeCategory"
      ["targetEntity"]=>
      string(14) "RecipeCategory"
      ["mappedBy"]=>
      string(6) "recipe"
      ["type"]=>
      int(8)
      ["inversedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(false)
      ["sourceEntity"]=>
      string(6) "Recipe"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["orphanRemoval"]=>
      bool(false)
    }
    ["users"]=>
    array(19) {
      ["fieldName"]=>
      string(5) "users"
      ["targetEntity"]=>
      string(5) "Users"
      ["inversedBy"]=>
      string(12) "recipeRecipe"
      ["joinTable"]=>
      array(3) {
        ["name"]=>
        string(13) "users_recipes"
        ["joinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(16) "recipe_recipe_id"
            ["referencedColumnName"]=>
            string(9) "recipe_id"
          }
        }
        ["inverseJoinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(8) "users_id"
            ["referencedColumnName"]=>
            string(2) "id"
          }
        }
      }
      ["type"]=>
      int(8)
      ["mappedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(6) "Recipe"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["joinTableColumns"]=>
      array(2) {
        [0]=>
        string(16) "recipe_recipe_id"
        [1]=>
        string(8) "users_id"
      }
      ["relationToSourceKeyColumns"]=>
      array(1) {
        ["recipe_recipe_id"]=>
        string(9) "recipe_id"
      }
      ["relationToTargetKeyColumns"]=>
      array(1) {
        ["users_id"]=>
        string(2) "id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
  }
  ["isIdentifierComposite"]=>
  bool(false)
  ["containsForeignIdentifier"]=>
  bool(false)
  ["idGenerator"]=>
  NULL
  ["sequenceGeneratorDefinition"]=>
  NULL
  ["tableGeneratorDefinition"]=>
  NULL
  ["changeTrackingPolicy"]=>
  int(1)
  ["isVersioned"]=>
  NULL
  ["versionField"]=>
  NULL
  ["reflClass"]=>
  NULL
  ["isReadOnly"]=>
  bool(false)
  ["namingStrategy":protected]=>
  object(Doctrine\ORM\Mapping\DefaultNamingStrategy)#258 (0) {
  }
  ["reflFields"]=>
  array(0) {
  }
  ["_prototype":"Doctrine\ORM\Mapping\ClassMetadataInfo":private]=>
  NULL
}
object(Doctrine\ORM\Mapping\ClassMetadata)#476 (36) {
  ["name"]=>
  string(14) "RecipeCategory"
  ["namespace"]=>
  string(0) ""
  ["rootEntityName"]=>
  string(14) "RecipeCategory"
  ["customGeneratorDefinition"]=>
  NULL
  ["customRepositoryClassName"]=>
  NULL
  ["isMappedSuperclass"]=>
  bool(false)
  ["parentClasses"]=>
  array(0) {
  }
  ["subClasses"]=>
  array(0) {
  }
  ["namedQueries"]=>
  array(0) {
  }
  ["namedNativeQueries"]=>
  array(0) {
  }
  ["sqlResultSetMappings"]=>
  array(0) {
  }
  ["identifier"]=>
  array(1) {
    [0]=>
    string(2) "id"
  }
  ["inheritanceType"]=>
  int(1)
  ["generatorType"]=>
  int(1)
  ["fieldMappings"]=>
  array(3) {
    ["id"]=>
    array(6) {
      ["id"]=>
      bool(true)
      ["fieldName"]=>
      string(2) "id"
      ["columnName"]=>
      string(2) "id"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["name"]=>
    array(6) {
      ["fieldName"]=>
      string(4) "name"
      ["columnName"]=>
      string(4) "name"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(45)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["description"]=>
    array(6) {
      ["fieldName"]=>
      string(11) "description"
      ["columnName"]=>
      string(11) "description"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(45)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(true)
    }
  }
  ["fieldNames"]=>
  array(3) {
    ["id"]=>
    string(2) "id"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["columnNames"]=>
  array(3) {
    ["id"]=>
    string(2) "id"
    ["name"]=>
    string(4) "name"
    ["description"]=>
    string(11) "description"
  }
  ["discriminatorValue"]=>
  NULL
  ["discriminatorMap"]=>
  array(0) {
  }
  ["discriminatorColumn"]=>
  NULL
  ["table"]=>
  array(1) {
    ["name"]=>
    string(15) "recipe_category"
  }
  ["lifecycleCallbacks"]=>
  array(0) {
  }
  ["associationMappings"]=>
  array(2) {
    ["recipe"]=>
    array(19) {
      ["fieldName"]=>
      string(6) "recipe"
      ["targetEntity"]=>
      string(6) "Recipe"
      ["inversedBy"]=>
      string(14) "recipeCategory"
      ["joinTable"]=>
      array(3) {
        ["name"]=>
        string(22) "recipe_category_recipe"
        ["joinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(18) "recipe_category_id"
            ["referencedColumnName"]=>
            string(2) "id"
          }
        }
        ["inverseJoinColumns"]=>
        array(1) {
          [0]=>
          array(2) {
            ["name"]=>
            string(9) "recipe_id"
            ["referencedColumnName"]=>
            string(9) "recipe_id"
          }
        }
      }
      ["type"]=>
      int(8)
      ["mappedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(14) "RecipeCategory"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["joinTableColumns"]=>
      array(2) {
        [0]=>
        string(18) "recipe_category_id"
        [1]=>
        string(9) "recipe_id"
      }
      ["relationToSourceKeyColumns"]=>
      array(1) {
        ["recipe_category_id"]=>
        string(2) "id"
      }
      ["relationToTargetKeyColumns"]=>
      array(1) {
        ["recipe_id"]=>
        string(9) "recipe_id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
    ["parent"]=>
    array(19) {
      ["fieldName"]=>
      string(6) "parent"
      ["targetEntity"]=>
      string(14) "RecipeCategory"
      ["joinColumns"]=>
      array(1) {
        [0]=>
        array(2) {
          ["name"]=>
          string(9) "parent_id"
          ["referencedColumnName"]=>
          string(2) "id"
        }
      }
      ["type"]=>
      int(2)
      ["mappedBy"]=>
      NULL
      ["inversedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(14) "RecipeCategory"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["sourceToTargetKeyColumns"]=>
      array(1) {
        ["parent_id"]=>
        string(2) "id"
      }
      ["joinColumnFieldNames"]=>
      array(1) {
        ["parent_id"]=>
        string(9) "parent_id"
      }
      ["targetToSourceKeyColumns"]=>
      array(1) {
        ["id"]=>
        string(9) "parent_id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
  }
  ["isIdentifierComposite"]=>
  bool(false)
  ["containsForeignIdentifier"]=>
  bool(false)
  ["idGenerator"]=>
  NULL
  ["sequenceGeneratorDefinition"]=>
  NULL
  ["tableGeneratorDefinition"]=>
  NULL
  ["changeTrackingPolicy"]=>
  int(1)
  ["isVersioned"]=>
  NULL
  ["versionField"]=>
  NULL
  ["reflClass"]=>
  NULL
  ["isReadOnly"]=>
  bool(false)
  ["namingStrategy":protected]=>
  object(Doctrine\ORM\Mapping\DefaultNamingStrategy)#258 (0) {
  }
  ["reflFields"]=>
  array(0) {
  }
  ["_prototype":"Doctrine\ORM\Mapping\ClassMetadataInfo":private]=>
  NULL
}
object(Doctrine\ORM\Mapping\ClassMetadata)#478 (36) {
  ["name"]=>
  string(4) "Step"
  ["namespace"]=>
  string(0) ""
  ["rootEntityName"]=>
  string(4) "Step"
  ["customGeneratorDefinition"]=>
  NULL
  ["customRepositoryClassName"]=>
  NULL
  ["isMappedSuperclass"]=>
  bool(false)
  ["parentClasses"]=>
  array(0) {
  }
  ["subClasses"]=>
  array(0) {
  }
  ["namedQueries"]=>
  array(0) {
  }
  ["namedNativeQueries"]=>
  array(0) {
  }
  ["sqlResultSetMappings"]=>
  array(0) {
  }
  ["identifier"]=>
  array(2) {
    [0]=>
    string(10) "stepNumber"
    [1]=>
    string(6) "recipe"
  }
  ["inheritanceType"]=>
  int(1)
  ["generatorType"]=>
  int(1)
  ["fieldMappings"]=>
  array(4) {
    ["stepNumber"]=>
    array(6) {
      ["id"]=>
      bool(true)
      ["fieldName"]=>
      string(10) "stepNumber"
      ["columnName"]=>
      string(11) "step_number"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(false)
    }
    ["description"]=>
    array(4) {
      ["fieldName"]=>
      string(11) "description"
      ["columnName"]=>
      string(11) "description"
      ["type"]=>
      string(4) "text"
      ["nullable"]=>
      bool(true)
    }
    ["timer"]=>
    array(5) {
      ["fieldName"]=>
      string(5) "timer"
      ["columnName"]=>
      string(5) "timer"
      ["type"]=>
      string(7) "integer"
      ["unsigned"]=>
      bool(false)
      ["nullable"]=>
      bool(true)
    }
    ["image"]=>
    array(6) {
      ["fieldName"]=>
      string(5) "image"
      ["columnName"]=>
      string(5) "image"
      ["type"]=>
      string(6) "string"
      ["length"]=>
      int(100)
      ["fixed"]=>
      bool(false)
      ["nullable"]=>
      bool(true)
    }
  }
  ["fieldNames"]=>
  array(4) {
    ["step_number"]=>
    string(10) "stepNumber"
    ["description"]=>
    string(11) "description"
    ["timer"]=>
    string(5) "timer"
    ["image"]=>
    string(5) "image"
  }
  ["columnNames"]=>
  array(4) {
    ["stepNumber"]=>
    string(11) "step_number"
    ["description"]=>
    string(11) "description"
    ["timer"]=>
    string(5) "timer"
    ["image"]=>
    string(5) "image"
  }
  ["discriminatorValue"]=>
  NULL
  ["discriminatorMap"]=>
  array(0) {
  }
  ["discriminatorColumn"]=>
  NULL
  ["table"]=>
  array(1) {
    ["name"]=>
    string(4) "step"
  }
  ["lifecycleCallbacks"]=>
  array(0) {
  }
  ["associationMappings"]=>
  array(1) {
    ["recipe"]=>
    array(20) {
      ["fieldName"]=>
      string(6) "recipe"
      ["targetEntity"]=>
      string(6) "Recipe"
      ["id"]=>
      bool(true)
      ["joinColumns"]=>
      array(1) {
        [0]=>
        array(2) {
          ["name"]=>
          string(9) "recipe_id"
          ["referencedColumnName"]=>
          string(9) "recipe_id"
        }
      }
      ["type"]=>
      int(1)
      ["mappedBy"]=>
      NULL
      ["inversedBy"]=>
      NULL
      ["isOwningSide"]=>
      bool(true)
      ["sourceEntity"]=>
      string(4) "Step"
      ["fetch"]=>
      int(2)
      ["cascade"]=>
      array(0) {
      }
      ["isCascadeRemove"]=>
      bool(false)
      ["isCascadePersist"]=>
      bool(false)
      ["isCascadeRefresh"]=>
      bool(false)
      ["isCascadeMerge"]=>
      bool(false)
      ["isCascadeDetach"]=>
      bool(false)
      ["sourceToTargetKeyColumns"]=>
      array(1) {
        ["recipe_id"]=>
        string(9) "recipe_id"
      }
      ["joinColumnFieldNames"]=>
      array(1) {
        ["recipe_id"]=>
        string(9) "recipe_id"
      }
      ["targetToSourceKeyColumns"]=>
      array(1) {
        ["recipe_id"]=>
        string(9) "recipe_id"
      }
      ["orphanRemoval"]=>
      bool(false)
    }
  }
  ["isIdentifierComposite"]=>
  bool(true)
  ["containsForeignIdentifier"]=>
  bool(true)
  ["idGenerator"]=>
  NULL
  ["sequenceGeneratorDefinition"]=>
  NULL
  ["tableGeneratorDefinition"]=>
  NULL
  ["changeTrackingPolicy"]=>
  int(1)
  ["isVersioned"]=>
  NULL
  ["versionField"]=>
  NULL
  ["reflClass"]=>
  NULL
  ["isReadOnly"]=>
  bool(false)
  ["namingStrategy":protected]=>
  object(Doctrine\ORM\Mapping\DefaultNamingStrategy)#258 (0) {
  }
  ["reflFields"]=>
  array(0) {
  }
  ["_prototype":"Doctrine\ORM\Mapping\ClassMetadataInfo":private]=>
  NULL
}
Comment by Benjamin Eberlei [ 09/May/13 ]

Fixed and merged back to 2.3

Comment by Nicholas Van Dusen [ 29/May/13 ]

I tested this again using 2.3.4 (the version which contains this fix) and it is still occurring. Attempting to import mapping for a table with 2 foreign keys in the primary key results in the error "Database does not have any mapping information." Adding a third column on the primary key "fixes" the issue.

Currently our developers are being asked to add a fake third part to the key to work around the issue, then delete that key once they get into the entity class. This is a bit tedious and I'd love to see a fix!

Comment by Nicholas Van Dusen [ 29/May/13 ]

Issue still present in 2.3.4 and 2.4.0-RC1

Comment by Guillermo [ 11/Jun/13 ]

The database engine I use is PostgreSQL. I'm having problems when mapping entities with composite primary keys in other tables.

CREATE TABLE public.establecimiento
    (
      id_establecimiento integer NOT NULL,
      establecimiento character varying(100) NOT NULL,
      CONSTRAINT pk_establecimiento PRIMARY KEY (id_establecimiento )
    )
    WITH (
      OIDS=FALSE
    );
    CREATE TABLE public.establecimiento_sec
    (
      id_establecimiento_sec integer NOT NULL,
      id_establecimiento integer NOT NULL,
      det_seccion character varying(40) NOT NULL,
      plano character varying(100),
      sector_ingreso character varying(254),
      sponsor_imagen_sec character varying(96000),
      CONSTRAINT pk_establecimientos_sec PRIMARY KEY (id_establecimiento_sec , id_establecimiento ),
      CONSTRAINT fk_establec_reference_establec FOREIGN KEY (id_establecimiento)
          REFERENCES public.establecimiento (id_establecimiento) MATCH SIMPLE
          ON UPDATE RESTRICT ON DELETE RESTRICT
    )
    WITH (
      OIDS=TRUE
    );
    CREATE TABLE public.establecimiento_sec_plano
    (
      id_establecimiento_sec_plano integer NOT NULL,
      id_establecimiento_sec integer NOT NULL,
      id_establecimiento integer NOT NULL,
      det_plano character varying(512),
      cantidad integer NOT NULL,
      precio double precision,
      insert_charge double precision DEFAULT 0,
      descr character varying(254),
      CONSTRAINT pk_establecimiento_sec_plano PRIMARY KEY (id_establecimiento_sec_plano , id_establecimiento_sec , id_establecimiento ),
      CONSTRAINT fk_establecimiento_sec FOREIGN KEY (id_establecimiento, id_establecimiento_sec)
          REFERENCES public.establecimiento_sec (id_establecimiento, id_establecimiento_sec) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE CASCADE
    )
    WITH (
      OIDS=FALSE
    );

Defining the entity establecimientoSecPlano, $establecimientoSec variable containing the keys $establecimiento and $id_establecimiento_sec

//Entity/EstablecimientosSecPlano

/**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Ticketway\PruebaBundle\Entity\EstablecimientosSec")
     * @ORM\JoinColumns(
     *      @ORM\JoinColumn(name="id_establecimiento_sec", referencedColumnName="id_establecimiento_sec"),
     *      @ORM\JoinColumn(name="id_establecimiento", referencedColumnName="id_establecimiento")) 
     */
    private $establecimientoSec;

//Entity/EstablecimientosSec

/**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Ticketway\PruebaBundle\Entity\Establecimientos")
     * @ORM\JoinColumn(name="id_establecimiento", referencedColumnName="id_establecimiento") 
     */
    private $establecimiento;

When executing the command doctrine: mapping: import I get the following error

[Doctrine\ORM\Mapping\MappingException]
It is not possible to map entity 'EstablecimientoSec' with a composite primary key as part of the
primary key of another entity 'EstablecimientoSecPlano#idEstablecimiento'.

Comment by Sam Van der Borght [ 28/Jun/13 ]

We are having the same problem as Guillermo. Any idea on how to fix this issue?
Does anybody know why this problem occurs? I have been looking in the code but havent had much success.

Comment by Diego Antunes [ 01/Jul/13 ]

Doesn't work for me either, composite keys aren't working in 2.3.4

Can we get a fix for this?





[DDC-2363] Duplicated record with orphanRemoval and proxy Created: 22/Mar/13  Updated: 27/Mar/14

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

Type: Bug Priority: Major
Reporter: Manuele Menozzi Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: orphanRemoval, proxy
Environment:

Tested both Mac OS X and Ubuntu


Issue Links:
Duplicate
is duplicated by DDC-2364 [GH-625] [DDC-2363] Duplicated record... Open

 Description   

There is a problem that causes duplicate records are created when EntityManager has to remove an entity due to orphanRemoval. The problem occurs only with a double flush and referred object is a proxy.

I'm trying to submit a pull request for this ticket. Please, stand by.



 Comments   
Comment by Marco Pivetta [ 27/Mar/14 ]

Hey Manuele Menozzi, do you remember if this has been fixed? Are you still able to reproduce the problem?

Comment by Manuele Menozzi [ 27/Mar/14 ]

Hi Marco,
I made a PR (https://github.com/doctrine/doctrine2/pull/625) with an automated test that reproduce the problem. I just ran this test over latest version of doctrine2 and it's still failed. So, the problem has not be fixed and I (or you) can reproduce it easily.

Let me know if I can help you somehow.

Comment by Marco Pivetta [ 27/Mar/14 ]

Thanks, didn't see it!





[DDC-2193] Named native query bug? Created: 11/Dec/12  Updated: 31/Dec/12

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

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

Attachments: File DDC2193Test.php    

 Description   

@NamedNativeQueries is a useful thing, but I have found some problems during my using.
1、Normal

 /**
 * @NamedNativeQueries({
 *      @NamedNativeQuery(
 *          name            = "fetchMultipleJoinsEntityResults",
 *          resultSetMapping= "mappingMultipleJoinsEntityResults",
 *          query            = "SELECT * FROM test "
 *      )
 * })
 */

2、Error,cannot connect to the server

 /**
 * @NamedNativeQueries({
 *      @NamedNativeQuery(
 *          name            = "fetchMultipleJoinsEntityResults",
 *          resultSetMapping= "mappingMultipleJoinsEntityResults",
 *          query            = "SELECT * 
            FROM test "
 *      )
 * })
 */

3、Cannot use alias.The same problem as the second one.

.......
 query            = "SELECT a as test FROM test "


 Comments   
Comment by Fabio B. Silva [ 12/Dec/12 ]

Hi

Doctrine does not change the native query at all
The problem seems related with database connection.

Could you provide more details please?

Cheers

Comment by dingdangjyz [ 13/Dec/12 ]

Doctrine\Common\Lexer.php

Hello, after checking, I found the problem should be here. As long as SQL wrap, or fill in alias, it will be error. It seems to be the preg_split problem?

        $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
        $matches = preg_split($regex, $input, -1, $flags);

        foreach ($matches as $match) {
            // Must remain before 'value' assignment since it can change content
            $type = $this->getType($match[0]);

            $this->tokens[] = array(
                'value' => $match[0],
                'type'  => $type,
                'position' => $match[1],
            );
Comment by Fabio B. Silva [ 13/Dec/12 ]

Hi

Could you try to add a failing test case please ?

Cheers

Comment by dingdangjyz [ 14/Dec/12 ]

xp php5.3.8 Apache

<?php

namespace Models\Entities;

/**
 * @Entity
 * @Table
 *
 * @NamedNativeQueries({
 *      @NamedNativeQuery(
 *          name             = "find-hotel-item",
 *          resultSetMapping = "mapping-find-item",
 *          query            = "SELECT Top 1 VEI_SN AS SN 
            FROM tourmanager.dbo.VEndorInfo vi 
INNER JOIN tourmanager.dbo.VEndorInfo2 vi2 ON 
vi.VEI_SN = vi2.VEI2_VEI_SN LEFT OUTER JOIN tourmanager.dbo.HotelInfo hi 
ON hi.hotelid = vi2.VEI2_VEI_SN INNER JOIN tourmanager.dbo.HotelInfo2 
hi2 ON hi2.hotelid = vi2.VEI2_VEI_SN AND hi2.LGC = 1 "
 *      )
 * })
 *
 * @SqlResultSetMappings({
 *      @SqlResultSetMapping(
 *          name    = "mapping-find-item",
 *          entities= {
 *              @EntityResult(
 *                  entityClass = "HTHotelItem",
 *                  fields = {
 *                      @FieldResult(name = "id",   column="SN")
 *                  }
 *              )
 *          }
 *      )
 * })
 *
 */

class HTHotelItem{
    /** @Id @Column(type="integer") @GeneratedValue */
    protected $id;
        
    /** @name */
    protected $name;
    
    /** @city */
    protected $city;
    
    /** @url */
    protected $url;
    
    public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata){
        $metadata->addNamedNativeQuery(array(
            'name'              => 'find-hotel-item',
            'query'             => 'SELECT h FROM HTHotelItem h',
            'resultSetMapping'  => '\\Models\\Entities\\HTHotelItem'
        ));
    }
    
    function getId(){
        return $this->id;
    }
    
    function getName(){
        return $this->name;
    }
    
    function getCity(){
        return $this->city;
    }
    
    function getUrl(){
        return $this->url;
    }
}
Comment by dingdangjyz [ 14/Dec/12 ]

@NamedNativeQueries query

If we write the long SQL, it will be fault. NO error massage.
1251 charecter must be wrong.
I still insist it is the problem of preg_split in
Doctrine\Common\Lexer.php

Comment by Fabio B. Silva [ 16/Dec/12 ]

Can't reproduce,

Could you try to change the attached test case and make it fail.

Cheers

Comment by Benjamin Eberlei [ 24/Dec/12 ]

The Doctrine\Common\Lexer is never used in combination with native queries, only with the Annotation Parser, so i cannot be the preg_split that causes your SQL to be broken. Or do you get annotation errors?

Also what database are you using? maybe its related to the DBAL sql parsing?

Comment by dingdangjyz [ 31/Dec/12 ]

I'm sorry my English is too bad.

I think it's Doctrine \ is \ Lexer. PHP preg_split the function of the problem in this file.
My system environment is xp/apache 5.3 + / php_pdo_sqlsrv_53 / mssql2000





[DDC-2184] [GH-530] Singular form of generated methods should end with 'y' when property ends with 'ies' Created: 04/Dec/12  Updated: 05/Mar/14

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

Type: Improvement Priority: Major
Reporter: Benjamin Eberlei Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 1
Labels: None

Issue Links:
Duplicate
duplicates DDC-2150 EntityGenerator.php - Guessing singul... Resolved
duplicates DDC-2160 [GH-520] Fix for Doctrine\ORM\Tools\E... Resolved

 Description   

In Doctrine 2.3 the 'add' and 'remove' methods in oneToMany associations have another problem (in earlier versions like 2.2 this worked correct). The singular form is not correctly detected if the property ends with 'ies' like 'entries' which should be transformed to 'entry'.
I have this YAML definition:

Archive:
  type: entity
  fields:
    id:
      id: true
      type: integer
      unsigned: false
      nullable: false
      generator:
        strategy: IDENTITY
  oneToMany:
    entries:
      targetEntity: Entry
      mappedBy: archive

This generates these methods:

public function addEntrie(\Entry $entries) { ... }
public function removeEntrie(\Entry $entries) { ... }

Because in the EntityGenerator only the plural 's' is removed. It would be nice if an ending of 'ies' could be replaced by 'y'. So that we get these methods

public function addEntry(\Entry $entries) { ... }
public function removeEntry(\Entry $entries) { ... }

My fork already has the changes https://github.com/naitsirch/doctrine-orm2/commit/a3adfccb4927d61da7debae46ed0fff61e4212f8
I have opened a pull request here https://github.com/doctrine/doctrine2/pull/530



 Comments   
Comment by Christian Stoller [ 04/Dec/12 ]

Sorry, I accidently clicked on the button 'Request Feedback'
Now the status has changed to 'Awaiting Feedback'

Comment by Benjamin Eberlei [ 06/Jan/13 ]

Mark as improvement

Comment by Ed Page Croft [ 04/Jul/13 ]

Is this issue going to be resolved? It's a major problem for our project - a stock market application that uses properties like 'securities' and entities of name 'Security'.

Comment by Doctrine Bot [ 05/Mar/14 ]

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





[DDC-2147] Custom annotation in MappedSuperclass Created: 15/Nov/12  Updated: 07/May/13

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers, ORM
Affects Version/s: 2.2.1
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: kluk Assignee: Marco Pivetta
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Linux 3.6.6-1.fc17.x86_64


Attachments: Text File error.log    

 Description   

When you try use custom annotation in mappedsuperclass like here http://pastebin.com/YMxKvcLk and then i try get metadata for class i get this error
Undefined index: fieldName
ClassMetadataInfo.php function addInheritedFieldMapping
Problem is that custom annotation doesnt have fieldName.
Quick fix is add condition to test if fieldName isset.



 Comments   
Comment by kluk [ 15/Nov/12 ]

error log from orm:validate-schema

Comment by Marco Pivetta [ 23/Jan/13 ]

Copying from pastebin:

use \Doctrine\ORM\Mapping as ORM;
use \xxx\Doctrine\Annotation\Entity as re;
use \xxx\Doctrine\Annotation\Forms as rf;
use \Doctrine\Common\Collections;
 
/**
 * @ORM\Entity
 */
class EventPicture extends \Picture
{
 
    /**
     * @ORM\ManyToOne(targetEntity="Event", inversedBy="eventPicture")
     * @ORM\JoinColumn(name="FK_Event", referencedColumnName="id")
     */
    protected $event;
 
}
use \Doctrine\ORM\Mapping as ORM;
use \xxx\Doctrine\Annotation\Entity as re;
use \xxx\Doctrine\Annotation\Forms as rf;
use \Doctrine\Common\Collections;
 
/** @ORM\MappedSuperclass */
class Picture extends \xxx\Doctrine\Entity\BaseEntity
{
 
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @var type
     */
    protected $id;
 
    /**
     * @ORM\Column(type="string",unique=true, nullable=false)
     *  @rf\FileUpload(fileSize="php",uploadType="local",fieldName="link",formControl="FileUploadField",image=true)
     *
     */
    protected $link;
 
}

kluk does this happen also with any other simple custom annotation? For example following:

/**
 * @Annotation 
 * @Target({"PROPERTY","ANNOTATION"})
 */
final class Entity implements Annotation
{
    /**
     * @var string
     */
    public $value;
}
Comment by kluk [ 30/Jan/13 ]

the same error when using simple annotation.

 
<?php

use \Doctrine\ORM\Mapping as ORM;
use \xxx\Doctrine\Annotation\Entity as re;
use \xxx\Doctrine\Annotation\Forms as rf;
use \Doctrine\Common\Collections;

/** @ORM\MappedSuperclass */
class Picture extends \xxx\Doctrine\Entity\BaseEntity {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @var type
     */
    protected $id;

   
    /**
     * @ORM\Column(type="integer")
     * @rf\SetClass({"class","hide"})
     */
    public $value;

    /**
     * @ORM\Column(type="string",unique=true, nullable=true)
     * @rf\FileUpload(fileSize="php",uploadType="local",fieldName="link",formControl="FileUploadField",image=true)
     *
     */
    protected $link;

}

When i remove $value , $picture from class everything goes ok.
Easy fix for me is change ClassMetadataInfo.

    /**
     * INTERNAL:
     * Adds a field mapping without completing/validating it.
     * This is mainly used to add inherited field mappings to derived classes.
     *
     * @param array $fieldMapping
     *
     * @return void
     */
    public function addInheritedFieldMapping(array $fieldMapping)
    {
        if(isset($fieldMapping['fieldName'])){
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
        $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
        }
    }

But i dont know if this fix can break another part of doctrine.

Comment by Benjamin Eberlei [ 04/May/13 ]

Can you put the code of your annotations online? I can't seem to understand why this happens.

Comment by kluk [ 07/May/13 ]
Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
 
namespace libs\Doctrine\Annotation\Entity;
use Doctrine\Common\Annotations\Annotation;

/** @Annotation */
class CustomMapping extends Annotation
{
    /**
     *
     * @var string
     */
    public $className;
    /**
     * 
     * 
     * @var IQueryable| string
     */
    public $dataSource;
}




[DDC-1986] findBy hydration with limit and offset with Oracle database (oci8 driver) Created: 17/Aug/12  Updated: 08/Jan/13

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

Type: Bug Priority: Major
Reporter: Benjamin Grandfond Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 1
Labels: oracle
Environment:

composer.json require :

"php": ">=5.3.3",
"symfony/symfony": "2.1.*",
"doctrine/orm": ">=2.2.3,<2.4-dev",
"doctrine/doctrine-bundle": "dev-master",
"twig/extensions": "dev-master",
"symfony/assetic-bundle": "dev-master",
"symfony/swiftmailer-bundle": "dev-master",
"symfony/monolog-bundle": "dev-master",
"sensio/distribution-bundle": "dev-master",
"sensio/framework-extra-bundle": "dev-master",
"sensio/generator-bundle": "dev-master",
"jms/security-extra-bundle": "1.2.*",
"jms/di-extra-bundle": "1.1.*",
"twitter/bootstrap": "master",
"friendsofsymfony/rest-bundle": "dev-master",
"doctrine/doctrine-fixtures-bundle": "dev-master"



 Description   

I tried to use the findBy method with limit and offset parameters against an Oracle database using oci8 driver.

The query seems to executed successfully but the hydrator fails when hydrating data as there is a DOCTRINE_ROWNUM column appending the "limit" clause.

Here is the exception thrown : "Notice: Undefined index: DOCTRINE_ROWNUM in [...]/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php line 183"

I was thinking about something like this to fix this issue :

  • add an attribute (platformExtraColumns) to the platform class, storing every column added by methods like doModifyLimitQuery
  • check in hydrator method hydrateRowData if the column exists among the extra columns attribute of the custom platform
  • don't use the column if true

Maybe there is a better approach, what are your thoughts?



 Comments   
Comment by Benjamin Grandfond [ 17/Aug/12 ]

I implemented it in my forks :

https://github.com/benja-M-1/doctrine2/commit/c8d899b14446accf869ddc0043f4235284375755
https://github.com/benja-M-1/dbal/commit/b9423c8d46a2bcdaa5a1f0b26a9a28259b1e44a2

It works for me, but I didn't write unit tests.

Comment by Benjamin Grandfond [ 24/Aug/12 ]

Hi,

Did you have time to have a look at this issue?

Thanks

Comment by Christophe Coevoet [ 24/Aug/12 ]

Please send a pull request when you submit a fix. It is the proper way to submit them for review. When we want to see things waiting for review, we look at the list of pending PRs, not at all comments of the issue tracker to find links in them.

And I can tell you that this change has a big issue: it introduces a state in the database platform whereas it is currently stateless. This is likely to cause some issues when using more than 1 query (which is a common use case).

Comment by Benjamin Grandfond [ 29/Aug/12 ]

Hi Christophe thank you for your feedback.

I didn't send a PR because I wanted someone sharing his thoughts about what I suggested in this current issue. However I don't really understand the stateless argument, can you explain a bit more?

Otherwise how would do you proceed to tell Doctrine not to hydrate platform-specific columns?

Comment by Christophe Coevoet [ 29/Aug/12 ]

If you run several queries, they will be affected by the extra columns of previous requests, which is wrong

Comment by Benjamin Eberlei [ 29/Aug/12 ]

I think the ObjectHydrator catches this by skipping undefined columns, i think we might just have overoptimized the SimpleObjectHydrator a little bit.





[DDC-1879] Orphans are neither nulled nor removed when merging a graph of detached entities Created: 18/Jun/12  Updated: 23/Jan/13

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

Type: Bug Priority: Major
Reporter: Philippe Van Eerdenbrugghe Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Doctrine 2.2.2
PHP 5.3.10 with Suhosin-Patch
mysql Ver 14.14 Distrib 5.5.15, for osx10.7
Mac OS X 10.7 Lion



 Description   

When merging a graph of detached entities, the created entitied are created and the updated entities are updated but the non-present entities (which exist in the database but are not in the graph) are neither removed nor have them their association column nullified.

Example :

In my code I have 2 entities : Parent and Child. There is a OneToMany(cascade=

{"all"}

, orphanRemoval=true) relation defined in Parent.

In my database I have a Parent row with an id of 1, which has 3 Children with ids 1,2,3.

When I write the following code, I expect the Parent with id 1 and the Child with id 2 to be updated, a new Child to be created and the Child with id 1 and 3 to be deleted.

$parent = new Parent(); $parent->id = 1  // detached entity
$existing_child = new Child(); $child->id = 2 // detached entity
$new_child = new Child(); // new entity
$dinner->addChild($existing_child);
$dinner->addChild($new_child);

$em->merge($dinner);

$em->flush();

The objects I expect to be created and updated have the correct behaviour but the old children are not touched, they are still present in the database.



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

I don't think this is valid. Orphan removal scheduling is handled only when an unit of work is available.

What's the state of `$dinner` before your example? Can you `var_dump` it?





[DDC-1803] Paginator usage with a DQL query that is using 2 time the same named binded value failed Created: 30/Apr/12  Updated: 25/Jan/13

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

Type: Bug Priority: Major
Reporter: Marc Drolet Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

linux, oracle



 Description   

I use a dql query where I bind a named parameter 2 time in the same query for different joined fields. The query work but the count query failed saying that there are missing bind variable.

ex:
$qb = $this->getQueryBuilder()
->select('
partial fl.

{id, title, listing_date, abstract}

,
partial fla.

{id},
partial ca.{id}

,
partial ds.

{id}

')
->from('Fo_Listing', 'fl')
->join('fl.listing_properties', 'flp')
->join('flp.property', 'fp')
->leftjoin('fl.listing_assets', 'fla')
->leftjoin('fla.asset', 'ca')
->leftjoin('ca.ds', 'ds')
->where('fp.id = :propertyId')
->setParameter('propertyId', $id)
->andWhere('fl.object_status_id <> :deleted')
->setParameter('deleted', CoRefObjectStatus::DELETE)
->andWhere('fl.publishing_status_id = :published')
->setParameter('published', CoRefPublishingStatus::PUBLISHED)
->andWhere('fp.object_status_id <> :deleted')
->setParameter('deleted', CoRefObjectStatus::DELETE)
->andWhere('fp.publishing_status_id = :published')
->setParameter('published', CoRefPublishingStatus::PUBLISHED)
->add('orderBy', 'fl.listing_date DESC, fl.published_date DESC')
->setMaxResults($onTheMarketLimit);

$onTheMarket = new Paginator($qb, $fetchJoin = true);

To make it work, I've renamed the second usage of the named variable with a 2 at the end. deleted2 and published2.



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

This seems to be quite old. Marc Drolet is it still valid with the latest ORM?

Comment by Marc Drolet [ 25/Jan/13 ]

I'll try to test this problem on an updated version and I'll let you know.
The bug entry is also quite old and I've a local modified version of the paginator here to make it work with oracle, so it can take some time before I can test this out on the current doctrine version.

Comment by Marco Pivetta [ 25/Jan/13 ]

Ok, marking as awaiting feedback





[DDC-1721] LIKE clausule should accept functions on the pattern Created: 21/Mar/12  Updated: 01/Apr/14

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

Type: Improvement Priority: Major
Reporter: Ignacio Larranaga Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 2
Labels: querybuilder,, sql-walker

Attachments: Text File Parser.patch     Text File SqlWalker.patch    

 Description   

Example:
SELECT .... WHERE upper(n.title) LIKE upper(:filter)

should be a valid SQL, now is rejected because the walker only accept a variable or an string expression.

I'm adding a patch to address this.



 Comments   
Comment by Ignacio Larranaga [ 21/Mar/12 ]

Sorry the Parser has to be modified also to allow expressions to be recognized, I'm attaching the necessary patch.

Comment by Benjamin Eberlei [ 22/Mar/12 ]

I am sure there is a reason why the walker doesn't accept this such as not all supported vendors allowing functions in right hand side LIKE expressions, but i am not sure about this.

Comment by Glen Ainscow [ 03/Oct/12 ]

This is not possible either:

WHERE CASE WHEN p.name IS NULL THEN u.username ELSE p.name END LIKE :name

Comment by Thomas Mayer [ 24/Jan/13 ]

In my case it worked when using "=" instead of "LIKE".

//works:
(CASE WHEN (Book.id = BookFrom.id) THEN BookTo.displayName ELSE BookFrom.displayName END) = :name

//[Syntax Error] line 0, col 1217: Error: Expected =, <, <=, <>, >, >=, !=, got 'LIKE'
(CASE WHEN (Book.id = BookFrom.id) THEN BookTo.displayName ELSE BookFrom.displayName END) LIKE :name

So the LIKE operator only needs to be allowed here.

I'm wondering which vendor should not be able to handle that:
The CASE WHEN ... THEN ... END is documented in DQL, and allowed.
LIKE itself is allowed.
If an RDBMs cannot use CASE WHEN and LIKE in combination, this would be a strange limitation.

Comment by Martin Keckeis [ 31/Mar/14 ]

Having the same problem here.

LIKE + CASE is often used in my application at the WHERE part.
(e.g. data filtering of a datagrid column)





[DDC-2570] Doctrine CLI Tools - Clear All Cache Created: 24/Jul/13  Updated: 08/Feb/14

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: ORM, Tools
Affects Version/s: 2.3.4
Fix Version/s: 2.x, 2.5

Type: Improvement Priority: Minor
Reporter: Frederick Marcoux Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: Cli, cache, orm


 Description   

It would be nice to be able to clear all cache one shot instead of clearing them one after one...

Like this:

root$ ./doctrine orm:clear-cache:all

Instead of:

root$ ./doctrine orm:clear-cache:metadata
root$ ./doctrine orm:clear-cache:result
root$ ./doctrine orm:clear-cache:query






[DDC-2281] Validation against database-first generated xml requires that the column order within a composite primary key match the order the columns are in in mapping xml Created: 06/Feb/13  Updated: 09/May/13

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.3.2
Fix Version/s: None
Security Level: All

Type: Bug Priority: Minor
Reporter: Aaron Moore Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None


 Description   

In using a database-first approach utilizing orm:convert-mapping to generate xml, the validation and schema-tool reports that my composite primary key (ex. Columns A, C, B) be dropped and added in the order in which the mapping appears in the xml (ex. Columns A, B, C).

These columns are not auto-increment and are simply a mixture of int and varchar.



 Comments   
Comment by Benjamin Eberlei [ 09/May/13 ]

Is the composite key a mix of association and field types?

Comment by Aaron Moore [ 09/May/13 ]

I'm trying to remember the usage as it was a short term project but I believe it is.

For example a user has a userid.

The table in question might have a primary key consisting of the userid and an int representing a year..





[DDC-2035] XML Mapping : add attribute "length" for tag "id" Created: 20/Sep/12  Updated: 29/Sep/12

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.2.3
Fix Version/s: None
Security Level: All

Type: New Feature Priority: Minor
Reporter: Erik Müller Assignee: Fabio B. Silva
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Linux, Doctrine ORM 2.3.0, MySQL



 Description   

XML mapping :

<id name="id" type="string" length="16"/>

Generate SQL :

id varchar(255) not null

It's not possible with XML mapping to have :

 id varchar(16) not null

Because tag "id" doesn't support "length" attribute.
Please add this attribute



 Comments   
Comment by Fabio B. Silva [ 20/Sep/12 ]

Hi Erik,

The atribute "id" arealdy support "length" in the current doctrine version
: https://github.com/doctrine/doctrine2/blob/2.3/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php#L259

Which version are you using ?





[DDC-1630] Get PersistentCollection::getDeleteDiff is empty when collection changes from 1 item to zero items Created: 31/Jan/12  Updated: 09/Feb/13

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

Type: Bug Priority: Minor
Reporter: Lee Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 2
Labels: None
Environment:

Symfony2


Attachments: File DDC1630Test.php    

 Comments   
Comment by Steve Müller [ 09/Feb/12 ]

Same problem here. I wanted to write some unit tests, checking the entity relations and ran into exactly the same problem. Maybe my code can provide some more information (Group entity is the owning side, role entity is the inverse side):

WHAT DOES NOT WORK:

        /**
         * Test ArrayCollection
         */
        $group = new Group('Group Test');
        $em->persist($group);
        $em->flush();

        $groups = new ArrayCollection();
        $groups->add($group);

        $this->role->setGroups($groups);

        $this->assertEquals($groups, $this->role->getGroups());

        /**
         * Test PersistentCollection
         */
        $em->persist($this->role);
        $em->flush();

        $groups = $this->role->getGroups();
        $groups->removeElement($group); // first remove element before adding a new one

        $group = new Group('Group Test 2');
        $em->persist($group);
        $em->flush();
        $groups->add($group);        

        $this->role->setGroups($groups);

        $this->assertEquals($groups, $this->role->getGroups());

WHAT WORKS:

        /**
         * Test ArrayCollection
         */
        $group = new Group('Group Test');
        $em->persist($group);
        $em->flush();

        $groups = new ArrayCollection();
        $groups->add($group);

        $this->role->setGroups($groups);

        $this->assertEquals($groups, $this->role->getGroups());

        /**
         * Test PersistentCollection
         */
        $em->persist($this->role);
        $em->flush();

        $groups = $this->role->getGroups();

        $group2 = new Group('Group Test 2');
        $em->persist($group2);
        $em->flush();
        $groups->add($group2);  // first adding a new element before removing one

        $groups->removeElement($group);

        $this->role->setGroups($groups);

        $this->assertEquals($groups, $this->role->getGroups());

Hope this helps in any way... I tried figuring it out on my own but I am too drunk right now xD

Comment by Benjamin Eberlei [ 10/Feb/12 ]

Thanks for the report, formatted it

Comment by Benjamin Eberlei [ 10/Feb/12 ]

Which version is that btw?

Comment by Steve Müller [ 16/Feb/12 ]

Occurs in version 2.1.6

Comment by Benjamin Eberlei [ 20/Feb/12 ]

If group is the owning side, why do you only set Role::$groups? This has to be the other way around or not?

Comment by Benjamin Eberlei [ 20/Feb/12 ]

@Steve

I cannot reproduce your issue.

Attached is a test script.

Your code is very weird btw, why are you getting and setting groups collection? It is passed by reference so you can just have something like $role->addGroup() and $role->removeGroup() and encapsulate the logic?

Also your tests are pretty useless, you check if two variables which are the same reference to the same collection are the same. Which should always be true.

@Lee

Can you provide more details? I cant verify this without more details.

Comment by Alexander [ 09/Feb/13 ]

Can anyone provide us with more feedback?





[DDC-1494] Query results are overwritten by previous query. Created: 15/Nov/11  Updated: 09/Feb/13

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

Type: Bug Priority: Minor
Reporter: J Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None
Environment:

PHP 5.3 + MySQL 5.5


Attachments: File DDC1494Test.php    

 Description   

I am running a query that JOINs three tables, with a simple WHERE:

$q = $em->createQuery("

SELECT cat, n, c
FROM Project_Model_NoticeCategory cat
JOIN cat.notices n
JOIN n.chapters c
WHERE
c.id = :chapter_id

");

When I do this:

  $q->setParameter('chapter_id', 1);
  $a = $q->getResult();

  $q->setParameter('chapter_id', 2);
  $b = $q->getResult();

$b always has the wrong results. Running the following code:

  $q->setParameter('chapter_id', 1);
  $a = $q->getResult();

  $q->setParameter('chapter_id', 2);
  $b = $q->getResult();
  $z = $q->getArrayResult();

BUG Results: $b != $z (getArrayResult IS CORRECT, it refreshes the results) Note: $a==$b (which is wrong)

Explanation:

There is a chapter table, this has a many-to-many join to notices (these are meta info
about the chapter – a little like tagging a blog post) the notices are grouped into
categories.

Data model:

/**
 * @Entity
 * @Table(name="chapter")
 */
class Project_Model_Chapter
{
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /** @Column(type="string") */
    private $title;

	/**
	 * @ManyToMany(targetEntity="Project_Model_Notice", mappedBy="chapters")
	 */
	private $notices;
	
	.... /lots of code snipped/ ....
	
}


/**
 * @Entity
 * @Table(name="notice")
 */
class Project_Model_Notice
{
	/**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /** @Column(type="string") */
    private $title;
	
	/**
	 * @ManyToMany(targetEntity="Project_Model_Chapter", inversedBy="notices")
	 * @JoinTable(name="chapter_notice")
	 */
	private $chapters;
	
	/**
	 * @ManyToOne(targetEntity="Project_Model_NoticeCategory", inversedBy="notices")
	 */
	private $notice_category;
	
	.... /lots of code snipped/ ....
	
}

/**
 * @Entity
 * @Table(name="notice_category")
 */
class Project_Model_NoticeCategory
{
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;
	/** @Column(type="string") */
    private $title;
	
	/**
	 * Bidirectional - One-To-Many (INVERSE SIDE)
	 *
	 * @OneToMany(targetEntity="Project_Model_Notice", mappedBy="notice_category", cascade={"persist", "remove"})
	 */
	private $notices;

	.... /lots of code snipped/ ....
	
}

Data fixtures:

$tools = new \Project_Model_NoticeCategory;
$tools->setTitle('Tools');
		
$spanner = new \Project_Model_Notice;
$spanner->setTitle('spanner');
$tools->addNotice($spanner);
		
$drill = new \Project_Model_Notice;
$drill->setTitle('power drill');
$tools->addNotice($drill);
		
$this->em->persist($tools);
$this->em->flush();

$tools = new \Project_Model_NoticeCategory;
$tools->setTitle('Safety');
		
$gloves = new \Project_Model_Notice;
$gloves->setTitle('gloves');
$tools->addNotice($gloves);
		
$goggles = new \Project_Model_Notice;
$goggles->setTitle('goggles');
$tools->addNotice($goggles);
		
$this->em->persist($tools);
$this->em->flush();

$chapter1 = new \Project_Model_Chapter;
$chapter1->setTitle('Chapter 1');
$this->em->persist($chapter1);

$chapter2 = new \Project_Model_Chapter;
$chapter2->setTitle('Chapter 2');
$this->em->persist($chapter2);

$chapter1->addNotice($spanner);
$chapter1->addNotice($gloves);

$chapter2->addNotice($spanner);
$chapter2->addNotice($gloves);
$chapter2->addNotice($drill);
$chapter2->addNotice($goggles);

// now persist and flush everything

Initial investigation:

I think it has something to do with HINT_REFRESH ? Stepping through:

ObjectHydrator->_hydrateRow
ObjectHydrator->_getEntity

when it requests the Project_Model_Category from the unit of work, it
seems that the second query is simply grabbing the cached results from
the first results. This MUST be wrong as the second query uses a
different query (the ID changes) and all the results are wrong.



 Comments   
Comment by Benjamin Eberlei [ 15/Nov/11 ]

Fixed formatting

Comment by Benjamin Eberlei [ 18/Nov/11 ]

are you using result caching?

Comment by J [ 21/Nov/11 ]

This is part of my bootstrap
,
,

		
$config = new \Doctrine\ORM\Configuration();
		
$cache = new \Doctrine\Common\Cache\ArrayCache;
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
		
// driver: schema
$driver = $config->newDefaultAnnotationDriver(
	APPLICATION_PATH . '/models'
);
$config->setMetadataDriverImpl($driver);

Comment by Benjamin Eberlei [ 15/Dec/11 ]

Cannot reproduce it with the script attached. Can you try to modify this to fail or write your own testcase?

Comment by Benjamin Eberlei [ 15/Dec/11 ]

Downgraded

Comment by Alexander [ 09/Feb/13 ]

Please provide extra feedback.





[DDC-1370] preInsert, postInsert, prePersist, postPersist, preUpdate, postUpdate code and documentation of events Created: 09/Sep/11  Updated: 26/Feb/14

Status: Awaiting Feedback
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: None
Fix Version/s: 2.x
Security Level: All

Type: Improvement Priority: Minor
Reporter: Guilherme Blanco Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 1
Labels: None


 Description   

Currently we have a set of Lifecycle events, but they seem to be misleading both in actual implementation and documentation.

One good example is prePersist and postPersist, which is only fired when you're creating new entities. It should be renamed to preInsert and postInsert.
As of preUpdate and postUpdate, they seem quite valid.

But if we rename prePersist and postPersist to (pre|post)Insert, we may have a situation where you wanna cover both insert and update.
For this, (pre|post)Persist should be reinstated, but acting differently from what it does currently.



 Comments   
Comment by Rafael Dohms [ 09/Sep/11 ]

Also, documentation for post* methods is broken at the website:

"Changes in here are not relevant to the persistence in the database, but you can use this events to"

It cuts off in mid-sentence.

Comment by Guilherme Blanco [ 09/Dec/11 ]

RDohms, this paragraph was already sorted out.

The actual ticket is still valid here.

Comment by Guilherme Blanco [ 20/Dec/11 ]

Updating fix version

Comment by Matt McNeill [ 26/Feb/14 ]

Commenting here to say that this caused a lot of headaches for our project until we got it sorted out what these events really do.

The problem is that the term 'persist' is ambiguous - it means both persist and update in the context of the persist() function call, but only means INSERT in the context of the event system.





[DDC-987] How to register lifecycle callbacks from YAML is not done correctly in the Events section of the documentation. Created: 14/Jan/11  Updated: 27/Nov/13

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

Type: Documentation Priority: Minor
Reporter: Amir Assignee: Benjamin Eberlei
Resolution: Unresolved Votes: 0
Labels: None


 Description   

http://www.doctrine-project.org/docs/orm/2.0/en/reference/events.html

The above URL has an example of how to register lifecycle callbacks from YAML, but actually it does not work. The correct way of doing it is mentioned on the page: http://www.doctrine-project.org/docs/orm/2.0/en/reference/yaml-mapping.html



 Comments   
Comment by Jeremy Postlethwaite [ 27/Nov/13 ]

Please close this issue

Both pages now have the same documentation.

Doctrine example
  lifecycleCallbacks:
    prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
    postPersist: [ doStuffOnPostPersist ]
Partial stack trace exhibiting call:
/redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php(73) : eval()'d code :: Tue, 26 Nov 2013 22:05:03 -0800 $eventName

prePersist

/redacted-path/src/Application/Modules/System/Entity/Sites.php(161) : eval()'d code :: Tue, 26 Nov 2013 22:05:03 -0800 $args

object(Doctrine\ORM\Event\LifecycleEventArgs)[839]
  private 'objectManager' (Doctrine\Common\Persistence\Event\LifecycleEventArgs) => 
    object(Doctrine\ORM\EntityManager)[330]
      private 'config' => 
        object(Doctrine\ORM\Configuration)[156]

/redacted-path/src/Application/Base.php(371) : eval()'d code :: Tue, 26 Nov 2013 22:05:03 -0800

#0 /redacted-path/src/Application/Modules/System/Entity/Sites.php(161): Application\Base::puke(Object(Doctrine\ORM\Event\LifecycleEventArgs), '/www/sites/loca...', true)
#1 /redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php(104): Application\Modules\System\Entity\Sites->prePersist(Object(Doctrine\ORM\Event\LifecycleEventArgs))
#2 /redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(845): Doctrine\ORM\Event\ListenersInvoker->invoke(Object(Doctrine\ORM\Mapping\ClassMetadata), 'prePersist', Object(Application\Modules\System\Entity\Sites), Object(Doctrine\ORM\Event\LifecycleEventArgs), 2)
#3 /redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1621): Doctrine\ORM\UnitOfWork->persistNew(Object(Doctrine\ORM\Mapping\ClassMetadata), Object(Application\Modules\System\Entity\Sites))
#4 /redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1577): Doctrine\ORM\UnitOfWork->doPersist(Object(Application\Modules\System\Entity\Sites), Array)
#5 /redacted-path/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(624): Doctrine\ORM\UnitOfWork->persist(Object(Application\Modules\System\Entity\Sites))
#6 /redacted-path/src/Application/Modules/Application/Controller/AbstractEntityFormActionController.php(128): Doctrine\ORM\EntityManager->persist(Object(Application\Modules\System\Entity\Sites))

My configuration was slightly different from the example.

  • Changed some of the details to protect the innocent.
My configuration inherited from a mappedSuperclass
  lifecycleCallbacks:
    prePersist: [ prePersist ]
    preFlush: [ preFlush ]




[DDC-2440] composer.json is wrong Created: 09/May/13  Updated: 09/May/13  Resolved: 09/May/13

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

Type: Bug Priority: Blocker
Reporter: Richard Nicol Assignee: Marco Pivetta
Resolution: Invalid Votes: 1
Labels: None


 Description   

In composer.json for doctrine/doctrine-orm-module there is the following require:
"doctrine/doctrine-module": "0.8.*"

However "0.8.*" does not exist, it should be "0.8.x-dev" or "dev-master". The later would then use the branch alias. Either way this needs changing, because it's stopping the package from being installed or updated.



 Comments   
Comment by Benjamin Eberlei [ 09/May/13 ]

Please open the ticket on the Doctrine ORM Module, this is the ORM bug tracker.

Comment by Richard Nicol [ 09/May/13 ]

Sorry my mistake! I have opened an issue on github here: https://github.com/doctrine/DoctrineORMModule/issues/219 in case anyone ends up here.

Cheers.

Comment by Marco Pivetta [ 09/May/13 ]

DoctrineModule has a 0.8.x branch. Closing





[DDC-1377] Doctrine doesn't understand associations from SINGLE_TABLE inheritances Created: 14/Sep/11  Updated: 15/Sep/11  Resolved: 15/Sep/11

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

Type: Bug Priority: Blocker
Reporter: Marcos Augusto da Silva Garcia Assignee: Guilherme Blanco
Resolution: Duplicate Votes: 0
Labels: None
Environment:

Xubuntu 11.04 Linux 2.6.38 i386


Issue Links:
Duplicate
duplicates DDC-16 DQL Ignores properties of subclasses Closed

 Description   

Doctrine doesn't understand when a query is built from an association to a SINGLE_TABLE parent to get its successors relations.

For example:
Many Lice reside in an Animal (Louse @ManyToOne Animal)
Animals can specialize into Cats or Dogs (SINGLE_TABLE Inheritance)
A Dog can have one Bone (Dog @OneToOne Bone)
A Cat can have one Yarn (Yarn @OneToOne Yarn)

From a specific Louse, Doctrine can't get the Animal's Bone or Yarn.



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

Doctrine implements strict OO inheritance, what you want does not work as no casting is currently possible.

Comment by Guilherme Blanco [ 15/Sep/11 ]

Reopening

Comment by Guilherme Blanco [ 15/Sep/11 ]

Duplicate to DDC-16





[DDC-513] LEFT JOIN on extended entity of associated entity from parent by @OneToOne throw exception "no such column" [testcase included] Created: 12/Apr/10  Updated: 12/Apr/10  Resolved: 12/Apr/10

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

Type: Bug Priority: Blocker
Reporter: Ondrej Sibrina Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File DDC513Test.php    

 Description   

Dear Roman ,

Watch testcase please to understand this issue.

Thank you.



 Comments   
Comment by Guilherme Blanco [ 12/Apr/10 ]

In http://github.com/doctrine/doctrine2/commit/56a8f5cd5353908b815607a6e089201c95e01e6c this issue was fixed.

Thanks for the report and unit test. It saved me a LOT of time!





[DDC-512] LEFT JOIN of extended null entity cause empty result [testcase included] Created: 12/Apr/10  Updated: 15/Apr/10  Resolved: 15/Apr/10

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

Type: Bug Priority: Blocker
Reporter: Ondrej Sibrina Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File DDC512Test.php    

 Description   

Dear developers,

I'm not sure about propriety of my query but what i want to do is left
join entity which is associeted by @OneToOne. Problem occur when
associeted entity is NULL. Then i got empty result. I think it's
because my associeted entity is extended so it cause in final SQL
query inner joins which are not in subselect.

class Shop_Data_Entity_StockItem extends Shop_Data_Entity_Item

{ /** * @OneToOne(targetEntity="Shop_Data_Entity_OrderItem", mappedBy="stockItem") */ protected $orderItem; ... }

So there's my query:

$q = $em->createQuery("select u from Shop_Data_Entity_StockItem u left
join u.orderItem uu");
echo $q->getSql();
$result = $q->getResult();
count($result[0]);

// print 0 even there're Shop_Data_Entity_StockItem in database and
without left join clause prints 2

There's echo $q->getSql():

SELECT s0_.ean AS ean0, s0_.title AS title1, s0_.description AS
description2, s0_.vat AS vat3, s0_.id AS id4, s1_.bestBefore AS
bestBefore5, s0_.discr AS discr6, s0_.price AS price7,
s1_.deliveryInvoice_id AS deliveryInvoice_id8 FROM
Shop_Data_Entity_StockItem s1_ INNER JOIN Shop_Data_Entity_Item s0_ ON
s1_.id = s0_.id LEFT JOIN Shop_Data_Entity_OrderItem s2_ ON s1_.id =
s2_.stockItem_id INNER JOIN Shop_Data_Entity_OfferItem s3_ ON s2_.id =
s3_.id INNER JOIN Shop_Data_Entity_Item s4_ ON s2_.id = s4_.id



 Comments   
Comment by Ondrej Sibrina [ 12/Apr/10 ]

This test case is slightly different from example i wrote in description but shows same issue

Comment by Guilherme Blanco [ 12/Apr/10 ]

Your report exposes exactly the issue pointed on DDC-349.

We should take a look how to fix this without having to update ALL unit tests that takes advantage of inheritance.

Also, the SQL spec requires that all joins need to be specified before write the ON keyword.
Example:

Exception: [PDOException] SQLSTATE[HY000]: General error: 1 a JOIN clause is required before ON
SELECT d0_.id AS id0, d0_.item AS item1 FROM DDC512Customer d0_ LEFT JOIN (DDC512OfferItem d1_ ON d0_.item = d1_.id INNER JOIN DDC512Item d2_ ON d1_.id = d2_.id)

And in the situation of a inheritance:

Exception: [PDOException] SQLSTATE[HY000]: General error: 1 a JOIN clause is required before ON             
SELECT o0_.id AS id0, o0_.name AS name1, o3_.id AS id2, o3_.name AS name3, o0_.discr AS discr4, o0_.mother_id AS mother_id5, o3_.discr AS discr6, o3_.mother_id AS mother_id7 FROM OJTIC_Pet o0_ LEFT JOIN OJTIC_Cat o1_ ON o0_.id = o1_.id LEFT JOIN OJTIC_Dog o2_ ON o0_.id = o2_.id INNER JOIN (OJTIC_Pet o3_ ON o0_.id = o3_.mother_id LEFT JOIN OJTIC_Cat o4_ ON o3_.id = o4_.id LEFT JOIN OJTIC_Dog o5_ ON o3_.id = o5_.id) WHERE o0_.name = 'Poofy' ORDER BY o3_.name ASC
Comment by Roman S. Borschel [ 13/Apr/10 ]

I am aware of the problem and yes, nested joins for CTI can be a solution but its just 1 solution. The other one is to simply turn these CTI joins into left joins when they appear in the middle of a query (that is, not in the FROM clause).

So, given a Class hierarchy like this:

class Item
class StockItem extends Item
class OfferItem extends Item
class OrderItem extends OfferItem

StockItem <-onetoone-> OrderItem

and a DQL like this:

DQL: select s from StockItem s left join s.orderItem o ...

We have 2 possible solutions.

Nr. 1: Nested inner join

SELECT ... FROM stockitem s1_
INNER JOIN item s0_ ON s1_.id = s0_.id
LEFT JOIN
    (orderitem s2_ INNER JOIN offeritem s3_ ON s2_.id = s3_.id
     INNER JOIN item s4_ ON s2_.id = s4_.id)
ON s1_.id = s2_.stockItem_id

Nr. 2: Just use left joins for parent tables for all CTI joins that are the result of a DQL join (This is what Hibernate does):

SELECT ... FROM stockitem s1_
INNER JOIN item s0_ ON s1_.id = s0_.id
LEFT JOIN orderitem s2_ ON s1_.id = s2_.stockItem_id
LEFT JOIN offeritem s3_ ON s2_.id = s3_.id
LEFT JOIN item s4_ ON s2_.id = s4_.id

According to DDC-349, most databases seem to support nested inner joins (Nr. 1) but nevertheless its not in the ANSI standard I think, so I am not sure we can rely on it.

The Hibernate solution seems simpler but I still wonder whether they perform differently (Usually, inner joins are more performant than outer joins),

Comment by Roman S. Borschel [ 15/Apr/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/01c2c06bbf529d89c9741ea97702359509ea230a using the "hibernate-way".

Please note that you currently should not name join columns the same as entity fields. See DDC-522. Better use @JoinColumn(name="item_id", ...)





[DDC-422] Unable to add entity in @ManyToMany association when owning entity is extended from another entity Created: 14/Mar/10  Updated: 18/Mar/10  Resolved: 18/Mar/10

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

Type: Bug Priority: Blocker
Reporter: Ondrej Sibrina Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

PHP 5.3.2


Attachments: File DDC406Test.php    

 Description   

Dear developers,

I'm not sure if i extend entities in right way but i found different behaviour in extended entity which won't process add in @ManyToMany association.
My class are defined like this:

/**
 * @Entity
 * @HasLifecycleCallbacks
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="discr", type="string")
 * @DiscriminatorMap({"guest" = "Shop_Data_Entity_Guest", "customer" = "Shop_Data_Entity_Customer"})
 */
class Shop_Data_Entity_Guest extends Shop_Data_Entity_Abstract {
    public function __construct() {
        /* call parent construct with all parameters */
        call_user_func_array(array("parent","__construct"),func_get_args());
    }
}

/**
 * @Entity
 * @HasLifecycleCallbacks
 */
class Shop_Data_Entity_Customer extends Shop_Data_Entity_Guest {
    /**
     * @ManyToMany(targetEntity="Shop_Data_Entity_Contact", cascade={"persist","remove"} )
     * @JoinTable(name="Shop_Data_Entity_Customer__homes",
     *      joinColumns={@JoinColumn(name="customer_id", referencedColumnName="id", onDelete="cascade" )},
     *      inverseJoinColumns={@JoinColumn(name="contact_id", referencedColumnName="id", onDelete="cascade" )}
     *      )
     */
    public $homes;

    public function __construct() {
        /* call parent construct with all parameters */
        call_user_func_array(array("parent","__construct"),func_get_args());

        $this->homes = new Shop_Data_Collection_Standard(); // this class only extends ArrayCollection
    }
}

When i fire this code:

        $q = $em->createQuery("select c from Shop_Data_Entity_Customer c");
        $q->setMaxResults(1);
        $customers = $q->getResult();
        
        $contact = new Shop_Data_Entity_Contact();
        $customers[0]->home->add($contact);
        $em->flush();

There's no change in database.
When i change definition od Guest class like this:

/** @MappedSuperclass */
class Shop_Data_Entity_Guest extends Shop_Data_Entity_Abstract {
    public function __construct() {
        /* call parent construct with all parameters */
        call_user_func_array(array("parent","__construct"),func_get_args());
    }
}

It start work and every firing of my code add one contact and association to my database. It causes problem only when owning entity is quered from database at first.



 Comments   
Comment by Ondrej Sibrina [ 16/Mar/10 ]

I tried to debug by myself and i found that on line 411 in UnitOfWork.php is on flush in first example $class->associationMappings == null. When i change Shop_Data_Entity_Guest to /** @MappedSuperclass */ i can find $class->associationMappings on same line with some elements.

Hope it help to fix it

Andy

Comment by Ondrej Sibrina [ 16/Mar/10 ]

I tried to made patch by myself and it looks it start working. I'm not sure if this solution is proper. Andy

from line 1731 of UnitOfWork.php

 if (isset($this->_identityMap[$class->name][$idHash])) {  // $$class->rootEntityName to $class->name
            $entity = $this->_identityMap[$class->name][$idHash]; // $class->rootEntityName to $class->name
            $oid = spl_object_hash($entity);
            if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
                $entity->__isInitialized__ = true;
                $overrideLocalValues = true;
            } else {
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
            }
        } else {
            //$entity = clone $class->prototype;
            $entity = new $className;
            $oid = spl_object_hash($entity);
            $this->_entityIdentifiers[$oid] = $id;
            $this->_entityStates[$oid] = self::STATE_MANAGED;
            $this->_originalEntityData[$oid] = $data;
            $this->_identityMap[$class->name][$idHash] = $entity; // $class->rootEntityName to $class->name
Comment by Ondrej Sibrina [ 16/Mar/10 ]

To ensure i checkouted trunk version but it doesn't solve this problem.

Andy

Comment by Ondrej Sibrina [ 16/Mar/10 ]

After using my patch i can't do $em->remove properly any other patch?

Comment by Roman S. Borschel [ 18/Mar/10 ]

This is fixed now in trunk.

Comment by Ondrej Sibrina [ 18/Mar/10 ]

Thank you very much Hope this report was better than my first one.





[DDC-50] Call to undefined method Doctrine\ORM\Mapping\OneToManyMapping::getQuotedJoinColumnName() Created: 15/Oct/09  Updated: 15/Oct/09  Resolved: 15/Oct/09

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

Type: Bug Priority: Blocker
Reporter: Arthur Purnama Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

PHP 5.3.0 (cli) (built: Jul 21 2009 08:22:07)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

Server Version: Apache/2.2.11 (Ubuntu) mod_jk/1.2.26 PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch
Server Built: Aug 18 2009 14:26:31

mysql Ver 14.14 Distrib 5.1.31, for debian-linux-gnu (i486) using EditLine wrapper


Attachments: File kateglox.sql     Zip Archive kateglo_doctrine.zip    

 Description   

with the $config->setAllowPartialObjects(false); i make an query to
phrase, and get a phrase object. sofar so good

but as i try to get the type or lexical member like $phrase->getType()
it throws a Fatal error like this:

Fatal error: Call to undefined method
Doctrine\ORM\Mapping\OneToManyMapping::getQuotedJoinColumnName() in
/usr/local/zend/apache2/htdocs/doctrine/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php
on line 599

i read this on doctrine-user groups

http://groups.google.com/group/doctrine-user/browse_thread/thread/a15d430b4ae483b4/dc7665ed137a4e78?lnk=gst&q=getQuotedJoinColumnName#dc7665ed137a4e78

and i take a look if the line

$owningAssoc = $this->_class->associationMappings[$coll->getMapping()->mappedByFieldName];

present on StandardEntityPersister#loadOneToManyCollection.

and yes. its present. but i still got this error.



 Comments   
Comment by Roman S. Borschel [ 15/Oct/09 ]

Should be fixed now in HEAD.





[DDC-51] Notice: Undefined index: [columnName] in/Doctrine/ORM/Mapping/OneToManyMapping.php on line 129 Created: 15/Oct/09  Updated: 15/Oct/09  Resolved: 15/Oct/09

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

Type: Bug Priority: Blocker
Reporter: Arthur Purnama Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

« Hide
PHP 5.3.0 (cli) (built: Jul 21 2009 08:22:07)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

Server Version: Apache/2.2.11 (Ubuntu) mod_jk/1.2.26 PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch
Server Built: Aug 18 2009 14:26:31

mysql Ver 14.14 Distrib 5.1.31, for debian-linux-gnu (i486) using EditLine wrapper



 Description   

With the same example as DDC-50

i now can not get the ManyToOne association (definitions, proverbs and relations);

i got php notice
Notice: Undefined index: phrase_id in /usr/local/zend/apache2/htdocs/doctrine/lib/Doctrine/ORM/Mapping/OneToManyMapping.php on line 129

if i check the line the variable $joinColumnValues is an empty array, because the PersistentCollection.php at line 233 did not send any joincolumnvalues to the load method. i dont know if this the cause.

i have try to set $entityManager->getConfiguration()->setAllowPartialObjects() to TRUE and make an dql like this
"SELECT p, d FROM ".models\Phrase::CLASS_NAME." p join p.definitions d WHERE p.phrase = '$phrase'"

but the result is still the same



 Comments   
Comment by Roman S. Borschel [ 15/Oct/09 ]

Can you please show the code that you're using. I dont mean the models but the code that causes this problem.

Thanks.

Comment by Arthur Purnama [ 15/Oct/09 ]

$definitions = $phrase->getDefinitions();
var_dump($definitions[0] instanceof kateglo\application\models\Definition);

hope this help you.

regards,
arthur

Comment by Roman S. Borschel [ 15/Oct/09 ]

Okay, thanks, I can reproduce this and will work on this. When I'm done I will add all these new tests to the test suite.

Comment by Roman S. Borschel [ 15/Oct/09 ]

By the way, eager loading works fine for me. See this snippet:


        $query = $this->_em->createQuery("SELECT p,d FROM Doctrine\Tests\ORM\Functional\Phrase p JOIN p.definitions d");
        $res = $query->getResult();
        $definitions = $res[0]->getDefinitions();
        
        $this->assertEquals(1, count($res));
        $this->assertTrue($definitions[0] instanceof Definition);

This works for me. But lazy-loading results in the error you mentioned and which I will fix.

Comment by Arthur Purnama [ 15/Oct/09 ]

ok thank you very much it works like charm.

du bist Gold Wert Roman

please close the ticket.

Regards,
Arthur





[DDC-49] Incomplete MySQL Query Generator (MySQL Syntax error) Created: 15/Oct/09  Updated: 15/Oct/09  Resolved: 15/Oct/09

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

Type: Bug Priority: Blocker
Reporter: Arthur Purnama Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

PHP 5.3.0 (cli) (built: Jul 21 2009 08:22:07)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies

Server Version: Apache/2.2.11 (Ubuntu) mod_jk/1.2.26 PHP/5.2.6-3ubuntu4.2 with
Suhosin-Patch
Server Built: Aug 18 2009 14:26:31

mysql Ver 14.14 Distrib 5.1.31, for debian-linux-gnu (i486) using EditLine wrapper


Attachments: Zip Archive kateglo_doctrine.zip     File Lexical.php     File PhraseType.php    

 Description   

i checkout the HEAD version. and try to write this DQL

$query = utilities\DataAccess::getEntityManager()->createQuery("SELECT
p, t FROM ".models\Phrase::CLASS_NAME." p join p.type t WHERE p.phrase
= '$phrase'");

i commented out the $config->setAllowPartialObjects(false);

and have PDO error that my MySQL statement sytax is invalid.

i check the Query that the Doctrine create and its like this :
SELECT p0_.phrase_id AS phrase_id0, p0_.phrase_name AS phrase_name1,
p1_.phrase_type_id AS phrase_type_id2, p1_.phrase_type_name AS
phrase_type_name3, p1_.phrase_type_abbreviation AS
phrase_type_abbreviation4 FROM phrase p0_ INNER JOIN phrase_type p1_
ON p0_.phrase_type_id = p1_. WHERE p0_.phrase_name = 'abu'

as you can si at the ON statement it writes p0_.phrase_type_id = p1_.

the p1_. is not completed. i think my DocAnnotation is OK, because i
look at the Doctrine Tests Models that test the OneToMany Function
(the one with the ECommerce models, product and features). I have
followed all the doc annotation writes there.



 Comments   
Comment by Roman S. Borschel [ 15/Oct/09 ]

Should be fixed now in HEAD.





[DDC-24] text type not portable (works only on MySQL) Created: 30/Sep/09  Updated: 14/Jun/10  Resolved: 01/Oct/09

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

Type: Bug Priority: Blocker
Reporter: Ismo Toijala Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

The text type is not portable to other RDBMS than MySQL. To support the text type, a platform must implement the method getClobDeclarationSql(array $field). Currently only MySqlPlatform has this method. This means that attempting to use the CLI tool to create a schema with a text column using another RDBMS fails with the following error:

Fatal error: Call to undefined method Doctrine\DBAL\Platforms\SqlitePlatform::getClobDeclarationSql() in D:\Projects\Test\tools\sandbox\lib\Doctrine\DBAL\Types\TextType.php on line 15

I believe at least Sqlite supports the CLOB type.

Either the text type should be supported by all platforms or the documentation should be revised. Currently it says:

"All Doctrine Mapping Types that ship with Doctrine are fully portable between different RDBMS."

This makes easy testing of models using sqlite with minimum configuration impossible with the text type.



 Comments   
Comment by Roman S. Borschel [ 30/Sep/09 ]

Thanks. We will try to address this as soon as possible. This is just an oversight in the implementation. The text type can definitely be made portable across all platforms.





[DDC-2845] booking Created: 08/Dec/13  Updated: 08/Dec/13  Resolved: 08/Dec/13

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Documentation, DQL, Mapping Drivers, ORM, Tools
Affects Version/s: 2.4.1
Fix Version/s: None
Security Level: All

Type: New Feature Priority: Critical
Reporter: wishver Assignee: Marco Pivetta
Resolution: Invalid Votes: 0
Labels: None
Environment:

Environment test



 Description   

DescriptiontestDescriptiontest
Descriptiontest
Descriptiontest
Descriptiontest






[DDC-2285] Repeating the same query with different parameter value returns the same results Created: 08/Feb/13  Updated: 08/Feb/13  Resolved: 08/Feb/13

Status: Closed
Project: Doctrine 2 - ORM
Component/s: DQL, Mapping Drivers, ORM
Affects Version/s: 2.3.2
Fix Version/s: None

Type: Bug Priority: Critical
Reporter: Mehdi Bakhtiari Assignee: Marco Pivetta
Resolution: Invalid Votes: 0
Labels: None
Environment:

Ubuntu 12.10, Zend Server CE



 Description   
$activeAdsCustomers = \Ez\Registry::getDoctrineEntityManager()
    ->createQuery( "SELECT c, a FROM \Spot101\Model\Ad\Customer c JOIN c.ads a WHERE a.status = :status" )
    ->setParameter( "status", \Spot101\Model\Ad\Status::ACTIVE )
    ->getResult();

$inactiveAdsCustomers = \Ez\Registry::getDoctrineEntityManager()
    ->createQuery( "SELECT c, a FROM \Spot101\Model\Ad\Customer c JOIN c.ads a WHERE a.status = :status" )
    ->setParameter( "status", \Spot101\Model\Ad\Status::INACTIVE )
    ->getResult();

Having the code above I am getting the same results for $inactiveAdsCustomers as I get for $activeAdsCustomers. And when I try hydrating the results everything works so fine.

Both queries look the same except the value provided for the "status" parameter.



 Comments   
Comment by Marco Pivetta [ 08/Feb/13 ]

You are hydrating joined and filtered resultsets. You should never do this, as this will hydrate an invalid association into your objects.





[DDC-2179] Transactions should sent in group not chunked Created: 29/Nov/12  Updated: 30/Nov/12  Resolved: 30/Nov/12

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.2, 2.3, Git Master
Fix Version/s: None
Security Level: All

Type: Bug Priority: Critical
Reporter: Florin Patan Assignee: Marco Pivetta
Resolution: Invalid Votes: 0
Labels: unitofwork
Environment:

MySQL 5.5 / Percona



 Description   

In UnitOfWork::commit() it seems that a transaction is done like this:

  • will send separate queries for transaction start
  • compute the queries/send them to the db driver
  • execute the commit statement
  • optionally execute rollback

The question would be, should my webserver have some issues with resources, wouldn't this part of the code be a pain for the DB?

I don't know how mysql, for example, handles sending the transaction in chunks as opposed to sending it in 2/3 statements ( begin + ops and commit / + revert in case of failure) or in mySQL,l the transaction is evaluated on COMMIT statement only?

If my assumption about how MySQL works, locking everything as soon as the statement is on the server, then shouldn't Doctrine use a internal buffer for sending transactions to the DB driver in order to avoid all sorts of problems that appear in high concurency scenarios?

Best regards.



 Comments   
Comment by Marco Pivetta [ 30/Nov/12 ]

Invalid IMO. This is an over-complication that (in such high load scenarios) is handled by clustering/load balancing. Not a problem of the ORM, since smashing all statements together will just make it impossible to trap any problems.

Comment by Marco Pivetta [ 30/Nov/12 ]

This performance improvement has been discussed directly on IRC.

The original problem is related to deadlocks and small transactions, which is not anyway solved by this issue.

Otherwise, this improvement requires a PoC that shows that it is possible to have exceptions still showing the query that caused the failure.

Comment by Marco Pivetta [ 30/Nov/12 ]

Sorry, was unclear. I basically mean that any approach squashing the queries into a single chunk sent to the DB should also allow us to get computed insert IDs and eventual exceptions should bubble up with the query that caused them.





[DDC-2036] indexBy breaks cascade remove Created: 20/Sep/12  Updated: 03/Oct/12  Resolved: 03/Oct/12

Status: Closed
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.3
Fix Version/s: None

Type: Bug Priority: Critical
Reporter: James Bench Assignee: Fabio B. Silva
Resolution: Can't Fix Votes: 0
Labels: None
Environment:

Ubuntu 12.04, PHP 5.3, 64 Bit, Symfony 2


Attachments: File AbstractData.php     File DDC2036Test.php     File Location.php     File LocationData.php     Text File msql.log     Text File sql_flush.log    

 Description   

Adding the annotation indexBy causes the cascade annotation to be ignored:

/**
*

  • @var ArrayCollection
  • @ORM\OneToMany(targetEntity="LocationData", mappedBy="location", indexBy="name", cascade= {"persist", "remove"}

    )
    */
    protected $data;

Will cause an error when deleting the objects:

An exception occurred while executing 'DELETE FROM location WHERE id = ?' with params

{"1":19306}

:

SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`symfony`.`location_data`, CONSTRAINT `FK_2DF7462364D218E` FOREIGN KEY (`location_id`) REFERENCES `location` (`id`))

Removing indexBy fixes the issue.

My class structure is:

Location has many LocationData
LocationData extends AbstractData

The name field I am trying to index by comes from the AbstractData class.



 Comments   
Comment by Fabio B. Silva [ 23/Sep/12 ]

Hi James

Do you have a failing test case ?
Could you attach/paste your entity please ?

Thanks

Comment by James Bench [ 28/Sep/12 ]

Entity exhibiting the behavior.

Comment by Fabio B. Silva [ 29/Sep/12 ]

Hi James,
I can't reproduce,

Are you sure that you dont have more than one LocationData whith the same name ?

When working with indexBy the index columns must be unique into the collection.
I recommend that you add a unique constrain in the columns name.

If i'm wrong about it, please
Could you change the added test case and try to make it fails ?

Thanks

Comment by James Bench [ 02/Oct/12 ]

Hi Fabio,

The test case uses a SQLite DB without any foreign keys so the issue wouldn't be apparent.

How would I run the test against a MySQL db?

I've also attached the SQL log for the script I'm running, it looks the same as the output from the testcase.

Cheers,JB

Comment by James Bench [ 02/Oct/12 ]

SQL Log from script causing errors.

Comment by Fabio B. Silva [ 02/Oct/12 ]

Hi James,

You must copy phpunit.xml.dist to phpunit.xml and change the database configurations.

I have attached the mysql log, the test case works fine.

Comment by James Bench [ 02/Oct/12 ]

It appears that it only attempts to delete 68 LocationData rows but in the database there are 76.

Comment by James Bench [ 02/Oct/12 ]

Okay, I've found the reason for this; the name isn't unique, some are duplicated.
Once the duplicates are removed there are 68 rows.
I'll add a unique index to the name column and hopefully this problem will go away.

Not sure whether you would consider this a bug.

Thanks for your help.

Comment by Fabio B. Silva [ 02/Oct/12 ]

No problem, you are welcome

Actually isn't a bug, it is a documented behavior.

"Fields that are used for the index by feature HAVE to be unique in the database."

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/working-with-indexed-associations.html#working-with-indexed-assocations

Comment by Fabio B. Silva [ 03/Oct/12 ]

Fields that are used for the index by feature HAVE to be unique in the database.

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/working-with-indexed-associations.html#working-with-indexed-assocations





[DDC-1352] ErrorException: Undefined index in array Created: 30/Aug/11  Updated: 01/Sep/11  Resolved: 01/Sep/11

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

Type: Bug Priority: Critical
Reporter: Søren Assignee: Guilherme Blanco
Resolution: Invalid Votes: 0
Labels: None
Environment:

Windows 7, PHP 5.3.3



 Description   

The following query:
"select c.id
from \\Domain
Cuisine c
left join c.nameTranslation t
left join t.translationValues v
left join v.language l
where v.value = :name
and l.id = :languageId"

Results in a PHP ErrorException in \Doctrine\ORM\Query\SqlWalker line 745:
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;

When I step through the execution, the array entry: $relation['mappedBy'] gets the following value: "Domain\Translation" in the scope where the error occurs.
$targetClass->associationMappings has the following indices: "translation" and "language" and this leads to a "Undefined index" error and the execution breaks. It means that I cannot execute DQL queries, which is critical for the application to run.

<entity name="Domain\Cuisine" table="Cuisine" repository-class="Infrastructure\Persistence\Doctrine\Repository\CuisineRepository">
<id name="id" type="integer" column="Id">
<generator strategy="AUTO"/>
</id>
<many-to-one target-entity="Domain\Translation" field="nameTranslation">
<join-column name="NameTranslation_Id" referenced-column-name="Id"/>
</many-to-one>
</entity>

<entity name="Domain\Translation" table="Translations">
<id name="id" type="integer" column="Id">
<generator strategy="AUTO" />
</id>
<one-to-many target-entity="Domain\TranslationValue" mapped-by="Domain\Translation" field="translationValues" orphan-removal="true">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
</entity>

<entity name="Domain\TranslationValue" table="TranslationValues">
<id name="id" type="integer" column="Id">
<generator strategy="AUTO" />
</id>

<field name="value" type="string" column="Value" />

<many-to-one field="translation" target-entity="Domain\Translation">
<join-column name="Translation_Id" nullable="false" referenced-column-name="Id" />
</many-to-one>

<many-to-one field="language" target-entity="Domain\Language">
<join-column name="Language_Id" nullable="false" referenced-column-name="Id" />
</many-to-one>
</entity>

<entity name="Domain\Language" table="Languages" repository-class="\Infrastructure\Persistence\Doctrine\Repository\TranslationRepository">
<id name="id" type="integer" column="Id">
<generator strategy="AUTO" />
</id>
<field name="name" type="string" column="Name" />
<field name="shortIsoCode" type="string" column="ShortIsoCode"/>
<field name="longIsoCode" type="string" column="LongIsoCode"/>
<field name="isDefault" type="boolean" column="IsDefault" />
</entity>



 Comments   
Comment by Guilherme Blanco [ 01/Sep/11 ]

Your mapping information is wrong.
The mappedBy value must reference the field name of the owning side, and not the class name.

Marking ticket as invalid.





[DDC-802] Missing variable $name in XmlExporter Created: 14/Sep/10  Updated: 15/Sep/10  Resolved: 15/Sep/10

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.0-BETA4
Fix Version/s: 2.0-RC1
Security Level: All

Type: Bug Priority: Critical
Reporter: Martin Hasoň Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

At line 112 http://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php#L112 is required variable $name. This variable should be replaced by $unique['name']. And before this line should be test if(isset($unique['name'])).



 Comments   
Comment by Guilherme Blanco [ 15/Sep/10 ]

On http://github.com/doctrine/doctrine2/commit/4845745337dbbed5eead25c3062a07103b38649f I fixed this issue.

Thanks for the report.





[DDC-610] Numeric strings are not quoted Created: 25/May/10  Updated: 25/May/10  Resolved: 25/May/10

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

Type: Bug Priority: Critical
Reporter: David Abdemoulaie Assignee: David Abdemoulaie
Resolution: Fixed Votes: 0
Labels: None


 Description   

I've been working on a MaterializedPath implementation and hit a roadblock.

This condition:

// $pathInterval = array('00010001', '0001ZZZZ');
$andX->add($expr->between('e.' . $this->getPathFieldName(), $expr->literal($pathInterval[0]), $expr->literal($pathInterval[1])));

results in this SQL:

AND (l0_.path BETWEEN 00010000 AND '0001ZZZZ')

This is clearly not correct. Numeric strings should still be quoted if the field is of type string.



 Comments   
Comment by David Abdemoulaie [ 25/May/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/b6a5402bcb014e78eb6c0b841609e6f0bba71ef6





[DDC-604] array_merge in Query::_doExecute causes parameter reordering Created: 20/May/10  Updated: 07/Jun/10  Resolved: 07/Jun/10

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

Type: Bug Priority: Critical
Reporter: David Abdemoulaie Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Hi all,
I think there is a bug with the doExecute method from the Query class.

foreach ($this->_params as $key => $value) { 
            if ( ! isset($paramMappings[$key])) { 
                throw QueryException::unknownParameter($key); 
            } 
            if (isset($this->_paramTypes[$key])) { 
                foreach ($paramMappings[$key] as $position) { 
                    $types[$position] = $this->_paramTypes[$key]; 
                } 
            } 
            if (is_object($value) && 
$this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { 
                $values = 
$this->_em->getUnitOfWork()->getEntityIdentifier($value); 
                $sqlPositions = $paramMappings[$key]; 
                $sqlParams = array_merge($sqlParams, 
array_combine((array)$sqlPositions, $values)); 
            } else { 
                foreach ($paramMappings[$key] as $position) { 
                    $sqlParams[$position] = $value; 
                } 
            } 
        } 

When constructing the $sqlParams array, array_merge is used for params wich
are objects. Php documentation says that numeric key are renumbered. So we
loose the position of the parameter.
I solved this problem by replacing the array_merge with that :
$sqlParams = $sqlParams + array_combine((array)$sqlPositions, $values);
But I'm not sure it doesn't have unwanted effects.
I created a fork on github for this bug, hope it could be usefull.

Edit a fail case :

SQL (for postgres):

CREATE TABLE first_class
(
  id serial NOT NULL,
  "text" character varying,
  second_class_id integer,
  CONSTRAINT first_class_pkey PRIMARY KEY (id),
  CONSTRAINT first_class_second_class_id_fkey FOREIGN KEY (second_class_id)
      REFERENCES second_class (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

CREATE TABLE second_class
(
  id serial NOT NULL,
  CONSTRAINT second_class_pkey PRIMARY KEY (id)
);

INSERT INTO second_class(
            id)
    VALUES (1);

FirstClass.php

/**
 * @Entity
 * @Table(name="first_class")
 */
class FirstClass {
  /**
   * @Id
   * @Column(name="id",type="integer")
   */
  private $id;
  /** @Column(name="text",type="string") */
  private $text;
  /**
   * @OneToOne(targetEntity="SecondClass")
   * @JoinColumn(name="second_class_id", referencedColumnName="id")
   */
  private $secondClassInstance;
}

SecondClass.php

/**
 * @Entity
 * @Table(name="second_class")
 */
class SecondClass {
  /**
   * @Id
   * @Column(name="id",type="integer")
   */
  private $id;
}

Test Case :

$secondClassInstance = $doctrineEntityManager->find('SecondClass',1);

$query = $doctrineEntityManager->createQuery("SELECT f FROM FirstClass f WHERE f.text = :text AND f.secondClassInstance = :instance")->setParameters(array('instance'=>$secondClassInstance,'text'=>'Un texte en francais',));
echo $query->getSQL();
$query->execute();

When you execute this query it fails. When printing the $sqlParams variable from _doExecute you can see the folowing :
Array ( [0] => 1 [1] => Un texte en francais )



 Comments   
Comment by David Abdemoulaie [ 20/May/10 ]

Brought Paul's changes over to doctrine/orm in branch DDC-604 http://github.com/doctrine/orm/tree/DDC-604

Comment by Paul Fariello [ 22/May/10 ]

I've just added the fail case





[DDC-599] Inheritance breaks cascading Created: 18/May/10  Updated: 07/Jun/10  Resolved: 07/Jun/10

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File DDC599Test.php    

 Description   

When using inheritance, cascade=

{"delete"} does not work anymore:

This example creates three Entities:
- Item
- SubItem (extends Item)
- Child

The Item has a OneToMany association with Child with cascade={"delete"}

, so if an Item is deleted, its Children are deleted too.

http://pastie.org/965096

However this does not work, the cascade is ignored when the Item is deleted. Without inheritance (e.g. only Item with Children) it works perfectly.



 Comments   
Comment by Nico Kaiser [ 18/May/10 ]

By the way, this cannot be reproduced with the included testcases (no DB connection). So the problem may be between the ORM and the DBAL...

Comment by Roman S. Borschel [ 18/May/10 ]

Do you get any error message? exception? stack trace?

Comment by Nico Kaiser [ 18/May/10 ]

PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`kaiser_sandbox`.`Child`, CONSTRAINT `Child_ibfk_1` FOREIGN KEY (`parentId`) REFERENCES `Item` (`id`))' in /home/kaiser/doctrine/doctrine/lib/Doctrine/DBAL/Connection.php:637
Stack trace:
#0 /home/kaiser/doctrine/doctrine/lib/Doctrine/DBAL/Connection.php(637): PDOStatement->execute(Array)
#1 /home/kaiser/doctrine/doctrine/lib/Doctrine/DBAL/Connection.php(385): Doctrine\DBAL\Connection->executeUpdate('DELETE FROM Ite...', Array)
#2 /home/kaiser/doctrine/doctrine/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php(353): Doctrine\DBAL\Connection->delete('Item', Array)
#3 /home/kaiser/doctrine/doctrine/lib/Doctrine/ORM/UnitOfWork.php(777): Doctrine\ORM\Persisters\BasicEntityPersister->delete(Object(Entities\SubItem))
#4 /home/kaiser/doctrine/doctrine/lib/Doctrine/ORM/UnitOfWork.php(316): Doctrine\ORM\UnitOfWork->_executeDeletions(Object(Doctri in /home/kaiser/doctrine/doctrine/lib/Doctrine/DBAL/Connection.php on line 637

Comment by Roman S. Borschel [ 19/May/10 ]

Scheduled for beta2 but not sure we can make it, might end up on beta3. Increasing priority though as this seems to be a bug and these have priority.

Comment by Benjamin Eberlei [ 06/Jun/10 ]

Attached a test-case that verifies this bug exists.

Problem is the CommitOrderNodeCalculator not knowing about sub-class dependencies.





[DDC-593] Subquery parenthesis omitted in generated SQL Created: 16/May/10  Updated: 16/May/10  Resolved: 16/May/10

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

Type: Bug Priority: Critical
Reporter: John Kleijn Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

$dQuery = $this->_em->createQuery(
'SELECT p FROM entity\system\Group p WHERE (p.lft >= (SELECT t.lft FROM entity\system\Group t WHERE t.name = :name)) AND (p.rgt <= (SELECT t2.rgt FROM entity\system\Group t2 WHERE t2.name = :name))');

As you see this includes brackets around the subqueries.

var_dump($dQuery->getSQL());

SELECT s0_.level AS level0, s0_.lft AS lft1, s0_.rgt AS rgt2, s0_.id AS id3, s0_.name AS name4, s0_.description AS description5 FROM system_group s0_ WHERE (s0_.lft >= SELECT s1_.lft FROM system_group s1_ WHERE s1_.name = ?) AND (s0_.rgt <= SELECT s2_.rgt FROM system_group s2_ WHERE s2_.name = ?)

Brackets gone, resulting in:

'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT s1_.lft FROM system_group s1_ WHERE s1_.name = 'root') AND (s0_.rgt <= SE' at line 1' in /usr/share/php/lib/Doctrine/DBAL/Connection.php:566

Brackets added and executed against database

SELECT s0_.level AS level0, s0_.lft AS lft1, s0_.rgt AS rgt2, s0_.id AS id3, s0_.name AS name4, s0_.description AS description5 FROM system_group s0_ WHERE (s0_.lft >= (SELECT s1_.lft FROM system_group s1_ WHERE s1_.name = "root")) AND (s0_.rgt <= (SELECT s2_.rgt FROM system_group s2_ WHERE s2_.name = "root"))

Works (MySQL).



 Comments   
Comment by John Kleijn [ 16/May/10 ]

Double brackets in the DQL results in

"exception 'Doctrine\ORM\Query\QueryException' with message '[Syntax Error] line 0, col 62: Error: Expected Literal, got 'SELECT'' in /usr/share/php/lib/Doctrine/ORM/Query/QueryException.php:42

Comment by Roman S. Borschel [ 16/May/10 ]

This might be caused by recent AST optimizations and thus I suspect this to be a regression.

Comment by John Kleijn [ 16/May/10 ]

Is there a workaround (other than not using a subquery)?

Comment by Roman S. Borschel [ 16/May/10 ]

Well, a workaround for any DQL issues that is always available is a NativeQuery (createNativeQuery). In essence, a DQL query is just a high-level abstraction for a native SQL query + a ResultSetMapping. http://www.doctrine-project.org/projects/orm/2.0/docs/reference/native-sql/en#native-sql

The resulting objects from a native query are still fully managed and all, so its just a difference in query abstraction.

Nevertheless, this should be fixed soon.

Comment by Roman S. Borschel [ 16/May/10 ]

Reproduced this successfully and already have a potential fix. Might not be a regression after all but a bug nevertheless.

Comment by Roman S. Borschel [ 16/May/10 ]

Should be fixed now in HEAD (doctrine2/master).





[DDC-518] Merging an entity with a one to one association to a MANAGED entity with no id throws 'The given entity has no identity.' Created: 13/Apr/10  Updated: 30/Jul/10  Resolved: 30/Jul/10

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

Type: Bug Priority: Critical
Reporter: Dave Keen Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: Text File ddc518.patch    

 Description   

Calling merge($entity) where $entity has a one to one association to another entity that has been persisted but not yet flushed (when using auto-generated ids) throws 'The given entity has no identity.'

It looks like it does this because _doMerge in UnitOfWork assumes for one to one associations only that the associated entity has an id and calls registerManaged, which then calls addToIdentityMap)on it.

I think that registeredManaged should only be called if !isset($this->_entityInsertions[spl_object_hash($other)])

Reproduce.php
// This is a new element
$doctor = new \vo\Doctor(); $d1->name = "New doctor";

// This is a detached element which is in the database
$patient = new \vo\Patient(); $p1->name = "Existing patient"; $p1->id = 1;

$doctor->patients->add($patient);
$patient ->doctor = $doctor;

$em->persist($doctor);

// This throws InvalidArgumentException: The given entity has no identity. in D:\Projects\ORM\flextrine2\flextrine\web\lib\Doctrine\ORM\UnitOfWork.php on line 1014
$em->merge($patient);
Doctor.php
class Doctor {

    /** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
    public $id;
	
    /** @Column(length=100, type="string") */
    public $name;
	
	/**
     * @OneToMany(targetEntity="Patient", mappedBy="doctor", fetch="EAGER")
     */
	public $patients;
	
	public function __construct() {
		$this->patients = new ArrayCollection();
	}
	
}
Patient.php
class Patient {
	
	var $_explicitType = "vo/Patient";
	
    /** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
    public $id;
	
    /** @Column(length=100, type="string") */
    public $name;

	/**
     * @OneToOne(targetEntity="Doctor", inversedBy="patients")
	 * @JoinColumn(name="doctor_id", referencedColumnName="id")
     */
	public $doctor;
	
}


 Comments   
Comment by Roman S. Borschel [ 08/May/10 ]

I think the order of operations in your example is not correct even though the error is misleading.

You are associating a "new doctor" to a "detached patient". That does not seem right, remember that merge() returns a managed copy, thus when merging the patient later, the "new doctor" is still associated with the "detached patient", not with the managed one.

The following should work:

// Merge detached patient
$managedPatient = $em->merge($patient);

// Associate new doctor with patient
$doctor->patients->add($managedPatient);
$managedPatient->doctor = $doctor;

// Persist new doctor and flush
$em->persist($doctor);
$em->flush();
Comment by Dave Keen [ 16/May/10 ]

You are quite right - that does work.

However, I am now trying to implement my use case (turning a tree of detached objects into a tree of managed objects) and implementing the correct order you describe above seems to cause another problem. I am not sure if this should be another ticket, but I'll put it here for the moment.

Note that the part within the stars that creates the unmanaged doctor and the detached patient simulates exactly what is received by Doctrine in my application so I can't put any merges in here.

/********************************************************************/
// This is a new element
$doctor = new \vo\Doctor(); $doctor->name = "New doctor";

// This is a detached element which is in the database
$patient = new \vo\Patient(); $patient->name = "Existing patient"; $patient->id = 1;

$doctor->patients->add($patient); $patient->doctor = $doctor;
/********************************************************************/

// Now replace $patient with its managed version
$managedPatient = $em->merge($patient);
$doctor->patients->set(0, $managedPatient);

// Persist the doctor
$em->persist($doctor);

$em->flush();

This works up to the flush, which throws an error of the form:

Notice: Undefined index: 000000007dd346c3000000005d0908d2 in \Doctrine\ORM\UnitOfWork.php on line 1955

In the database this results in a new doctor being created, but doctor_id in the patients table is set to NULL for the patient that is supposed to be linking to it.

Comment by Benjamin Eberlei [ 06/Jun/10 ]

I think this can't work, because obviously $doctor->patients still points to the unmanaged $patient, not the managed one.

Can you elaborate on why the star part is done by Doctrine? Where is doctrine doing that? what are you doing with the public API?

All the detached entities should be merged before doing anything regarding a NEW entity in my opinion.

Comment by Benjamin Eberlei [ 06/Jun/10 ]

Moved to beta3 for now

Comment by Benjamin Eberlei [ 06/Jun/10 ]

Patch with test-case for this merging scenario

Comment by Roman S. Borschel [ 30/Jul/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/69073c4b37ee28f988306db4965f512b70f45181





[DDC-576] New entities must have primary key values right after flushing with IDENTITY strategy Created: 07/May/10  Updated: 08/May/10  Resolved: 08/May/10

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

Type: Bug Priority: Critical
Reporter: Václav Novotný Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

PostgreSQL 8.4.3, Ubuntu 10.04


Attachments: GZip Archive example.tar.gz    

 Description   

After insertion of some new entity the primary key must be set. Example code is attached above.



 Comments   
Comment by Roman S. Borschel [ 07/May/10 ]

I think I reproduced this already. Seems to be related to PostgreSQL + IDENTITY only. The SequenceIdentityGenerator must be used but apparently it is not used.

Thanks for the report. Will keep you updated.

ps. The standalone reproduce scripts are good. Thanks for that! If you are interested it would be even better if the test code would be provided as a unit test or with some additional comments and/or assertions about what the desired behavior is or where the supposedly wrong behavior is (even if it is very obvious, like in this issue).





[DDC-561] Metadata caching broken due to incomplete __sleep functions Created: 30/Apr/10  Updated: 30/Apr/10  Resolved: 30/Apr/10

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: Text File ddc-561.patch    

 Description   

When using a persistent Metadata cache, the serialized ClassMetadata objects are not complete.

This leads to very strange behavior since not all Metadata is loaded in the next request (which uses cached Metadata). The problem is that the __sleep methods of Doctrine\ORM\Mapping\AssociationMapping and Doctrine\ORM\Mapping\ClassMetadata are note complete (missing "namespace", "fetchMode" properties).



 Comments   
Comment by Nico Kaiser [ 30/Apr/10 ]

This patch fixes the issue for AssociationMapping and ClassMetadata. I'm not sure if there are more properties missing...

Comment by Roman S. Borschel [ 30/Apr/10 ]

Ouch, bad oversight. Sorry for that. We do actually have tests for serializing and unserializing the metadata, obviously not enough... will fix it asap.

Comment by Roman S. Borschel [ 30/Apr/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/db2be55e27c87fa513073b2bf44456f1d1423582 .

Thanks for your help.

Comment by Benjamin Eberlei [ 30/Apr/10 ]

Should we re-release Beta1? This is pretty serious and might annoy people

Comment by Roman S. Borschel [ 30/Apr/10 ]

Hehe, no its fine. You can easily patch it manually if needed and beta2 is only a few weeks away.

Comment by Roman S. Borschel [ 30/Apr/10 ]

And you can just use HEAD and not the tag, of course





[DDC-531] Collections broken in self-referenced Entities Created: 20/Apr/10  Updated: 23/Mar/14  Resolved: 23/May/10

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File DDC531Test.php    
Sub-Tasks:
Key
Summary
Type
Status
Assignee
DDC-597 Add check for public properties in Va... Sub-task Resolved Benjamin Eberlei  

 Description   

When dealing with parent / children entities, the UnitOfWork does not always hydrate all data correctly.

This example generates Group1 and its child Group2. Then Clears the Entity Manager, loads Group2 (so it is in the EM), loads Group1 and then the children of Group1 (Group2 is the child of Group1).
However the children of Group1 cannot be loaded, because $group4->children is not there:

Warning: Invalid argument supplied for foreach() in /Users/nico/Projects/test/test.php on line 20

When calling Debug::dump($group4), I get this:

object(stdClass)#56 (2) {
  ["__CLASS__"]=>
  string(26) "Proxies\EntitiesGroupProxy"
  ["id"]=>
  string(1) "1"
}

test.php
http://pastebin.com/7Q3wwtn6

Group entity:
http://pastebin.com/hBj2Emrf



 Comments   
Comment by Nico Kaiser [ 20/Apr/10 ]

If I clear the Entity Manager between lines 17 and 18, it works... Seems like the previously loaded Group is reused (which is perfectly fine), but its associations (or at least its self-referenced associations) are not loaded (neither proxies are generated)...

Comment by Roman S. Borschel [ 23/Apr/10 ]

Thanks for reporting. I will take a look as soon as I find the time.

Comment by Nico Kaiser [ 23/Apr/10 ]

Test case for Doctrine\Tests\ORM\Functional\Ticket

Comment by Christian Heinrich [ 07/May/10 ]

It might be worth noting that, after renaming $parent to $parent2, the object is loaded but $item4->children remains empty.

If $item3 isn't being fetched beforehands, then everything seems to work fine.

Comment by Christian Heinrich [ 12/May/10 ]

After investigating, it came to my mind that the test submitted uses

$proxy->publicProperty

instead of

$proxy->getPublicProperty()

Therefore, the proxy is not initialized and thus we get unexpected behaviour. Adding a getter a la "getChildren()" and calling this method only, everything works fine.

Therefore, I mark this ticket as invalid.

Comment by Nico Kaiser [ 12/May/10 ]

Shouldn't these properties be automagically instantiated? In our project, we did use getChildren() and it did not work either (sorry, can't provide test case now, will do on monday).

I think collections are populated automatically everywhere (you can always use $this->children in entities), so this should work here as well.

Comment by Christian Heinrich [ 12/May/10 ]

The problem here is, that you're not really using an entity but a proxy object. A proxy object does not initialize its collections, see here: http://www.doctrine-project.org/documentation/manual/2_0/en/best-practices#don%27t-use-public-properties-on-entities

As long as you're using normal entities, the PersistentCollections will normally lazy load, thats true. But as soon as you're using proxies - like in this case - you're running into severe problems. I strongly recommend not using public properties.

Comment by Nico Kaiser [ 17/May/10 ]

You are right - if I use getChildren and a "protected $children", this example works.
However, if I use inheritance (e.g. SINGLE_TABLE), it breaks again, even if I don't use sub items. I'll update the test case.

Comment by Nico Kaiser [ 17/May/10 ]

Updated DDC531Test.php to use SINGLE_TABLE inheritance. Breaks again, even if I don't use public members for the collection...

Comment by Christian Heinrich [ 17/May/10 ]

Hi Nico,

thanks for your response. I will take a look at this issue shortly and will hopefully resolve it before beta2.

regards
christian

Comment by Roman S. Borschel [ 20/May/10 ]

Thanks for your investigation. I tracked down the issue and have a fix pending.

Note though, that with such an example as in the provided testcase, the parents can never be lazy. That means all parents will be eagerly loaded. Of course you can work around that by eager-joining them in DQL or by using Query::HINT_FORCE_PARTIAL_LOAD and things like that.

The reason why the parents in the example can not be lazy is because a parent can potentially be of any subtype, so you would not know which proxy to put in. In general, a single-valued association that points to another entity that has mapped subclasses can not be lazy. This "problem" does not occur when the targeted entity has no mapped subclasses (this means it either does not participate in a mapped inheritance hierarchy or it is a leaf in the hierarchy).

See also: http://www.doctrine-project.org/jira/browse/DDC-357

Comment by Roman S. Borschel [ 23/May/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/616f2eda0af1a15ba205cc5013b5f001c34dfc55

Comment by Doctrine Bot [ 23/Mar/14 ]

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





[DDC-500] Single Table Inheritance Selects Created: 07/Apr/10  Updated: 26/Apr/10  Resolved: 26/Apr/10

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

Type: Bug Priority: Critical
Reporter: Michael Ridgway Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File DDC500Test.php    
Issue Links:
Reference
is referenced by DDC-497 find() and findAll() on Repository do... Closed

 Description   

We have a set of models that use Single Table inheritance and we are trying to select all objects of one type:

/**
 * @Entity
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"Child"="Child", "OtherChild"="OtherChild"})
*/
abstract class ParentModel
{
    /**
     * @Id @Column(name="id", type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** @Column(type="string") */
    public $property;

    function getId() {return $this->id;}
}

abstract class SubParent extends ParentModel
{
}

/**
 * @Entity
 */
class Child extends SubParent
{
    /** @Column(type="string") */
    public $anotherProperty;
}

/**
 * @Entity
 */
class OtherChild extends SubParent
{
    /** @Column(type="string") */
    public $someOtherProperty;
}

Now to query for all of the Child objects we do:

$children = $em->getRepository('Child')->findall();
foreach($children AS $child) {
    echo $child->getId();
}

but we get "Notice: Undefined index: id in ./Doctrine/ORM/UnitOfWork.php on line 1727" on the finAll() call and the objects that aren't of the correct type have their properties nulled out. This is because the query that is being executed doesn't have any conditionals for the type of model it's looking for and the ORM doesn't check to make sure that the object is of the correct type.

This could be solved by having conditionals in the SQL query (chaining a bunch of 'or' statements for all of the child objects) or by pulling back all of the objects and then filtering out what isn't of the correct type. Unfortunately neither solution seems ideal.

I'll try to make a test case for this then.



 Comments   
Comment by Michael Ridgway [ 07/Apr/10 ]

Adding another child class just to be clear.

Comment by Roman S. Borschel [ 07/Apr/10 ]

Related to DDC-497 ? Of course there should be conditionals in the query when querying for a subtype. It really surprises me that this seems not to be the case. Maybe there has been some regression.

Comment by Michael Ridgway [ 07/Apr/10 ]

Attached a unit test that may or may not work.

It looks to be a similar issue for sure.

Comment by Michael Ridgway [ 08/Apr/10 ]

Fixed test case. Now gives the 'Undefined index: id' error.

Comment by Roman S. Borschel [ 19/Apr/10 ]

Reproduced successfully and working on it.

Comment by Roman S. Borschel [ 26/Apr/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/760ea34a0cc3cae4e3caea17e8aab6ceb74ecace





[DDC-497] find() and findAll() on Repository do not work when SINGLE_TABLE inheritance is used Created: 05/Apr/10  Updated: 26/Apr/10  Resolved: 26/Apr/10

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

Type: Bug Priority: Critical
Reporter: Marcus Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

MacOsX 10.6.2 - Zend Server 4.0.6 - PHP 5.3 - MySql 5.1.40


Issue Links:
Reference
relates to DDC-500 Single Table Inheritance Selects Closed

 Description   
 
Class Orm\Models\Car:

	@Entity
	@Table(name="cars")
	@InheritanceType("SINGLE_TABLE")
 	@DiscriminatorColumn(name="discr", type="string")
	@DiscriminatorMap({"car" = "Orm\Models\Car", "bluecar" = "Orm\Models\BlueCar"})

Class Orm\Models\BlueCar extends Orm\Models\Car:
	
	@Entity

Now my Database holds 4 records:
	
	id 	title 		discr
	1 	blue car 	bluecar
	2 	only Car 	car
	5 	blue car2 	bluecar
	6 	only Car2 car

Now querying objects by Repository leads to some errors.

	
	//quering for all BlueCars
	$em->getRepository('Orm\Models\BlueCar')->findAll();
	
	//the result set counts 4 objects
	blue car	: Orm\Models\BlueCar
			: Orm\Models\Car
	blue car2	: Orm\Models\BlueCar
			: Orm\Models\Car

	//The 2 Car Items where the title is missing are to much for this result set, and useless
	// there is also a php notice coming up for each Orm\Models\Car object
	Notice: Undefined index: id in /library/Doctrine/ORM/UnitOfWork.php  on line 1727
	I use mysql as Database and so the sql query looks like this.

	"SELECT c0.id AS id1, c0.title AS title2, discr FROM cars c0"
	
	It seems that the query is missing the where discriminator = "bluecar" part

querying for Car
	$em->getRepository('Orm\Models\Car')->findAll();
	
	blue car: Orm\Models\BlueCar
	only Car: Orm\Models\Car
	blue car2: Orm\Models\BlueCar
	only Car2: Orm\Models\Car

	Is working without errors but i think it is a mere chance if you look at the query
	"SELECT c0.id AS id1, c0.title AS title2, discr FROM cars c0" it is the same.

	I am not shure if it is intended that quering for Cars also returns BlueCars but due to the behavior when using DQL for quering, i guess it is right.
	That means, when using DQL:

	$q = $em->createQuery('select u from Orm\Models\BlueCar u');
	$results = $q->execute();

	everything works as expected, so using a custom EntityRepository and override find, findAll and so on is a workaround. So this is a minor bug ?


 Comments   
Comment by Roman S. Borschel [ 05/Apr/10 ]

You mention ALPHA4 as the affected version. Did you test this with trunk?

Comment by Roman S. Borschel [ 05/Apr/10 ]

@"I am not shure if it is intended that quering for Cars also returns BlueCars but due to the behavior when using DQL for quering, i guess it is right."

Of course. Anything else would be wrong. If you query for cars you get all cars. BlueCars are Cars.

Comment by Marcus [ 07/Apr/10 ]

Tested with Revision: 7533.

Comment by Roman S. Borschel [ 26/Apr/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/760ea34a0cc3cae4e3caea17e8aab6ceb74ecace





[DDC-481] Incorrect table aliasing when using quoting on table names Created: 29/Mar/10  Updated: 14/Jun/10  Resolved: 08/May/10

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers, ORM
Affects Version/s: 2.0-ALPHA4
Fix Version/s: 2.0-BETA2
Security Level: All

Type: Bug Priority: Critical
Reporter: Menno Luiten Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

Doctrine trunk (r7479)


Attachments: Text File DDC-481.patch    

 Description   

I need to quote several table names (because of reserved words within MySQL) like so:

 
/**
 * @Entity
 * @Table(name="`Column`")
 */
class Column extends OptionAbstract

Another entity Question is related one-to-many to these entities, however, when trying to fetch the relation I get SQL errors because it seems to use the backtick as the table alias, which is illegal. An example of the query it's generating:

SELECT `0.id AS id1, `0.name AS name2, `0.sequence AS sequence3, `0.question_id AS question_id4 FROM `Column` `0 WHERE question_id = ?


 Comments   
Comment by Menno Luiten [ 29/Mar/10 ]

Here's a patch that works for me; looping through the table name until there is a character in the range a-z (case insensitive) to use as an alias.

Might need some more error-reporting and endless loop protection etc.

Comment by Roman S. Borschel [ 29/Mar/10 ]

Thanks but the problem is elsewhere. The table name should not have the quote characters. The quote characters should be stripped during metadata parsing and instead a flag quoted => true set for the table in ClassMetadata::$table. Its not hard to fix, we will look into it soon.





[DDC-448] Cannot select rows from chield table by JoinColumn in @InheritanceType("JOINED") parent table Created: 20/Mar/10  Updated: 13/Apr/10  Resolved: 12/Apr/10

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

Type: Bug Priority: Critical
Reporter: Uvarov Michael Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

This bug is connected with http://www.doctrine-project.org/jira/browse/DDC-416
I have this schema.

/**
 * @Entity
 * @InheritanceType("JOINED") 
 * @DiscriminatorColumn(name="discr", type="smallint")
 * @DiscriminatorMap({
 * "0" = "mainTable",
 * "1" = "SubTable"
 * })
 */
class mainTable
{  	
    /**
     * @Id
     * @Column(name="id", type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ManyToOne(targetEntity="connectedClass",  cascade={"all"}, fetch="EAGER")
     * @JoinColumn(name="connectedClassId", referencedColumnName="id", onDelete="CASCADE", onUpdate="CASCADE", nullable=true)
     */
    private $connectedClassId;
}

/**
 * @Entity
 * @Table(name="connectedClass")
 * @HasLifecycleCallbacks
 */
class connectedClass
{	
    /**
     * @Id
     * @Column(name="id", type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id; // connected with mainTable
}

/**
 * @Entity
 * @Table(name="SubTable")
 */
class SubTable extends mainTable
{
}


$qb->select(array('b'))
       ->from('SubTable', 'b')
       ->where(
           $qb->expr()->eq('b.connectedClassId', '?1') // select by JoinColumn field does not work
                                                                                          // select by normal column work (after http://www.doctrine-project.org/jira/browse/DDC-416  ) 
       )
       ->setParameter(1, $value); // $value - const or connectedClass object


Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'i1_.connectedClassId' in 'where clause'' in /var/www/shelly/library/Doctrine/DBAL/Connection.php:573
Stack trace:
#0 /var/www/shelly/library/Doctrine/DBAL/Connection.php(573): PDOStatement->execute(Array)
#1 /var/www/shelly/library/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php(42): Doctrine\DBAL\Connection->execute('SELECT p0_.id A...', Array)
#2 /var/www/shelly/library/Doctrine/ORM/Query.php(231): Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute(Object(Doctrine\DBAL\Connection), Array)
#3 /var/www/shelly/library/Doctrine/ORM/AbstractQuery.php(514): Doctrine\ORM\Query->_doExecute(Array)
#4 /var/www/shelly/library/Doctrine/ORM/AbstractQuery.php(391): Doctrine\ORM\AbstractQuery->execute(Array, NULL)

So, we cannot select rows by JoinColumn from Inheritance tables, because doctrine selects SubTable.connectedClassId field, not mainTable.connectedClassId

This bug was fix in http://www.doctrine-project.org/jira/browse/DDC-416 patch, but it was not commited. I may write unit test for this problem



 Comments   
Comment by Guilherme Blanco [ 12/Apr/10 ]

In http://github.com/doctrine/doctrine2/commit/56a8f5cd5353908b815607a6e089201c95e01e6c this issue was fixed!

Thanks for the report!

Comment by Roman S. Borschel [ 13/Apr/10 ]

The testcase is irritating though, because connectedClassId is an object, not an id.





[DDC-419] Problem when I make a INNER JOIN between 2 classes Created: 12/Mar/10  Updated: 08/Aug/10  Resolved: 08/Aug/10

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

Type: Bug Priority: Critical
Reporter: Henrique Girardi dos Santos Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

Ubuntu 9.10
PHP 5.3.1-0.dotdeb.1 with Suhosin-Patch (cli) (built: Dec 5 2009 20:08:29)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies
with Xdebug v2.0.5, Copyright (c) 2002-2008, by Derick Rethans
with Suhosin v0.9.29, Copyright (c) 2007, by SektionEins GmbH
PHPUnit 3.4.6 by Sebastian Bergmann


Attachments: File DDC419Test.php     File TestCase.tar.bz2    
Issue Links:
Dependency
depends on DDC-522 Join columns can not be named the sam... Closed

 Description   

I hae some problems when I make a inner join between 2 classes, one of these classes makes join to others 2 classes in the same time and when I try to make a inner to this class, it's not coming like a object, but object Id...

I don't know why its happening..

I make a test case..there're all the classes and yml that show my problem..it's on ItemPregaoTest.php.. just need to run phpunit in this file..



 Comments   
Comment by Benjamin Eberlei [ 14/Mar/10 ]

This is what returns for me:

object(stdClass)[82]
  public '__CLASS__' => string 'ItemPregao' (length=10)
  public 'id' => int 1
  public 'item' => string '1' (length=1)

The expected result I guess. What happens for you?

Comment by Henrique Girardi dos Santos [ 14/Mar/10 ]

Ok...this is what happen for me...
but I think the expected result would be

object(stdClass)[82]
  public '__CLASS__' => string 'ItemPregao' (length=10)
  public 'id' => int 1
  public 'item' => string 'Item' (length=4)

because 'public item' makes refereces to Item class, so I think it should give me a Item class, not its Id...

Comment by Henrique Girardi dos Santos [ 16/Mar/10 ]

When I try to make a inner join between Item and ItemPregao, happens what I've said before, but if I make a inner join between Item and its refereces tables, it's working fine

$qb = $this->em->createQueryBuilder();
        $qb->select('i, cc, ca, cl')
           ->from('Item', 'i')
           ->innerJoin('i.classificacaoCatalogo', 'cc')
           ->innerJoin('cc.catalogo', 'ca')
           ->innerJoin('cc.classificacao', 'cl')
        ;

object(stdClass)[141]
  public '__CLASS__' => string 'Item' (length=4)
  public 'id' => int 1
  public 'descricao' => string 'ITEM 1' (length=6)
  public 'classificacaoCatalogo' => 
    object(stdClass)[99]
      public '__CLASS__' => string 'ClassificacaoCatalogo' (length=21)
      public 'id' => int 1
      public 'classificacao' => string 'Classificacao' (length=13)
      public 'catalogo' => string 'Catalogo' (length=8)

It brings a class on 'classificacaoCatalogo' and inside classificacaoCatalogo you can see that 'classificacao' and 'catalogo' are objects too..so it worked! But If I make this:

$qb = $this->em->createQueryBuilder();
        $qb->select('ip, i')
           ->from('ItemPregao', 'ip')
           ->innerJoin('ip.item', 'i');

do not work like before..

object(stdClass)[152]
  public '__CLASS__' => string 'ItemPregao' (length=10)
  public 'id' => int 1
  public 'item' => int 1

'item' should be a object, not a integer..

Comment by Benjamin Eberlei [ 16/Mar/10 ]

Ah now I see it. Yes, this seems to be a problem.

Comment by Benjamin Eberlei [ 18/Mar/10 ]

I have taken some time now trying to re-produce it, there is definately a bug i have identified, however i havent found the reason yet.

Comment by Henrique Girardi dos Santos [ 18/Mar/10 ]

ok man!
I've try to find the reason too but I havent found yet too...
I dont know if there's something about the item are conected to a table that there're 2 table...
i dont know :S

Comment by Benjamin Eberlei [ 20/Mar/10 ]

I found the issue. A quick fix is to rename the join column to "item_id" instead of "item". This is causing the bug, we will look how to fix it.

@Roman:

This is particularly nasty, in UnitOfWork::createEntity the join column is set to the field name in $data, which makes the following code write the primitive FK value to the entity:

            if ($this->_useCExtension) {
                doctrine_populate_data($entity, $data);
            } else {
                foreach ($data as $field => $value) {
                    if (isset($class->reflFields[$field])) {
                        $class->reflFields[$field]->setValue($entity, $value);
                    }
                }
            }

This then leads in the ObjectHydrator to:

                    // PATH B: Single-valued association
                    $reflFieldValue = $reflField->getValue($parentObject);
                    if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {

not evaluating to null, but to the primitive value. the If condition does not match here and the relation is never set.

A simple solution would be to replace !$reflFieldValue with !is_object().

Comment by Benjamin Eberlei [ 20/Mar/10 ]

Attached a reproduce test-case.

Comment by Roman S. Borschel [ 20/Mar/10 ]

I see. As their is an easy workaround, this is not a blocker, however.

Comment by Roman S. Borschel [ 15/Apr/10 ]

DDC-522 needs to be fixed, then this issue is solved, too (its the same after all, the other issue is just more specific).

Comment by Roman S. Borschel [ 15/Apr/10 ]

Scheduled for beta2

Comment by Roman S. Borschel [ 08/Aug/10 ]

Should be fixed now in master.





[DDC-388] Private properties in @MappedSupperclass don't work Created: 03/Mar/10  Updated: 14/Apr/10  Resolved: 14/Apr/10

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

Type: Bug Priority: Critical
Reporter: Jaka Jancar Assignee: Roman S. Borschel
Resolution: Fixed Votes: 2
Labels: None
Environment:

PHP 5.3.1


Issue Links:
Duplicate
is duplicated by DDC-456 Wrong implementation of loading metad... Closed

 Description   

All of my entites extend an abstract class Model to inherit the 'id' property and some other functionality. It has the @MappedSuperclass annotation.

If the id property is declared private to the Model class, I'll get the following exception when using entites extending it:

[03-Mar-2010 11:36:29] exception 'ReflectionException' with message 'Property id does not exist' in library/Doctrine/ORM/Mapping/ClassMetadata.php:370
Stack trace:
#0 library/Doctrine/ORM/Mapping/ClassMetadata.php(370): ReflectionClass->getProperty('id')
#1 [internal function]: Doctrine\ORM\Mapping\ClassMetadata->__wakeup()
...

I haven't looked at Doctrine code, but perhaps you should be looking for the property on the class which actually has the @Column annotation?



 Comments   
Comment by Roman S. Borschel [ 03/Mar/10 ]

Yes, I think the 'inherited' key is simply not set for fields that are inherited from mapped superclasses. This needs to be fixed.

Comment by Roman S. Borschel [ 14/Apr/10 ]

Fixed in http://github.com/doctrine/doctrine2/commit/d4232d906e433b1fe4dd8aa85aa7a4aca3a2cf4c .
Make sure to clear the metadata cache if necessary.





[DDC-335] Refactor DQL EBNF to use JOIN FETCH as syntax for fetch joins only Created: 14/Feb/10  Updated: 19/Feb/10  Resolved: 19/Feb/10

Status: Closed
Project: Doctrine 2 - ORM
Component/s: DQL
Affects Version/s: 2.0-ALPHA4
Fix Version/s: 2.0-BETA1
Security Level: All

Type: Improvement Priority: Critical
Reporter: Benjamin Eberlei Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Dependency
is required for DDC-195 Ordering of associations Resolved

 Description   

There are several problems with the current approach on fetch joins:

1. There is no way to determine in the parser already if a join is fetch or not, this makes it much harder to implement things like @OrderBy or @OrderColumn
2. A DQL like "SELECT u, g.name FROM User u JOIN u.group g" currently tries to partially load the group object instead of just returning g.name as scalar. This is very unintuiative.

Solution, change the EBNF too:

Join                                       ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" [FETCH] JoinAssociationPathExpression 
                                               ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]

Question would be how to specify partial object selects.

Romans idea was something like:

select u.{name, other, field, stuff}

However my take is this would again put information into the select clause that might be interesting elsewhere, so

SELECT u FROM User u JOIN FETCH u.group PARTIAL id, name

That way we could add both $isFetchJoin and $isPartial flags to the AST/Join Node plus an additional $partialFields array.



 Comments   
Comment by Benjamin Eberlei [ 16/Feb/10 ]

Ah just to remember, the idea on the partial fetch syntax was something like:

select u FROM User u.{name, other, field, stuff} JOIN FETCH u.group g.{id, name, baz}
Comment by Benjamin Eberlei [ 16/Feb/10 ]

I think this will also make the HINT_PARTIAL_LOAD constant obsolet, since the DQL is already implicitly requesting a partial load.

Comment by Roman S. Borschel [ 16/Feb/10 ]

Regarding HINT_FORCE_PARTIAL_LOAD: Not necessarily, but it might need to be renamed since its still used to decide whether to stub associations with empty collections/proxies.

Anyways, I've played around a bit with different implementations in the last days and I changed my mind regarding changing the fetch join syntax. It has many complications, like more difficult sql construction in the walker, among other things, and of course the fact that it would be a huge bc break.

However, some things will change. "select u.name" will select a scalar value, as you expect. The new syntax for partial object selection will currently be:

select partial u.{id,name}, partial a.{id,city} from User u join u.address a

There will be a new AST node PartialObjectExpression that represents such a construct.

Thats the current state of my progress. Regarding the isFetchJoin/isPartial decisions, we need to find another way and I'm confident there are some other ways.

So far...

Comment by Benjamin Eberlei [ 16/Feb/10 ]

If we change u.name to retrieving a scalar value always its easy to get all the fetch joins identification variables inside the SELECT already:

If (identificiationVariable) => fetchJoin

Then when the join is found, you can mark the Join as Fetch already.

Comment by Roman S. Borschel [ 19/Feb/10 ]

Implemented.





[DDC-317] Using a function only in hydration returns only one result Created: 11/Feb/10  Updated: 19/Feb/10  Resolved: 19/Feb/10

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

Type: Bug Priority: Critical
Reporter: Benjamin Eberlei Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Output of $arg:

array(1) {
  [-1]=>
  array(1) {
    ["namedep"]=>
    string(25) "Jonathan W.Administration"
  }
}
    public function testConcatFunction()
    {
        $this->generateFixture();
        $arg = $this->_em->createQuery('SELECT CONCAT(m.name, m.department) AS namedep FROM Doctrine\Tests\Models\Company\CompanyManager m')
                         ->getArrayResult();

        $this->assertEquals(4, count($arg)); // fails with 1
    }

    protected function generateFixture()
    {
        $manager1 = new CompanyManager();
        $manager1->setName('Roman B.');
        $manager1->setTitle('');
        $manager1->setDepartment('IT');
        $manager1->setSalary(100000);

        $manager2 = new CompanyManager();
        $manager2->setName('Benjamin E.');
        $manager2->setTitle('');
        $manager2->setDepartment('HR');
        $manager2->setSalary(200000);

        $manager3 = new CompanyManager();
        $manager3->setName('Guilherme B.');
        $manager3->setTitle('');
        $manager3->setDepartment('Complaint Department');
        $manager3->setSalary(400000);

        $manager4 = new CompanyManager();
        $manager4->setName('Jonathan W.');
        $manager4->setTitle('');
        $manager4->setDepartment('Administration');
        $manager4->setSalary(800000);

        $this->_em->persist($manager1);
        $this->_em->persist($manager2);
        $this->_em->persist($manager3);
        $this->_em->persist($manager4);
        $this->_em->flush();
        $this->_em->clear();
    }


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

Additional bug, changing the DQL to:

SELECT CONCAT(m.name, m.department) AS namedep FROM Doctrine\Tests\Models\Company\CompanyManager m WHERE m.id = 4

returns an empty result.

Comment by Benjamin Eberlei [ 11/Feb/10 ]

Thie following DQL gives a partial error bug:

SELECT m, m.salary+2500 AS add FROM Doctrine\Tests\Models\Company\CompanyManager m

Error:

Doctrine\Tests\ORM\Functional\QueryDqlFunctionTest::testOperatorAdd()
Exception: [Doctrine\Common\DoctrineException] Loading partial objects is dangerous. Fetch full objects or consider using a different fetch mode. If you really want partial objects, set the doctrine.forcePartialLoad query hint to TRUE.
Comment by Benjamin Eberlei [ 11/Feb/10 ]

The same goes for - * and /

Comment by Benjamin Eberlei [ 13/Feb/10 ]

Generated SQL of the CONCAT Example:

SELECT CONCAT(c0_.name, c1_.department) AS sclr0 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id
Comment by Roman S. Borschel [ 19/Feb/10 ]

Should be fixed now.





[DDC-263] YAML and XML Driver do not support cascading options "all", "detach". Also: Improvement of YAML Syntax (Patch attached) Created: 19/Jan/10  Updated: 22/Jan/10  Resolved: 22/Jan/10

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: 2.0-ALPHA3
Fix Version/s: 2.0-ALPHA4
Security Level: All

Type: Bug Priority: Critical
Reporter: Christian Heinrich Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: Text File MappingDriversCascade.patch     Text File MappingDriversCascade.patch     Text File YamlDriver_change_cascade_syntax.php.patch    

 Description   

Hi there,

I've attached a small patch which addresses the following issues:

-> YAML did not support cascading options "all" (as described in documentation) and "detach" (as used in AssociationMapping class). The attached patch fixes that.

-> XML does not support these things either, but the attached patch DOES NOT FIX THAT (see below).

Also addressed:

YAML had syntax like cascadeRemove, cascadePersist etc., but the documentation for the annotation driver uses only "persist, remove" etc.

This patch changes the syntax to the following:

cascade: [ persist, remove ]

OR

cascade:

  • persist
  • remove

(Both work!)

It is now also possible to use cascade: [all]

The YAML Driver was changed in that way that he must not know about the currently supported cascate actions. This has been pushed towards the AssociationMapping and I strongly believe that this is the right way.

On the other hand, I did not change the XML Driver functionality. It does still know about the cascade actions and does most probably not support the detach and "all" actions. That should be fixed, but won't take too long.



 Comments   
Comment by Christian Heinrich [ 22/Jan/10 ]
  • YAML Driver fixed.
  • XML Driver fixed (supports now both cascade-persist and (because the other drivers do not use the prefixed cascade-) single "persist")
  • I've modified the UnitTests to cover cascade="all" for XML & YAML
Comment by Roman S. Borschel [ 22/Jan/10 ]

That patch looks good but there seem to be tabs in your patches. Please make sure that you use "soft tabs" (4 spaces) in your editor.

Comment by Christian Heinrich [ 22/Jan/10 ]

Resolved tab issue.

Comment by Roman S. Borschel [ 22/Jan/10 ]

Patch applied. Thanks! So this will be included in todays ALPHA4 release.





[DDC-222] Create unit tests for CLI components Created: 22/Dec/09  Updated: 16/Apr/14  Resolved: 16/Apr/14

Status: Closed
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.0-ALPHA3
Fix Version/s: 2.x
Security Level: All

Type: Task Priority: Critical
Reporter: Roman S. Borschel Assignee: Guilherme Blanco
Resolution: Invalid Votes: 0
Labels: None

Issue Links:
Reference
is referenced by DDC-359 Specified, but empty CLI Options --op... Resolved

 Comments   
Comment by Roman S. Borschel [ 19/May/10 ]

Whats the status here? Do we have any?

Comment by Guilherme Blanco [ 19/May/10 ]

Since we moved to Symfony Console I don't think this is needed anymore.
The purpose of this ticket was actually to test our own CLI support, which was dropped.

I'm closing the ticket due to this. Reopen if you have any other comment.

Comment by Benjamin Eberlei [ 20/May/10 ]

I think we do need some basic functional tests of our Commands, they have been subject to many bugs in the past becaues they are not tested.

Comment by Benjamin Eberlei [ 19/Jun/10 ]

Fixed another fatal error in the command due to missing namespace dependency. We need tests for all the commands, there have been dozens of issues on these things so far.

This commit shows a simple approach on how testing is easily possible for symfony commands:

http://github.com/doctrine/doctrine2/commit/51e6681934a7cf4448b85c5670c04045f66c6056

Comment by Roman S. Borschel [ 26/Aug/10 ]

Can we expect some more tests for beta4 or is it unlikely that you find the time? Should we move this further back or does someone else want to step in?

Comment by Guilherme Blanco [ 16/Apr/14 ]

This is not valid anymore as we use Symfony CLI component





[DDC-193] Remove ClassMetadataInfo#inverseMappings Created: 06/Dec/09  Updated: 10/Apr/10  Resolved: 10/Apr/10

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers, ORM
Affects Version/s: 2.0-ALPHA3
Fix Version/s: 2.0-BETA1
Security Level: All

Type: Improvement Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 1
Labels: None

Attachments: Text File ddc193.patch    

 Description   

The ClassMetadataInfo#inverseMappings attribute is an unnecessary complication.

The problem comes down to: "Given an association $assoc, how do I know if the association is bidirectional?".

If $assoc is the inverse side ($assoc->isInverseSide()), its clear, its bidirectional. But if $assoc is the owning side, we dont know.
What we currently do to determine that in such a case looks as follows:

            if (isset($targetClass->inverseMappings[$assoc->sourceEntityName][$assoc->sourceFieldName])) {
                // Bi-directional
            }

So we check whether the target class has an inverse mapping to the source class. This is quite cumbersome. Additionally, we're usually only interested in the sourceFieldName of the inverse association.

Therefore, it would simplify internal code a lot if we simply introduced a "inversedBy" attribute on an association that is used on the owning side and that corresponds to "mappedBy" on the inverse side. Example:

/** @Entity */
class A {
...
   /**
    * @OneToOne(targetEntity="Other", inversedBy="a")
    * @JoinColumn(name="other_id", referencedColumnName="id")
    */
   private $other;
...
}
...
/** @Entity */
class Other {
    /** @OneToOne(targetEntity="A", mappedBy="other") */
    private $a;
}

This breaks BC. However, I think this is really necessary as it simplifies and speeds up the code, together with removing the inverseMappings attribute altogether.

So this should happen before beta.



 Comments   
Comment by Roman S. Borschel [ 31/Mar/10 ]

Patch is ready and will be committed within the next week.





[DDC-199] collection-valued, polymorphic fetch joins broken (hydration) Created: 07/Dec/09  Updated: 07/Dec/09  Resolved: 07/Dec/09

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

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Polymorphic fetch-joins of collections are currently broken in the ObjectHydrator.






[DDC-187] JOIN fails with inherited entities Created: 02/Dec/09  Updated: 03/Dec/09  Resolved: 03/Dec/09

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

"Entity\User\AbstractUser" is a base entity for user records, "Entity\User\Person" extends AbstractUser (Single Table inheritance in this case).
The "Setting" property is a OneToOne relation with another entity.

Now when I call this DQL:
SELECT u, t FROM Entity\User\AbstractUser u LEFT JOIN u.Setting t

The ObjectHydrator fails in line 276 ("if ( ! $relation->isOneToOne()) {"...) with "PHP Fatal error: Call to a member function isOneToOne() on a non-object in .../lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php".

It works when using the sub class for the query:
SELECT u, t FROM Entity\User\Person u LEFT JOIN u.Setting t



 Comments   
Comment by Nico Kaiser [ 02/Dec/09 ]

It seems like ObjectHydrator::_hydrateRow tries to find a _ce entry for AbstractUser (but _ce only holds entries for Person)...

Comment by Roman S. Borschel [ 02/Dec/09 ]

Does Setting use inheritance also? TopnewsSetting extends Setting? If so which inheritance type?

Comment by Roman S. Borschel [ 02/Dec/09 ]

Can we see the classes and their mappings? AbstractUser, User, Setting and TopnewsSetting. Then I can set up a test to reproduce it. Thanks!

Comment by Nico Kaiser [ 02/Dec/09 ]

Oh no I forgot to change TopnewsSetting to Setting in this report, there is no inheritance, just copy & paste bug.

Comment by Nico Kaiser [ 03/Dec/09 ]

Here are the classes and example code:

http://pastie.org/private/ljkiowarvm9trhdyug903q

Comment by Roman S. Borschel [ 03/Dec/09 ]

Successfully reproduced in our test suite and working on it.

Comment by Roman S. Borschel [ 03/Dec/09 ]

Should be fixed now. Thanks for reporting.





[DDC-171] Cascade persist update foreign key for all unchanged entities Created: 23/Nov/09  Updated: 10/Dec/09  Resolved: 10/Dec/09

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

Type: Bug Priority: Critical
Reporter: Eric Durand-Tremblay Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Using cascade persist, linked entities get updated even if there is no change made.

I get an update on the unchanged foreign key even if I don't modify
the entity at all.

UPDATE email SET client_id = ? WHERE id = ?
$query = $qb->getQuery();
$client = $query->getSingleResult();
$em->flush(); 


 Comments   
Comment by Roman S. Borschel [ 09/Dec/09 ]

Can we see the query? I mean the DQL query! Thanks.

Comment by Roman S. Borschel [ 09/Dec/09 ]

Nevermind. Got it reproduced.

Comment by Eric Durand-Tremblay [ 09/Dec/09 ]

I tried with revision 6913 and I still reproduce the problem.

Is there anyway I can use your UniTest framework, that way, I could check your test case and maybe suggest improvement.

Comment by Eric Durand-Tremblay [ 09/Dec/09 ]

I managed to run the UnitTests.

Do you have a test for this specific issue ?

Comment by Eric Durand-Tremblay [ 09/Dec/09 ]

Here is my result for Doctrine/Tests/ORM/AllTests.php

1) testOptimisticTimestampSetsDefaultValue(Doctrine\Tests\ORM\Functional\Locking\OptimisticTest)
strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/New_York' for 'EST/-5.0/no DST' instead
/home/eric/doctrine_trunk/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php:126

2) testOptimisticTimestampFailureThrowsException(Doctrine\Tests\ORM\Functional\Locking\OptimisticTest)
DateTime::createFromFormat(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/New_York' for 'EST/-5.0/no DST' instead
/home/eric/doctrine_trunk/lib/Doctrine/DBAL/Types/DateTimeType.php:33
/home/eric/doctrine_trunk/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:225
/home/eric/doctrine_trunk/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php:239
/home/eric/doctrine_trunk/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php:112
/home/eric/doctrine_trunk/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:102
/home/eric/doctrine_trunk/lib/Doctrine/ORM/AbstractQuery.php:511
/home/eric/doctrine_trunk/lib/Doctrine/ORM/AbstractQuery.php:387
/home/eric/doctrine_trunk/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php:136

3) testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray(Doctrine\Tests\ORM\Functional\Ticket\DDC168Test)
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'salary' cannot be null
/home/eric/doctrine_trunk/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php:190
/home/eric/doctrine_trunk/lib/Doctrine/ORM/UnitOfWork.php:698
/home/eric/doctrine_trunk/lib/Doctrine/ORM/UnitOfWork.php:283
/home/eric/doctrine_trunk/lib/Doctrine/ORM/EntityManager.php:288
/home/eric/doctrine_trunk/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php:34
Comment by Roman S. Borschel [ 09/Dec/09 ]

You need to fix your timezone settings but that is unrelated to this problem.

To reproduce it, I added the following test temporarily to Doctrine\Tests\ORM\Functional\BasicFunctionalTest.php:

    public function testFlushDoesNotIssueUnnecessaryUpdates()
    {
        
        $user = new CmsUser;
        $user->name = 'Guilherme';
        $user->username = 'gblanco';
        $user->status = 'developer';
        
        $address = new CmsAddress;
        $address->country = 'Germany';
        $address->city = 'Berlin';
        $address->zip = '12345';
        
        $address->user = $user;
        $user->address = $address;
        
        $this->_em->persist($user);
        
        $this->_em->flush();
        $this->_em->clear();
        
        // show SQL from here on...
        $this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);
        
        
        $query = $this->_em->createQuery('select u, a from Doctrine\Tests\Models\CMS\CmsUser u join u.address a');
        echo "QUERY!".PHP_EOL;
        $user2 = $query->getSingleResult();
        
        echo "FLUSH!".PHP_EOL;
        $this->_em->flush();
        
    }

However, since I did not really know how to check that, keeping this test in didnt make much sense.

Can you try this test? Before my patch it issued an UPDATE query but not after my changes.

It is pretty difficult to test this in an isolated manner.

Comment by Benjamin Eberlei [ 09/Dec/09 ]

First two failures are your fault, you havent set the default timezone in php.ini

The last failure is unrelated, its a benchmark for another bug.

Comment by Roman S. Borschel [ 09/Dec/09 ]

OK. Latest trunk contains a test now that verifies that no SQL is executed on the flush.

Can you please tell us how to modify it to reproduce the problem?

Look into: Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php

Last test method.

Comment by Eric Durand-Tremblay [ 09/Dec/09 ]

Got it! Your fix worked on OneToOne relation but not OneToMany

I did the same test using the CmsArticles entity and here is the result :

SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.id AS id4, c1_.topic AS topic5, c1_.text AS text6, c1_.user_id AS user_id7 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id
FLUSH!
UPDATE cms_users SET name = ? WHERE id = ?
array(2) {
  [0]=>
  string(6) "FooBar"
  [1]=>
  int(17)
}
UPDATE cms_articles SET user_id = ? WHERE id = ?
array(2) {
  [0]=>
  int(17)
  [1]=>
  int(2)
}
DONE!
DELETE FROM cms_users_groups
DELETE FROM cms_groups
DELETE FROM cms_addresses
DELETE FROM cms_phonenumbers
DELETE FROM cms_articles
DELETE FROM cms_users
Comment by Eric Durand-Tremblay [ 09/Dec/09 ]

Dont forget to add the cascade persist for the articles relation

public function testFlushDoesNotIssueUnnecessaryUpdates()
    {

        $user = new CmsUser;
        $user->name = 'Guilherme';
        $user->username = 'gblanco';
        $user->status = 'developer';

        $address = new CmsAddress;
        $address->country = 'Germany';
        $address->city = 'Berlin';
        $address->zip = '12345';

        $article = new \Doctrine\Tests\Models\CMS\CmsArticle();
        $article->text = "Lorem ipsum dolor sunt.";
        $article->topic = "A Test Article!";
        $article->setAuthor($user);
        $user->articles[] = $article;


        $address->user = $user;
        $user->address = $address;

        $this->_em->persist($user);

        $this->_em->flush();
        $this->_em->clear();

        // show SQL from here on...
        $this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);


        //$query = $this->_em->createQuery('select u, a from Doctrine\Tests\Models\CMS\CmsUser u join u.address a');
        $query = $this->_em->createQuery('select u, a from Doctrine\Tests\Models\CMS\CmsUser u join u.articles a');
        echo "QUERY!".PHP_EOL;
        $user2 = $query->getSingleResult();

        $user2->name = "FooBar";

        echo "FLUSH!".PHP_EOL;
        $this->_em->flush();

        echo "DONE!".PHP_EOL;

    }
Comment by Roman S. Borschel [ 09/Dec/09 ]

OK. I will look into this later or tomorrow.

Thanks for your help.

Comment by Roman S. Borschel [ 10/Dec/09 ]

Should be fixed now (second attempt)

Can you check whether DDC-140 can be closed also?

Comment by Eric Durand-Tremblay [ 10/Dec/09 ]

Yes, this is now fixed for me.
I confirm you this also close DDC-140

Thank you for your great work !!





[DDC-168] serialization/unserialization of ClassMetadata lose reflFields order causing insertSql statement to fail Created: 20/Nov/09  Updated: 17/Dec/09  Resolved: 17/Dec/09

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

Type: Bug Priority: Critical
Reporter: Eric Durand-Tremblay Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

This problem will be difficult to reproduce for you, but I isolated the exact cause of it.

To reproduce

  • Use any MetadataCache
  • Create a complex entity with some inheritance. (I may detail in later if your really need it)
  • Create an element of this entity with persist.
  • Create an other element of this entity but not on the same php run. (ex: reload the page)
  • The insert statement will fail because field names will not match parameters.

I narrowed it down to a serialization issue.

When you serialize an MetadataInfo, you don't serialize reflFields.
When you unserizlize you regenerate this value using ReflectionClass
That sound's like a good optimization.

The problem is that somehow, the reflection class can return the fields not in the same order that they were at the initial creation of the MetadataInfo.

Because the insertSql value is not re-generated, it can be out of sync with the new fields order.

The solution :
Call something like MetadataInfoFactory::_generateStaticSql (whish is private but ...) at wakeup time.



 Comments   
Comment by Roman S. Borschel [ 21/Nov/09 ]

Are you using composite keys anywhere?

The fact that reflFields is not serialized is not really an optimization but rather due to the fact that they simply can can not be serialized/unserialized properly.

We surely need some more concrete test case in order to reproduce this.

Comment by Eric Durand-Tremblay [ 23/Nov/09 ]

I do not uses composite key anyware.

The field who change place is a foreign key.

EntityRevision extends Entity
EntityRevision.parent_entity : manyToOne entity

(the field parent_entity_id changes place during serialization)

What do you need to reproduce the case ? Just entitiy classes? a completely fuctionnal project? some kind of unit test ?

Comment by Roman S. Borschel [ 05/Dec/09 ]

Hm. the order shouldnt even matter because insertSql only contains placeholders. Can you show the SQL INSERT that is failing? With parameters, if possible, and error message.

Comment by Eric Durand-Tremblay [ 06/Dec/09 ]

For what I can remember, parameters are not bound in the right order which is causing an error like "field x could not be null"

INSERT INTO table (a, b) VALUES (?, ?)
bind : $b, $a

I will post the real query on monday morning when I get back to work.

Comment by Eric Durand-Tremblay [ 07/Dec/09 ]

Here is the real world example. I put the statement, the parameters, the error, and all the related entities.

INSERT INTO fna_client_revision (fna_parent_record_id, record_id, display_name, last_name, maiden_name, first_name, civility, gender, birthdate, employer, employment, employment_from, civil_status, civil_status_from, insurability, fna_client_fna_id, fna_spouse_fna_id, rev_no, created_at, modified_at, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Array
(
    [1] => 1
    [2] => George Simard2
    [3] => Simard2
    [4] =>
    [5] => George
    [6] =>
    [7] => MALE
    [8] => 1962-09-14
    [9] => Plomberie ABC Inc.
    [10] => Plombier
    [11] =>
    [12] => MARRIED
    [13] =>
    [14] => INSURABLE
    [15] => 5
    [16] => 2009-12-07 08:54:46
    [17] => 2009-12-07 08:54:46
    [18] =>  Eric Durand-Tremblay
    [19] =>  Eric Durand-Tremblay
    [20] => 1
    [21] =>
    [22] =>
)

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'employment' cannot be null
namespace ORM\BaseEntity;

/**
 * @MappedSuperclass
 */
class Entity
{
	private static $boolean_values = array("YES", 'NO');
	
	 /**
     * @Column(name="id", type="integer")
     * @Id
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;
    
    /**
     * @Column(name="created_at", type="datetime")
     */
    protected $created_at;
    
    /**
     * @Column(name="modified_at", type="datetime")
     */
    protected $modified_at;
    
    
     /**
     * @Column(name="created_by", type="string")
     */
    protected $created_by="";
    
    /**
     * @Column(name="modified_by", type="string")
     */
    protected $modified_by="";
}

/**
 * @MappedSuperclass
 * @HasLifecycleCallbacks
 */
class Revisionable extends ORM\BaseEntity\Entity
{

     /**
     * @Column(name="rev_no", type="integer")
     */
    protected $revision=1;
}


namespace ORM\BaseEntity;

/**
 * @MappedSuperclass
 * @HasLifecycleCallbacks
 */
class Client extends Revisionable
{

    /**
     * @Column(name="display_name", type="string")
     */
    protected $display_name;

    /**
     * @Column(name="last_name", type="string")
     */
    protected $last_name="";

    /**
     * @Column(name="maiden_name", type="string")
     */
    protected $maiden_name="";

    /**
     * @Column(name="first_name", type="string")
     */
    protected $first_name="";

    /**
     * @Column(name="civility", type="string")
     */
    protected $civility="";
    
    /**
     * @Column(name="gender", type="string", length="25")
     */
    protected $gender="";

    /**
     * @Column(name="birthdate", type="date", nullable="true")
     */
    protected $birthdate;

    /**
     * @Column(name="employer", type="string")
     */
    protected $employer="";

    /**
     * @Column(name="employment", type="string")
     */
    protected $employment="";

    /**
     * @Column(name="employment_from", type="date", nullable="true")
     */
    protected $employment_from;

    /**
     * @Column(name="civil_status", type="string", length="25")
     */
    protected $civil_status="";
    
    /**
     * @Column(name="civil_status_from", type="date", nullable="true")
     */
    protected $civil_status_from;
    
    /**
     * @Column(name="insurability", type="string", length="25")
     */
    protected $insurability="";

    
    /**
     * @OneToOne(targetEntity="ORM\Entity\FNA")
     * @JoinColumns({
     *   @JoinColumn(name="fna_client_fna_id", referencedColumnName="id")
     * })
     */
    protected $client_fna;

	/**
     * @OneToOne(targetEntity="ORM\Entity\FNA")
     * @JoinColumns({
     *   @JoinColumn(name="fna_spouse_fna_id", referencedColumnName="id")
     * })
     */
    protected $spouse_fna;
}


namespace ORM\RevisionEntity;

/**
 * @Entity
 * @Table(name="fna_client_revision", indexes={@index(name="idx_record_id", columns={"record_id"})})
 * @HasLifecycleCallbacks
 */
class Client extends ORM\BaseEntity\Client{
	
	 /**
     * @ManyToOne(targetEntity="Kronos\FNA\ORM\Entity\Client")
     * @JoinColumns({
     *   @JoinColumn(name="fna_parent_record_id", referencedColumnName="id", onDelete="SET NULL")
     * })
     */
    protected $parent_record;  /// HERE IS THE  FIELD WHO CHANGE PLACE AFTER SERIALIZATION !!!!!!!!!!!!!!!!!!!!!!
    
    /**
     * 
     * @Column(name="record_id", type="int")
     */
    protected $record_id;
}

Comment by Eric Durand-Tremblay [ 07/Dec/09 ]
   
    [1] => 1  (fna_parent_record_id)
(expected record_id)
    [2] => George Simard2 (display_name)
    [3] => Simard2 (last_name)
    [4] => (maiden_name)
    [5] => George (first_name)
    [6] => (civility)
    [7] => MALE (gender)
    [8] => 1962-09-14 (birthdate)
    [9] => Plomberie ABC Inc. (employer)
    [10] => Plombier (employment)
    [11] => (employment_from)
    [12] => MARRIED (civil_status)
    [13] => (civil_status_from)
    [14] => INSURABLE (insurability)
    [15] => 5 (rev_no)
    [16] => 2009-12-07 08:54:46 (created_at)
    [17] => 2009-12-07 08:54:46 (modified_at)
    [18] =>  Eric Durand-Tremblay (created_by)
    [19] =>  Eric Durand-Tremblay (modified_by)
    [20] => 1 (record_id) !! Should be in position  2
    [21] => (fna_client_fna_id)
    [22] => (fna_spouse_fna_id)

Comment by Benjamin Eberlei [ 07/Dec/09 ]

I could reproduce the issue and generated a failing test for this. We already discussed how this might be fixed.

Comment by Eric Durand-Tremblay [ 07/Dec/09 ]

This is a very good news !

Comment by Roman S. Borschel [ 17/Dec/09 ]

Should be fixed now.

Comment by Eric Durand-Tremblay [ 17/Dec/09 ]

Resolution confirmed.

Thank your for your great work !





[DDC-164] find() not polymoprhic with single table inheritance Created: 20/Nov/09  Updated: 21/Nov/09  Resolved: 21/Nov/09

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

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None





[DDC-152] Polymorphic queries and fields of subclasses Created: 16/Nov/09  Updated: 19/Dec/09  Resolved: 19/Dec/09

Status: Closed
Project: Doctrine 2 - ORM
Component/s: DQL, ORM
Affects Version/s: 2.0-ALPHA3
Fix Version/s: 2.0-ALPHA4
Security Level: All

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Currently fields of subclasses are added to the ResultSetMapping under the alias of the queried (parent) class. Later during hydration if the queried (parent) class does not own a field AbstractHydrator#_lookupDeclaringClass looks up the first subclass that defines a field with that name. However, multiple subclasses can define a field with the same name, so this is error-prone.

Fields of subclasses in a polymorphic query should probably be added to the ResultSetMapping with their own alias. The lookup during hydration should be removed and the information which field belongs to which class encoded in the ResultSetMapping.






[DDC-145] Cascaded delete on a lazyloaded persistent collection Created: 13/Nov/09  Updated: 13/Nov/09  Resolved: 13/Nov/09

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

Type: Bug Priority: Critical
Reporter: Avi Block Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

It seems that if you call PersistentCollection#unwrap on a lazy loaded collection, you get nothing back (unless you initialize the collection first). Because of this, UnitOfWork#_cascadeRemove is "unwrapping" the collection before transversing it, and nothing gets deleted on the cascade.



 Comments   
Comment by Roman S. Borschel [ 13/Nov/09 ]

Right, I think I made that change for all _cascade* methods but actually I think for _cascadeRemove initialization is actually intended. For the others it doesnt make sense.

Comment by Roman S. Borschel [ 13/Nov/09 ]

Will need to write a functional test for this before I fix it by reverting the change for _cascadeRemove.





[DDC-141] DQL with WHERE queries on Single Table Inheritance Entities fail Created: 13/Nov/09  Updated: 13/Nov/09  Resolved: 13/Nov/09

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

The ORM query builder seems to be broken for Single Table Inheritance with discriminator.

When using inheritance like here:
http://www.doctrine-project.org/documentation/manual/2_0/en/inheritance-mapping#single-table-inheritance

Queries with WHERE fail:

SELECT p FROM MyProject\Model\Person p WHERE p.id = 1

This translates to this SQL:

SELECT p0_.id AS id0, p0_.discr AS discr1 FROM person p0_ WHERE p0_.id = 1 p0_.discr IN ('', 'person', 'employee')

(AND is missing after p0_.id = 1)



 Comments   
Comment by Roman S. Borschel [ 13/Nov/09 ]

Thanks for reporting this. I'm just wondering where the empty string in the IN clause comes from? I dont seem to be able to reproduce that bit.

Comment by Nico Kaiser [ 13/Nov/09 ]

Argh, ignore the empty string, it is

SELECT p0_.id AS id0, p0_.discr AS discr1 FROM person p0_ WHERE p0_.id = 1 p0_.discr IN ('person', 'employee')

My initial example was a superclass that did not appear in the DiscriminatorMap...

Comment by Roman S. Borschel [ 13/Nov/09 ]

OK. Should be fixed now. Was a very trivial mistake. Now its covered in the tests.





[DDC-137] Only last relation id updated with multiple one-to-one self-referencing relations Created: 12/Nov/09  Updated: 15/Nov/09  Resolved: 15/Nov/09

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

Type: Bug Priority: Critical
Reporter: Reinier Kip Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

Take this simplified situation:

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

	/**
	 * @OneToOne(targetEntity="Entity", cascade={"persist"})
	 * @JoinColumn(name="other1", referencedColumnName="id")
	 */
	public $other1;

	/**
	 * @OneToOne(targetEntity="Entity", cascade={"persist"})
	 * @JoinColumn(name="other2", referencedColumnName="id")
	 */
	public $other2;
}

$entity1 = new Entity();
$em->persist($entity1);
$entity1->other1 = $entity2 = new Entity();
$entity1->other2 = $entity3 = new Entity();
$em->flush();

The entities' ids are now:

Entity 1: 1, Entity 2: 2, Entity 3: 3

However, the other1's relation id is not updated:

id	other1	other2
1  	NULL 	3:	// other1's id is missing
2 	NULL 	NULL
3 	NULL 	NULL

The SQL clarifies:

INSERT INTO Entity (other1, other2) VALUES (?, ?)
array(2) {
  [1]=>
  NULL
  [2]=>
  NULL
}
INSERT INTO Entity (other1, other2) VALUES (?, ?)
array(2) {
  [1]=>
  NULL
  [2]=>
  NULL
}
INSERT INTO Entity (other1, other2) VALUES (?, ?)
array(2) {
  [1]=>
  NULL
  [2]=>
  NULL
}
UPDATE Entity SET other2 = ? WHERE id = ?
array(2) {
  [0]=>
  int(3)
  [1]=>
  int(1)
}

Adding 'other3' proved that only the last relation id is updated, as such:

id	other1	other2	other3
1  	NULL 	NULL	4	// other1 and other2 missing
2 	NULL 	NULL	NULL
3 	NULL 	NULL	NULL
4 	NULL 	NULL	NULL

This is not the case with relations that reference to entities outside the class hierarchy (so the same problem occurs with relations inside the class hierarchy).



 Comments   
Comment by Roman S. Borschel [ 15/Nov/09 ]

Thanks, should be fixed now.





[DDC-132] Subclass' columns missing from cached ClassMetadata::$resultColumnNames Created: 09/Nov/09  Updated: 09/Dec/09  Resolved: 09/Dec/09

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers, ORM
Affects Version/s: 2.0-ALPHA3
Fix Version/s: 2.0-ALPHA4
Security Level: All

Type: Bug Priority: Critical
Reporter: Reinier Kip Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File patch.diff    

 Description   

I have a Customer with class table inheritance mapping and some fields, and a subclass called SuperCustomer with two fields: bool `isSuper` and string `extra`.

I found that when replacing the usual ArrayCache with a MemcacheCache, everything works fine on the first request, but notices occur on subsequent requests:

Notice: Undefined index: isSuper in X:\...\Doctrine\ORM\Persisters\StandardEntityPersister.php on line 577
Notice: Undefined index: extra in X:\...\Doctrine\ORM\Persisters\StandardEntityPersister.php on line 577

Inspection of ClassMetadata::$resultColumnNames on line 569 showed that the two fields `isSuper` and `extra` were (the only things) missing. The resulting object's `isSuper` and `extra` properties were empty.

I have yet to find the cause of this, but maybe you have an idea.



 Comments   
Comment by Roman S. Borschel [ 09/Nov/09 ]

This is indeed strange. My first guess was that the resultColumnNames get lost during serialization but the ClassMetadata#__sleep implementation seems to be correct in so far as it returns the resultColumnNames field as part of the fields to serialize.

Let me know as soon as you have more information.

Btw. Which metadata driver are you using? Annotations?

Comment by Reinier Kip [ 10/Nov/09 ]

> My first guess was that the resultColumnNames get lost during serialization
This would be really strange, as the columns of the parent class Customer are still there after unserialization, so only a part is lost during serialization. My first guess was that the metadata is cached before the metadata loading is finished.

> Which metadata driver are you using? Annotations?
I'm using a custom metadata driver. Caching is separated from the driver, so the driver couldn't have anything to do with this, right?

I'll try to find where it goes wrong and let you know.

Comment by Reinier Kip [ 10/Nov/09 ]

First request:

  • Customer metadata is loaded and cached to Customer$CLASSMETADATA. $resultColumnNames contains Customer's fields.
  • SuperCustomer metadata is loaded and cached to SuperCustomer$CLASSMETADATA. $resultColumnNames contains Customer's and SuperCustomer's fields.
  • I request an object of class Customer be loaded from the database, which is an instance of SuperCustomer.
  • StandardEntityPersister::_createEntity() is called to create the entity, and uses Customer metadata ALSO CONTAINING SuperCustomer's $resultColumnNames.

Second request:

  • Customer metadata is loaded from Customer$CLASSMETADATA. $resultColumnNames contains Customer's fields.
  • SuperCustomer metadata is loaded from SuperCustomer$CLASSMETADATA. $resultColumnNames contains Customer's and SuperCustomer's fields.
  • I request an object of class Customer be loaded from the database, which is an instance of SuperCustomer.
  • StandardEntityPersister::_createEntity() is called to create the entity, and uses Customer metadata NOT CONTAINING SuperCustomer's $resultColumnNames.

Thought: the Customer's metadata is somehow changed after caching to cope with Customer's subclasses as well, but these changes are not in the cache. I don't have enough knowledge of Doctrine's internals, but you probably have a pretty good idea where this could happen.

Comment by Roman S. Borschel [ 10/Nov/09 ]

Thanks for your detailed information. I think I know what the issue is now. May be non-trivial to fix. I will try to look into it as soon as I got some free time.

Comment by Jonathan H. Wage [ 11/Nov/09 ]

Here is a patch which fixes the issue but through doing this roman and I realized another issue.

Comment by Roman S. Borschel [ 11/Nov/09 ]

Scheduled for ALPHA4 as this may need some restructuring.

Comment by Roman S. Borschel [ 09/Dec/09 ]

This should be fixed now in trunk.





[DDC-119] Collection change tracking broken with NOTIFY policy Created: 06/Nov/09  Updated: 16/Jul/10  Resolved: 15/Jul/10

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

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 1
Labels: None


 Description   

Looks like change tracking of collections together with the NOTIFY policy doesnt work well as collection updates are detected in _computeAssociationChanges. Perhaps the collection itself should inform the UnitOfWork directly?



 Comments   
Comment by Kawsar Saiyeed [ 16/Mar/10 ]

Not sure if the issue is identical but seems at least related. Using NOTIFY change tracking with many-to-many bidirectional associations does not work. Objects added to the associations do not get persisted when calling EntityManager#flush.

Tested on r7404.

Comment by Michael Zach [ 16/Jul/10 ]

Dear Roman,

the line # 456 in UnitOfWork.php seems wrong to me:

            $isChangeTrackingNotify = isset($this->entityChangeSets[$oid]);

Shouldn't this only be set if the entity has

 @ChangeTrackingPolicy("NOTIFY") *

set in his class docBlock? The current behaviour now is to assign $changeset if changes exists, leaving the NOTIFY tracking policy out:

             $changeSet = $isChangeTrackingNotify ? $this->entityChangeSets[$oid] : array();

Because of this change, all our unit tests involving saving of entites break (basically, the whole application), which implement @postUpdate for logging purposes logging an own computed changeset.

Comment by Roman S. Borschel [ 16/Jul/10 ]

Hi,

you're right, I did that naivly because I thought the only case where an entity would already have a changeset on flush is with the NOTIFY policy. I did not think of custom use cases like yours. It is fixed now in master. My apologies. However, this still means your approach would be broken if you would use the NOTIFY policy right? That sounds like maybe there is potential to improve the approach you're currently using. If you're missing anything in the API or implementation that forbids a different approach feel free to raise some new JIRA issues so we can possibly improve the situation.

Comment by Michael Zach [ 16/Jul/10 ]

Thank you for fixing this real quick! Our current approach is to compute the changeset on @preUpdate and after a successful save write the changeset to the database in @postUpdate. This conflicted with the changes made, I will however look into it and see if it's feasible for us to implement another approach.

Once again, thanks for your reply and fix.





[DDC-116] array_combine error when using combined Primary Key Created: 05/Nov/09  Updated: 14/Jun/10  Resolved: 06/Nov/09

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: Text File Assigned.php    

 Description   

I use a combined Primary Key for a User's Phonenumber entity (to match our existing DB model). The key is combined from a "Type" field and a "userId" field that references the User table.

To mark the (referenced!) userId field and the type field as PK, I need to add "userId" to the model (adding @Id to User does not work). This works well when generating Users and Phonenumbers - but the delete command breaks:

PHP Warning: array_combine(): Both parameters should have an equal number of elements in ..../lib/Doctrine/ORM/Persisters/StandardEntityPersister.php on line 274
PHP Catchable fatal error: Argument 2 passed to Doctrine\DBAL\Connection::delete() must be an array, boolean given, called in ..../lib/Doctrine/ORM/Persisters/StandardEntityPersister.php on line 275 and defined in ..../lib/Doctrine/DBAL/Connection.php on line 372

Entities:
http://pastebin.com/d51e021e2

Test code:
http://pastebin.com/m51d1f497



 Comments   
Comment by Nico Kaiser [ 06/Nov/09 ]

I attached a patch for Doctrine\Orm\Id\Assigned, there seems to be a runaway line which makes the class add each element twice...

Comment by Roman S. Borschel [ 06/Nov/09 ]

Whoops. Thanks for finding this! Does it fix this issue? I will need to add a testcase to the suite.

Comment by Nico Kaiser [ 06/Nov/09 ]

Does not entirely fix it: if the $value is empty, too few elements are added again.
Need to remove the "if (isset($value)) {" clause. I'll attach the fixed Assigned.php.

Comment by Roman S. Borschel [ 06/Nov/09 ]

I think the isset() is correct. That way the $identifier array remains empty if the PK is null and later if ( ! $identifier) evaluates to false on the empty array and raises the exception. If we remove the isset() we would "accept" PKs with null values. But it might be better to do:

 if (!isset($value)) {
        throw ORMException::entityMissingAssignedId($entity);
  } else {
         $identifier[] = $value;
 }
Comment by Roman S. Borschel [ 06/Nov/09 ]

I will fix this and add some new tests for it.

Comment by Roman S. Borschel [ 06/Nov/09 ]

Should be fixed now.





[DDC-113] Cascaded persist avoids LifecycleCallbacks Created: 04/Nov/09  Updated: 18/Dec/09  Resolved: 18/Dec/09

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

When an Entity is created by 'cascade=

{"persist"}

', its LifecycleCallbacks (e.g. "PrePersist"!) are not invoked.

When it is persisted explicitly, everything workes fine and the events are called...



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

Works for me on trunk, added test-case that proves it (Doctrine\Tests\ORM\Functional\LifecycleCallbackTest::testCascadedEntitiesCallsPrePersist())

Comment by Nico Kaiser [ 14/Dec/09 ]

There is still an issue. You can reproduce it if you change the test case slightly to this:

    /**
     * @group DDC-113
     */
    public function testCascadedEntitiesCallsPrePersist()
    {
        $e1 = new LifecycleCallbackTestEntity;
        $e2 = new LifecycleCallbackTestEntity;

        $c = new LifecycleCallbackCascader();
        
        $this->_em->persist($c);
        
        $c->entities[] = $e1;
        $c->entities[] = $e2;

        //$this->_em->persist($c);
        $this->_em->flush();

        $this->assertTrue($e1->prePersistCallbackInvoked);
        $this->assertTrue($e2->prePersistCallbackInvoked);
    }

The difference to the existing (and indeed working) test case is that the Cascader entity is persisted before the collection entries are added.

Comment by Benjamin Eberlei [ 14/Dec/09 ]

That is valid behaviour according to the lifecycle of an entity, persist gets cascaded right and only when ->persist() is called.

In the case you show, the two entities are also not persisted, because the cascade was already executed.

Comment by Nico Kaiser [ 14/Dec/09 ]

From a technical point of view this is ok - but from an intuitive point of view I would think I can use persist whereever I want. So I can create an entity, persist it (so it gets written to the DB), make changes to it (like adding entities to its collection) and getting its saved (and the collection entities persisted) when I call flush().

So I suspect lifecycle events to be called whenever an entity is persisted. And in this case LifecycleCallbackTestEntity's are persisted automatically by flush() (and this is after the Cascader is persisted!), so lifecycle events should fire here.

Comment by Roman S. Borschel [ 17/Dec/09 ]

I have a fix for this ready, however, I want to look into something else before committing. I'll address this over the weekend.

Comment by Roman S. Borschel [ 18/Dec/09 ]

Should be fixed now.





[DDC-61] OneToOne relation with an entity using Class Table Inheritance fails Created: 27/Oct/09  Updated: 04/Nov/09  Resolved: 03/Nov/09

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

Type: Bug Priority: Critical
Reporter: Ville Väänänen Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Attachments: File cti_test.diff     File cti_test.diff     File StandardEntityPersister.php.diff    

 Description   

The bug occurs when there's a lazy bidirectional one-to-one relation where the inverse side only specifies the root entity. In this case, the final type of the target-entity is not resolved correctly. See the attached file for a failing test case.



 Comments   
Comment by Ville Väänänen [ 27/Oct/09 ]

The situation is actually worse if the relation is eager. In that case not only is the classname incorrectly resolved, but if the root-entity is an abstract class, the execution ends up in a fatal error.

It would seem to me that there are two ways to deal with the described problem:

1. join all the tables that contain the discriminator-fields, so that the correct subclasses can be inferred
OR
2. change the proxy pattern to a variant which is used in the FLOW3 framework, where there's only one proxy-class

Comment by Ville Väänänen [ 27/Oct/09 ]

A fix to the patch. Use the newer attachment.

Comment by Ville Väänänen [ 02/Nov/09 ]

I have patches for both of the options above. Should this be a configuration option? As it stands now, OneToMany and ManyToMany relationships enjoy the advantage of having the Collection in between the target and source entities. We could even use a hybrid approach, where a general proxy-object is created only when the object type cannot be inferred without joins.

Comment by Roman S. Borschel [ 02/Nov/09 ]

It should of course be Nr. 1. The discriminator column is always stored in the topmost (root) table and tables of base classes must be joined, always. If this is not the case this is a bug. Can you show your patch?

Comment by Ville Väänänen [ 03/Nov/09 ]

It would seem to me that this would cause an inconsistency in how similar associations with different kinds of entities are treated.

From DDC-38: But inverse sides of one-one associations can never be lazy. Now going with option 1 would mean that in case the association is with an entity using class-table inheritance, these lazy associations would be allowed (because if we're joining the base-table anyway, there's no reason to forbid this) . This begs the question that if we're ready to do the joins in this case, should we reconsider doing them also to find out about target-existency when dealing with other types of entities?

Comment by Ville Väänänen [ 03/Nov/09 ]

Describing the changes StandardEntityPersister.php.diff:

  • _getSelectEntitiesSql
    • For every inverse OneToOne relation join the target-table/base-table and find out the existence of the target-entity and the value of the discriminator column if one should exist. These values are given the names of the sourceFieldNames in the result array
  • _createEntity
    • Pass the existence and discrimnator column values to unitOfWork->createEntity in the hints['associations'] array. This part is only speculative.

This selects the existence/discrimator-column information when an entity is queried using EntityManager->find, it's doesn't help if one is using a DQL query.

Comment by Roman S. Borschel [ 03/Nov/09 ]

I understand now what the problem is. Its tricky. Your testcase however has a wrong mapping. The way you mapped it, any event that is fetched of a company will automatically be the main event In this case it would be correct to put a join column on the organization (main_event_id). But I understand the problem and will create a separate new test for it.

It will probably be treated similar to inverse sides of one-one. That means, when a one-one associations references a target entity that is a base type (not an outermost type, that means there are still possible subtypes), whether the association is the owning side or not it must be eagerly fetched.

Comment by Ville Väänänen [ 04/Nov/09 ]

Okay, so not allowing lazy-loading in this case at all. Why is this? As I stated earlier there are at least to ways to solve this without resorting to eager-loading. If using class-table inheritance means forgetting about lazy-load, to me at least it's a huge disadvantage and would be a deal breaker in using this inheritance-type. The problem with eager-load is not the first fetch that follows, but the uncertainty of how many fetches are going to follow in total. If the target-entity is the tip of a huge object-hierarchy full of entities using class-table inheritance, the current strategy is unusable. Having a couple of extra tables joined is nothing compared to the snowball that eager-fetching might get rolling.

Comment by Roman S. Borschel [ 04/Nov/09 ]

Well, option Nr.1 is too complicated and can lead to lots of new issues. You can not always join arbitrary tables, not when it comes to DQL where it alters the overall result. The situation is much more complex than it might look to you right now.

Nr.2 is no option at all because such a proxy approach does not even hold for instanceof checks. An absolute no-go.

What it does now is the best thing from my point of view. If performance becomes an issue you can always improve upon that by eager fetching with DQL. Also, you did not yet understand how it works. The eager load does not happen always with class table inheritance, only when the target entity is a base type. If it is a subtype that does not have any further subtypes, then a proxy can be used without fear. Same goes for single table inheritance.

Comment by Roman S. Borschel [ 04/Nov/09 ]

If you still feel this is too bad open a new issue as "improvement" and move any old/new patches you have there. There would need to be much more tests for such a change. Also you did not mention whether your patch actually passes all existing unit tests.

Comment by Ville Väänänen [ 04/Nov/09 ]

Sure I understood how it works. But to me one of the biggest advantages of class-table inheritance is exactly that the parent doesn't have to know the final type., and so in the configuration the association is pointed to the base-type. Yes, I accept that the issue might be way too complicated in the DQL case.

Option number two can be improved if the parent model that might have these general proxies invokes a load in it's getters. Exactly the way the generated proxies work now. Of course, this means that the models need to be aware of the possible proxies. It's not perfect, but maybe it could be an option?

Comment by Roman S. Borschel [ 04/Nov/09 ]

Please open a new issue, I think this has potential as an improvement that can complement the current behavior.

What I mean is: for normal find/load ... operations that are not triggered by DQL we can probably do the join and in the case of DQL the current behavior is used, if you want to avoid the extra query in that case its as simple as adding the join to DQL.

So I think the approaches can be complementary.

Comment by Ville Väänänen [ 04/Nov/09 ]

Sure like the sound of that! I will be very happy to open this as an improvement. Thanks for your patience .





[DDC-74] Updates get lost when Lifecycle Events (@PreUpdate) are invoked Created: 29/Oct/09  Updated: 13/Nov/09  Resolved: 13/Nov/09

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

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 1
Labels: None


 Description   

When Lifecycle Events are invoked on entity update (@PreUpdate), the entity is not updated properly in the database.

This code creates a User object, sets its name to "Bob" and its value to empty, then updates the object and updates the name to "Alice".

$user = new User;
$user->setName('Bob');
$user->setValue('');
$em->persist($user);
$em->flush();
$user->setName('Alice');
$em->flush();

However, when the User class has a @PreUpdate event that e.g. sets the value to "Hello World", the name change gets lost and only the value is updated by the second flush() call.

This is a critical bug which prevents creation of entities that simulate the "Timestampable" behaviour of Doctrine 1.x...



 Comments   
Comment by Benjamin Eberlei [ 02/Nov/09 ]

I think this might be a hen-egg problem.

@PreUpdate is only invoked after it is calculated that a change occured on all those entities that have updates. After a change in @PreUpdate events there would have to be another calculation of changes on all those entities, which would probably mean a significant performance hit.

Comment by Eric Durand-Tremblay [ 13/Nov/09 ]

I may not know all the consequences of this, but I think I have a fix for this bug.

In the class ORM\UnitOfWork

As I understand it, computeSingleEntityChangeSet() is called to update the changeset and _originalEntityData is set to the current values.

But, I see that the old changes are lost.

If i modify the function computeSingleEntityChangeSet to merge the changeset, it works.

At line 656 replace

$this->_entityChangeSets[$oid] = $changeSet;

By :

if($this->_entityChangeSets[$oid]){
    $this->_entityChangeSets[$oid] += $changeSet;
}
else {
    $this->_entityChangeSets[$oid] = $changeSet;
}

Here is my test case :

$qb = new \Doctrine\ORM\QueryBuilder($em);
$qb->select('fna')
			->from('Entity\FNA', 'fna')
			->andwhere($qb->expr()->eq('fna.id', ':fna_id'));
$qb->setParameter('fna_id', 1);
$query = $qb->getQuery();

$fna = $query->getSingleResult();
		
$fna->setStatus('COMPLETED');
$em->persist($fna);
$em->flush();

AND The preUdate :

/** 
 * @PreUpdate
*/
public function onPreUpdate($args=false)
 {
        $this->modified_at = new \DateTime();
}
Comment by Roman S. Borschel [ 13/Nov/09 ]

Indeed this looks like a good fix except that the addition has to be the other way around so that when the same field is changed twice, first before the flush and then in a lifecycle callback/event the change from the callback prevails.

I will work on this and write a test for it.

Thanks Eric.

Comment by Roman S. Borschel [ 13/Nov/09 ]

Fixed now.

Thanks Nico for reporting and thanks Eric for the suggestion!





[DDC-41] Getting error with lazy loading via createQuery() followed by $em->flush() Created: 10/Oct/09  Updated: 12/Oct/09  Resolved: 12/Oct/09

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

Type: Bug Priority: Critical
Reporter: Nichlas Löfdahl Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None
Environment:

Doctrine2-trunk, Postgresql, Lazy loading



 Description   

Simple O-O relationship between \Entities\User and \Entities\Feed. Seems like there's a problem with not-yet lazy-loaded proxies and $em->flush().

"Entities/User.php"
<?php
namespace Entities;

/** @Entity @Table(name="users_debug") */
class User {
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @OneToOne(targetEntity="Feed", mappedBy="User", cascade={"persist"})
     */
    private $Feed;

    public function getID() {
        return $this->id;
    }
    public function getFeed() {
        return $this->Feed;
    }
    public function setFeed($feed) {
        $this->Feed = $feed;
    }
}
?>
"Entities/Feed.php"
<?php
namespace Entities;

/**
 * @Entity @Table(name="feeds_debug")
 */
class Feed {
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO", allocationSize=1)
     */
    private $id;

     /**
     * @OneToOne(targetEntity="User", cascade={"persist"})
     * @JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $User;

    function setID($value) {
        $this->id = $value;
    }
    function getID() {
        return $this->id;
    }
    function getUser() {
        return $this->User;
    }
    function setUser($user) {
        $this->User = $user;
    }
}


?>

Table-data

users_debug:
 id
 361

feeds_debug:
 id  | user_id
 461 |     361

Code:

$user = $em->createQuery("SELECT u FROM Entities\User u WHERE u.id = 361")->getSingleResult();
print $user->getID(); // 361
// uncomment line below and it works
// print $user->getFeed()->getID();
$em->flush();

Error:
Warning: spl_object_hash() expects parameter 1 to be object, null given in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/UnitOfWork.php on line 1010
Warning: spl_object_hash() expects parameter 1 to be object, null given in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/UnitOfWork.php on line 566
Warning: ReflectionProperty::setValue() expects parameter 1 to be object, null given in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/UnitOfWork.php on line 575
Warning: get_class() expects parameter 1 to be object, null given in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/UnitOfWork.php on line 979
Fatal error: Uncaught exception 'ReflectionException' with message 'Class does not exist' in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadata.php:69
Stack trace:
#0 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadata.php(69): ReflectionClass->__construct(false)
#1 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(247): Doctrine\ORM\Mapping\ClassMetadata->__construct(false)
#2 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(177): Doctrine\ORM\Mapping\ClassMetadataFactory->_newClassMetadataInstance(false)
#3 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(115): Doctrine\ORM\Mapping\ClassMetadataFactory->_loadMetadata(false)
#4 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/EntityManager.php(212): Doctrine\ORM\Mapping\ClassMetadataFactory->getMetadataFor(false)
#5 /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/UnitOfWork.php(979): Doctrine\ORM\EntityManager->getClassMetadata in /home/crotalus/src/Doctrine2-Dev/lib/Doctrine/ORM/Mapping/ClassMetadata.php on line 69

PostgreSQL log:
2009-10-10 16:32:58 CEST LOGG: execute pdo_stmt_000000000a283af8: SELECT u0_.id AS id0 FROM users_debug u0_ WHERE u0_.id = 361
2009-10-10 16:32:58 CEST LOGG: sats: DEALLOCATE pdo_stmt_000000000a283af8
2009-10-10 16:32:58 CEST LOGG: execute pdo_stmt_000000000a283af8: SELECT NEXTVAL('feeds_debug_id_seq')
2009-10-10 16:32:58 CEST LOGG: sats: DEALLOCATE pdo_stmt_000000000a283af8
2009-10-10 16:32:58 CEST LOGG: execute pdo_stmt_000000000a283af8: SELECT NEXTVAL('users_debug_id_seq')
2009-10-10 16:32:58 CEST LOGG: sats: DEALLOCATE pdo_stmt_000000000a283af8



 Comments   
Comment by Roman S. Borschel [ 12/Oct/09 ]

Strange, all the warnings and errors and the stack trace rather indicate that the associated value is NULL and not a (not initialized) proxy object. I'm working on this and already found an issue to address but I'm still unable to exactly reproduce this. Will keep you updated. If you have any further information, please let me know.

Comment by Roman S. Borschel [ 12/Oct/09 ]

OK, managed to reproduce this. Working on it.

Comment by Roman S. Borschel [ 12/Oct/09 ]

This should now be fixed but you need to manually delete your proxy classes so that they're regenerated. More improvements to the proxy classes and CLI tasks for (re)generating proxy classes will follow.





[DDC-34] schema-tool --create tries to create duplicate associations and exits with exception Created: 07/Oct/09  Updated: 07/Oct/09  Resolved: 07/Oct/09

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Tools
Affects Version/s: 2.0-ALPHA2
Fix Version/s: 2.0-ALPHA3
Security Level: All

Type: Bug Priority: Critical
Reporter: Ismo Toijala Assignee: Jonathan H. Wage
Resolution: Fixed Votes: 0
Labels: None
Environment:

HEAD



 Description   

I'm getting the error

SchemaTool:Duplicate field mapping ()

when trying to create my database schema. I am pretty sure this worked before. The error occurs on a field defined in a mapped superclass on the first entity that extends this class on the first association.

My entity classes:

namespace Model;

use \Model\User\User;
use \DateTime;

/**
 * @MappedSuperclass
 */
abstract class Blameable
{
	/**
	 *
	 * @var DateTime
	 *
	 * @Column(type="datetime", name="date_created")
	 */
	protected $dateCreated;

	/**
	 *
	 * @var User
	 *
	 * @OneToOne(targetEntity="Model\User\User")
     * @JoinColumn(name="creator_id", referencedColumnName="id", nullable="true")
	 */
	protected $creator;

	/**
	 *
	 * @var DateTime
	 *
	 * @Column(type="datetime", name="date_updated", nullable="true")
	 */
	protected $dateUpdated;

	/**
	 *
	 * @var User
	 *
	 * @OneToOne(targetEntity="Model\User\User")
     * @JoinColumn(name="updater_id", referencedColumnName="id", nullable="true")
	 */
	protected $updater;

	/**
	 *
	 * @var DateTime
	 *
	 * @Column(type="datetime", name="date_deleted", nullable="true")
	 */
	protected $dateDeleted;

	/**
	 *
	 * @var User
	 *
	 * @OneToOne(targetEntity="Model\User\User")
     * @JoinColumn(name="deleter_id", referencedColumnName="id", nullable="true")
	 */
	protected $deleter;
}
namespace Model\User;

use \Zend_Validate_Alnum;
use \Zend_Validate_StringLength;
use \Zend_Validate_Alpha;
use \Zend_Validate_EmailAddress;
use \Zend_Registry;
use \Model\Blameable;
use \Model\ConstraintException;
use \Itoijala_Singletons;
use \Zend_Auth;
use \Itoijala_Password_Hash;

/**
 *
 *
 * @Entity @Table(name="user_users")
 */
class User extends Blameable
{
	/**
	 *
	 * @var int
	 *
	 * @Id @Column(type="integer", name="id")
     * @GeneratedValue(strategy="AUTO")
	 */
	private $id;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="20", name="username")
	 */
	private $username;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="20", name="first_name")
	 */
	private $firstName;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="20", name="last_name")
	 */
	private $lastName;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="255", name="email")
	 */
	private $email;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="20", name="signature")
	 */
	private $signature;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="128", name="password")
	 */
	private $password;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="255", name="role")
	 */
	private $role;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="32", name="session_id", nullable="true")
	 */
	private $sessionId;

	/**
	 *
	 * @var bool
	 *
	 * @Column(type="boolean", name="unlocked")
	 */
	private $unlocked;
}
namespace Model\Article;

use \Closure;
use \Model\Blameable;
use \Doctrine\Common\Collections\Collection;
use \Doctrine\ommon\Collections\ArrayCollection;
use \Model\User\User;
use \Model\Gallery\Gallery;
use \Model\ConstraintException;
use \Zend_Validate_StringLength;

/**
 * @Entity @Table(name="article_articles")
 */
class Article extends Blameable
{
	/**
	 *
	 * @var int
	 *
	 * @Id @Column(type="integer", name="id")
     * @GeneratedValue(strategy="AUTO")
	 */
	private $id;

	/**
	 *
	 * @var Category
	 *
	 * @ManyToOne(targetEntity="Model\Article\Category")
     * @JoinColumn(name="category_id", referencedColumnName="id")
	 */
	private $category;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="255", name="name")
	 */
	private $name;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="text", name="description")
	 */
	private $description;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="string", length="255", name="thumbnail")
	 */
	private $thumbnail;

	/**
	 *
	 * @var string
	 *
	 * @Column(type="text", name="content")
	 */
	private $content;

	/**
	 *
	 * @var int
	 *
	 * @Column(type="integer", name="views")
	 */
	private $views;

	/**
	 *
	 * @var bool
	 *
	 * @Column(type="boolean", name="news")
	 */
	private $news;

	/**
	 *
	 * @var bool
	 *
	 * @Column(type="boolean", name="unlocked")
	 */
	private $unlocked;

	/**
	 *
	 * @var Collection
	 *
	 * @ManyToMany(targetEntity="Model\Gallery\Gallery")
     * @JoinTable(name="article_article_galleries",
     *      joinColumns={@JoinColumn(name="article_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="gallery_id", referencedColumnName="id")})
	 */
	private $galleries;

	/**
	 *
	 * @var Collection
	 *
	 * @ManyToMany(targetEntity="Model\User\User")
     * @JoinTable(name="article_article_authors",
     *      joinColumns={@JoinColumn(name="article_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")})
	 */
	private $authors;

	/**
	 *
	 * @var Collection
	 *
	 * @OneToMany(targetEntity="Model\Article\Attachment", mappedBy="article")
	 */
	private $attachments;
}





[DDC-33] Remove allowPartialObjects option. Created: 07/Oct/09  Updated: 15/Oct/09  Resolved: 15/Oct/09

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

Type: Task Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

I think this option will cause unnecessary confusion. The best solution is to always disallow partial objects, yet you can force partial objects on individual object queries to increase performance if necessary. This is much simpler for the user than to remember all the details of how the current option affects certain behavior. Also, with the current default behavior of partial objects everywhere, some standard operations can unexpectedly not work, like adding an object to a collection of a managed object (but the collection itself was not fetched yet). As a result the collection will not be wrapped by doctrine with a PersistentCollection and modifications are lost.
There are more of such examples.

I will do this myself and I think this can even be done with full backwards compatibility (though its not strictly necessary since we're still in alpha).

This should be done before entering beta.






[DDC-32] EntityManager#getReference broken Created: 07/Oct/09  Updated: 07/Oct/09  Resolved: 07/Oct/09

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

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None


 Description   

.






[DDC-26] Add support for subdirectories in classdir when using schema-tool --create Created: 01/Oct/09  Updated: 05/Oct/09  Resolved: 05/Oct/09

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

Type: Improvement Priority: Critical
Reporter: Ismo Toijala Assignee: Jonathan H. Wage
Resolution: Fixed Votes: 0
Labels: None


 Description   

Currently when using schema-tool --create, all entity classes have to be in one directory. Subdirectories are not supported and result in errors. The iterator tries to require the subdirectories. This makes it impossible to use schema-tool --create to create the database for models that use namespaces and follow Doctrine rules for placing the files in subdirectories.

The schema-tool should iterate through all of the subdirectories of the classdir to find all of the entity classes. It should also not try to require() any directories.



 Comments   
Comment by Roman S. Borschel [ 01/Oct/09 ]

Jon, I remember you mentioning this issue already. Did you fix it already?

Comment by Jonathan H. Wage [ 01/Oct/09 ]

No but I will fix it. The problem exists in Doctrine\ORM\Tools\Export\ClassMetadataExporter as well.

Comment by Guilherme Blanco [ 03/Oct/09 ]

We should make is an option, not recursive all the time.... maybe include as an optional parameter --recursive

I'll check it out the issue and will come with a solution later today.





[DDC-21] Already fetched associations should not be overriden by subsequent queries. Created: 25/Sep/09  Updated: 09/Oct/09  Resolved: 09/Oct/09

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

Type: Improvement Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Dependency
depends on DDC-22 EntityManager#refresh() should also r... Closed

 Description   

The discussion about this was brought up by DC-41. After checking the behavior of other ORMs (notably Hibernate), already fetched associations should not be overridden by subsequent queries, just like with other persistent state of already fetched entities. This saves performance and can assure a better integrity of the object model in-memory.

Entities and their associations that are already in-memory should only be refreshed if this is done explicitly either through EntityManager#refresh($entity) or through using the Query::HINT_REFRESH query hint on any query.



 Comments   
Comment by Roman S. Borschel [ 25/Sep/09 ]

This behavior is already correct for single-valued associations but not for collections. Needs to be fixed in ObjectHydrator.





[DDC-22] EntityManager#refresh() should also refresh associations. Created: 25/Sep/09  Updated: 28/Oct/09  Resolved: 28/Oct/09

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

Type: Improvement Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Dependency
is required for DDC-21 Already fetched associations should n... Closed

 Description   

Initially this issue was brought up by DC-41 which in turn led to DDC-21. The implementation of DDC-21 requires a working refresh() implementation that also refreshes associations.

1) For the state of the entity that is refreshed itself this is self-explanatory (simple select on the primary table, this is what already works).

2) For single-valued associations the proper query depends on whether the entity being refreshed represents the owning side or the inverse side of the association. If it is the inverse side, a simple query like this should do:

select addresses.id, ... from addresses where addresses.user_id=?

If it is the owning side, a join may be required:

select addresses.id, ... from addresses inner join users on addresses.id = users.address_id where users.id=?

3) For one-to-many collections, a simple select on the target entity table, similar to the following should do:

select .... from phonenumbers ... where phonenumbers.user_id=?

For many-to-many collections a similar select that joins over the join table is required.

An clever way for collections might be to not trigger this SQL immediately on refresh() but to simply mark the collection as uninitialized again so that the first access would trigger the reload, similar to a lazy-load.

Note that the collection itself is refreshed, not the state of the entities contained therein.
Also note that only associations need to be refreshed that were already initialized. If an associated collection is uninitialized, it does not need to be refreshed. If an associated single-valued proxy is uninitialized, it does not need to be refreshed.






[DDC-2] Proxies in UoW::_computeChangeSets Created: 09/Sep/09  Updated: 06/Oct/09  Resolved: 06/Oct/09

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

Type: Bug Priority: Critical
Reporter: Roman S. Borschel Assignee: Roman S. Borschel
Resolution: Fixed Votes: 1
Labels: None


 Description   

When having a proxy object, say "PhonenumberProxy? extends Phonenumber", and its not yet loaded from the database I think an error occures in the UnitOfWork::_computeChangeSets function since it goes and retrieves that lazy load object value using an instance of "ReflectionProperty?". However with it being lazily loaded the value must be null.






[DDC-3037] cslssl Created: 18/Mar/14  Updated: 18/Mar/14  Resolved: 18/Mar/14

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: Cslssl Assignee: Marco Pivetta
Resolution: Incomplete Votes: 0
Labels: None





[DDC-2972] [GH-948] Update UnitOfWork.php Created: 10/Feb/14  Updated: 10/Feb/14  Resolved: 10/Feb/14

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: Doctrine Bot Assignee: Benjamin Eberlei
Resolution: Invalid Votes: 0
Labels: None


 Description   

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

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

Message:

For datetime objects the comparison doesn't work ex:
array (size=2)
0 =>
object(DateTime)[990]
public 'date' => string '2007-10-25 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Luxembourg' (length=17)
1 =>
object(DateTime)[1876]
public 'date' => string '2007-10-25 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Luxembourg' (length=17)

it's the same date but the scrict comparison make the original value is different of the actual value.



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

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

Comment by Alexander [ 10/Feb/14 ]

http://docs.doctrine-project.org/en/latest/cookbook/working-with-datetime.html





[DDC-2857] [GH-877] [WIP] Add failing test for DDC-1787. Created: 14/Dec/13  Updated: 14/Dec/13