[DDC-2252] Trying to delete ManyToMany relatrionship with composite keys. Created: 22/Jan/13  Updated: 16/Apr/13  Resolved: 16/Apr/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.1.7, 2.3.2
Fix Version/s: 2.4
Security Level: All

Type: Bug Priority: Major
Reporter: Jeremie Tom tom Assignee: Fabio B. Silva
Resolution: Fixed Votes: 0
Labels: None
Environment:

Debian
PHP 5.3.7


Attachments: File AdvancedAssociationTest.php     Text File jira-doctrine.txt     File patch.diff    

 Description   

Hi,

When i try to delete some entities attached to an entity I've got the following message.

My entity is specified as follow.

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Entity\User\Account", inversedBy="memberships")
     * @ORM\JoinColumn(name="uid", referencedColumnName="uid")
     */
    protected $userAccount;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Entity\Merchant\Account")
     * @ORM\JoinColumn(name="mch_accountid", referencedColumnName="accountid")
     */
    protected $merchantAccount;

    /**
     * @ORM\Column(type="datetime", name="date")
     * @var datetime
     */
    protected $date;

    /**
     * @ORM\Column(type="boolean")
     * @var boolean
     */
    protected $administrator;

    /**
     * @ORM\ManyToMany(targetEntity="Entity\User\Privilege", indexBy="privilegeid")
     * @ORM\JoinTable(name="fsbackend.user_mch_account_privilege",
     *   joinColumns={
     *       @ORM\JoinColumn(name="mch_accountid", referencedColumnName="mch_accountid"),
     *       @ORM\JoinColumn(name="uid", referencedColumnName="uid")
     *   },
     *   inverseJoinColumns={
     *       @ORM\JoinColumn(name="privilegeid", referencedColumnName="privilegeid")
     *   }
     * )
     */
    protected $privileges;

I delete the related entities

    $membership->getPrivileges()->clear();

    $this->_em->persist($userAccount);
    $this->_em->flush();

I tried on doctrine 2.1.7 and the last doctrine version and same thing happens.

Please find attached the error log.



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

Jeremie Tom tom is the schema validated by the cli tools?

Comment by Jeremie Tom tom [ 22/Jan/13 ]

Yes it's validated by the cli tools, if you are talking about the orm:validate-schema command.

Comment by Jeremie Tom tom [ 23/Jan/13 ]

I think the problem is that my @Id are entities.

Comment by Marco Pivetta [ 23/Jan/13 ]

Jeremie Tom tom can you abstract it away into a test?

Comment by Jeremie Tom tom [ 23/Jan/13 ]

Here is my test case.

I put it IN Doctrine\Test\ORMJT.
I don't really know if you need something else, please let met know.

<?php

namespace Doctrine\Tests\ORMJT;

use Doctrine\ORM\Query;
use Doctrine\Common\Collections\ArrayCollection;

require_once __DIR__ . '/../TestInit.php';

/**
 * Functional tests for the Single Table Inheritance mapping strategy.
 *
 * @author robo
 */
class AdvancedAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
    protected function setUp() {
        parent::setUp();
        try {
            $this->_schemaTool->createSchema(array(
                $this->_em->getClassMetadata('Doctrine\Tests\ORMJT\User'),
                $this->_em->getClassMetadata('Doctrine\Tests\ORMJT\MerchantAccount'),
                $this->_em->getClassMetadata('Doctrine\Tests\ORMJT\Membership'),
                $this->_em->getClassMetadata('Doctrine\Tests\ORMJT\Privilege')
            ));
        } catch (\Exception $e) {
            // Swallow all exceptions. We do not test the schema tool here.
        }
    }

    public function testIssue()
    {
        $user = new User;
        $merchantAccount = new MerchantAccount;
        $privilege = new Privilege;
        $membership = new Membership($user, $merchantAccount);
        $membership->addPrivilege($privilege);

        $this->_em->persist($user);
        $this->_em->persist($merchantAccount);
        $this->_em->persist($privilege);
        $this->_em->flush();

        $this->_em->persist($membership);
        $this->_em->flush();

        $membership->getPrivileges()->clear();

        $this->_em->flush();

        // Never reached
        $this->assertTrue(true);
    }
}


/**
 * @Entity
 * @Table(name="mch_account")
 */
class MerchantAccount
{
    /**
     * @Id @GeneratedValue
     * @Column(type="bigint")
     * @var bigint $accountid
     */
    protected $accountid;
}

/**
 * @Entity(repositoryClass="Repository\User\PrivilegeRepository")
 * @Table(name="acl_privilege")
 */
class Privilege
{
    /**
     * @Id @GeneratedValue
     * @Column(type="bigint")
     * @var integer
     */
    protected $privilegeid;
}

/**
 * @Entity
 * @Table(name="user_account")
 */
class User {
    /**
     * @Id @GeneratedValue
     * @Column(type="bigint")
     * @var integer
     */
    protected $uid;

    /**
     * @OneToMany(targetEntity="Membership", mappedBy="userAccount", cascade={"persist"})
     * @JoinColumn(name="uid", referencedColumnName="uid")
     */
    protected $memberships;

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

    public function getMemberships()
    {
        return $this->memberships;
    }

    public function addMembership(Membership $membership)
    {
        $this->memberships[] = $membership;
    }
}

/**
 * @Entity
 * @Table(name="mch_account_member")
 * @HasLifecycleCallbacks
 */
class Membership
{
    /**
     * @Id
     * @ManyToOne(targetEntity="User", inversedBy="memberships")
     * @JoinColumn(name="uid", referencedColumnName="uid")
     */
    protected $userAccount;

    /**
     * @Id
     * @ManyToOne(targetEntity="MerchantAccount")
     * @JoinColumn(name="mch_accountid", referencedColumnName="accountid")
     */
    protected $merchantAccount;

    /**
     * @ManyToMany(targetEntity="Privilege", indexBy="privilegeid")
     * @JoinTable(name="user_mch_account_privilege",
     *   joinColumns={
     *       @JoinColumn(name="mch_accountid", referencedColumnName="mch_accountid"),
     *       @JoinColumn(name="uid", referencedColumnName="uid")
     *   },
     *   inverseJoinColumns={
     *       @JoinColumn(name="privilegeid", referencedColumnName="privilegeid")
     *   }
     * )
     */
    protected $privileges;

    public function __construct(User $user, MerchantAccount $merchantAccount)
    {
        $this->userAccount = $user;
        $this->merchantAccount = $merchantAccount;
        $this->privileges = new ArrayCollection();
    }

    public function addPrivilege($privilege)
    {
        $this->privileges[] = $privilege;
    }

    public function getPrivileges()
    {
        return $this->privileges;
    }
}

And i've got the following error

1) Doctrine\Tests\ORMJT\AdvancedAssociationTest::testIssue
Exception: [PHPUnit_Framework_Error_Notice] Undefined index: mch_accountid
Comment by Marco Pivetta [ 23/Jan/13 ]

Thank you so far, this makes it much easier to work with it!

Comment by Jeremie Tom tom [ 26/Jan/13 ]

Interesting fact, if I only have one column in my jointable it works fine.

eg.

class Membership
     *   ....
     *   @JoinTable(name="user_mch_account_privilege",
     *       joinColumns={
     *           @JoinColumn(name="uid", referencedColumnName="uid")
     *       }
     *   ....
Comment by Jeremie Tom tom [ 07/Feb/13 ]

Ok here is my two cents solution. passes the tests.

Comment by Jeremie Tom tom [ 05/Mar/13 ]

Hi, any update on this issue?

Comment by Marco Pivetta [ 05/Mar/13 ]

Jeremie Tom tom will be checking this tomorrow

Comment by Jeremie Tom tom [ 14/Mar/13 ]

Everything ok with the check?

Comment by Fabio B. Silva [ 16/Apr/13 ]

Fixed by : https://github.com/doctrine/doctrine2/commit/cef20890dc75c11880827226e25f3fc6d5d66127

Generated at Wed Apr 23 11:54:45 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.