Google Mail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
Many-To-Many Associations with additional fields
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  10 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Follow-up To:
Add Cc | Add Follow-up to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers that you hear
 
Nico Kaiser  
View profile   Translate to Translated (View Original)
 More options 29 Oct, 17:10
From: Nico Kaiser <n...@kaiser.me>
Date: Thu, 29 Oct 2009 18:10:58 +0100
Local: Thurs 29 Oct 2009 17:10
Subject: Many-To-Many Associations with additional fields

Imagine there are two entities:

- User
- Subscription

Each User can have several Subscriptions, and of course each
Subscription can be subscribed by many Users (n:m).

When implementing this with Doctrine 2.0, I use a "join table" (e.g.
users_subscriptions") which holds (user_id, subscription_id) and a
"ManyToMany" mapping - The \Doctrine\ORM\Tools\Cli automatically creates
the "users_subscriptions" DB table:

/** @Entity */
class User
{
  /**
    * @ManyToMany(targetEntity="Subscription")
    * @JoinTable(name="users_subscriptions",
    *   joinColumns={@JoinColumn(name="user_id",
referencedColumnName="id")},
    *   inverseJoinColumns={@JoinColumn(name="subscription_id",
referencedColumnName="id")}
    *      )
    */
  private $subscriptions;
  // ...

}

However if I'd like to add additional data for the junction between
Users and Subscriptions, I need more fields in the "users_subscriptions"
table (like subscription expiry, etc.).

How can I realize this with Doctrine 2.0?

I tried to introduce a new Entity that maps to the "users_subscriptions"
table and holds "user_id", "subscription_id" and my additional fields.
But this fails when Doctrine 2 complains about "No identifier/primary
key specified" (Primary key is user_id AND subscription_id!)

Nico


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Roman Borschel  
View profile   Translate to Translated (View Original)
 More options 29 Oct, 17:20
From: Roman Borschel <r.borsc...@gmx.net>
Date: Thu, 29 Oct 2009 18:20:08 +0100
Local: Thurs 29 Oct 2009 17:20
Subject: Re: [doctrine-user] Many-To-Many Associations with additional fields

Hi,

On Oct 29, 2009, at 6:10 PM, Nico Kaiser wrote:

<snip>

> I tried to introduce a new Entity that maps to the  
> "users_subscriptions"
> table and holds "user_id", "subscription_id" and my additional fields.
> But this fails when Doctrine 2 complains about "No identifier/primary
> key specified" (Primary key is user_id AND subscription_id!)

> Nico

You're already on the right way. As soon as the association table has  
additional fields you must map it into a full entity and turn the  
associations into one-many/one-one.

And, in such a case it is recommended best practice to give the  
association class its own id, just like a regular entity. And since it  
is discouraged to map foreign keys to object fields (because they have  
no meaning) just use a separate, incremental id.

Composite keys are in general possible (just specify @Id on the mapped  
fields that compose the PK) but not recommended as dealing with  
composite keys is more complicated for you and the ORM.

/** @Entity */
class UsersSubscriptions {
     /** @Id @GeneratedValue(strategy="AUTO") */
     private $id;
     // map the following two as one-one
     private $user;
     private $subscription;

}

The only case where a composite key is fine and where doctrine indeed  
creates one is in a "real" many-many, where there is no association  
class.

Roman


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Roman Borschel  
View profile   Translate to Translated (View Original)
 More options 29 Oct, 20:20
From: Roman Borschel <r.borsc...@gmx.net>
Date: Thu, 29 Oct 2009 21:20:11 +0100
Local: Thurs 29 Oct 2009 20:20
Subject: Re: [doctrine-user] Re: Many-To-Many Associations with additional fields

Oh, and you should of course index the user_id and subscription_id,  
since they're used for joining. Foreign keys might be indexed  
automatically on some database platforms, I'm not sure.

You can do it with a unique constraint like this in annotations, I  
think a unique constraint might be the best idea:

/**
  * @Entity
  * @Table(name="foo",  
uniqueConstraints={@UniqueConstraint(name="fkunique",  
columns={"user_id", "subscription_id"})})
  */
class Foo {... }

Unique constraints are indexed automatically I think. Otherwise  
indexes can be defined like so in annotations:

/**
  * @Entity
  * @Table(name="foo", indexes={@Index(name="fkindex",  
columns={"user_id", "subscription_id"})})
  */
class Foo {... }

Roman

On Oct 29, 2009, at 6:20 PM, Roman Borschel wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nico Kaiser  
View profile   Translate to Translated (View Original)
 More options 30 Oct, 11:05
From: Nico Kaiser <n...@kaiser.me>
Date: Fri, 30 Oct 2009 04:05:43 -0700 (PDT)
Local: Fri 30 Oct 2009 11:05
Subject: Re: Many-To-Many Associations with additional fields
Ok, thanks!

I guess there is no way to build a composite primary key from a mapped
entity and another field? (Event if it's discouraged, I'd really like
to keep the current database model)

Something like this (each user can have several usernames, and in the
user_login table (userId, username) should be primary key:

/**
 * @Entity
 * @Table(name="user_login")
 */
class Login
{
    /**
     * @Column(name="username", type="string", length=45)
     * @Id
     */
    private $username;

    /**
     * @ManyToOne(targetEntity="Entity\User\UserAbstract")
     * @JoinColumn(name="userId", referencedColumnName="id")
     * @Id
     */
    private $User;

    // ...

}

Nico

On 29 Okt., 21:20, Roman Borschel <r.borsc...@gmx.net> wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nico Kaiser  
View profile   Translate to Translated (View Original)
 More options 2 Nov, 15:02
From: Nico Kaiser <n...@kaiser.me>
Date: Mon, 2 Nov 2009 07:02:39 -0800 (PST)
Local: Mon 2 Nov 2009 15:02
Subject: Re: Many-To-Many Associations with additional fields
I know it's complicated for the ORM (performace-wise) and not that
elegant. But I really do need to keep the database structure (which
uses composite keys) as in the example I gave. I cannot add additional
fields, as we need to use the same database with old classes (not only
DDC)...

E.g. the "I18n" behavior of Doctrine 1.x generates DB tables with (id,
lang) as primary key. To simulate their behavior (and re-use the
existing DB tables without modifications!!), I need composite keys...

Nico

On 30 Okt., 12:05, Nico Kaiser <n...@kaiser.me> wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Roman Borschel  
View profile   Translate to Translated (View Original)
 More options 2 Nov, 15:45
From: Roman Borschel <r.borsc...@gmx.net>
Date: Mon, 2 Nov 2009 16:45:28 +0100
Local: Mon 2 Nov 2009 15:45
Subject: Re: [doctrine-user] Re: Many-To-Many Associations with additional fields

Well, you can try to do it like this:

/** @Entity */
class UsersSubscriptions {
    /** @Id @Column(type="integer") */
    private $userId;
    /** @Id @Column(type="integer") */
    private $subscriptionId;

    // map the following two as one-one
    private $user;
    private $subscription;

}

I would just make sure that $userId and $subscriptionId is not  
revealed to the public, if possible.

So whenever you need a composite PK that is contains a FK I guess you  
have no other choice than to map the FK to an entity field.

Will let you know if I have a better idea.

On Nov 2, 2009, at 4:02 PM, Nico Kaiser wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nico Kaiser  
View profile   Translate to Translated (View Original)
 More options 3 Nov, 13:21
From: Nico Kaiser <n...@kaiser.me>
Date: Tue, 3 Nov 2009 05:21:46 -0800 (PST)
Local: Tues 3 Nov 2009 13:21
Subject: Re: Many-To-Many Associations with additional fields
Ok, great, this seems to work!
However, one last thing: userId / subscriptionId are not updated
correctly:

$assoc = new \UsersSubscriptions();
$em->persist($assoc);
$assoc->setUser($user);
$assoc->setSubscription($subscription);
$em->flush();

The private variable "$assoc->userId" is empty now, which prevents
"$em->remove($assoc)" from working correctly!

Do I have to invoke a lifecylce event to update e.g. "$this->userId =
$this->User()->getId;"?

Nico

On 2 Nov., 16:45, Roman Borschel <r.borsc...@gmx.net> wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Nico Kaiser  
View profile   Translate to Translated (View Original)
 More options 3 Nov, 13:31
From: Nico Kaiser <n...@kaiser.me>
Date: Tue, 3 Nov 2009 05:31:04 -0800 (PST)
Local: Tues 3 Nov 2009 13:31
Subject: Re: Many-To-Many Associations with additional fields
Another thing:
For the above code, I modified the UsersSubscriptions::setUser to set
the userId when adding a User:

    public function setUser($value)
    {
        $this->User = $value;
        $this->userId = $this->User->getId();
    }

Now when I call the above code, and then remove the object again with
"$em->remove($assoc);", the SQL dumper outputs this:

DELETE FROM user_subscription WHERE userId = ? AND subscriptionId = ?
array(2) { [0]=>NULL [1]=>NULL }

So, DDC correctly identifies the composite PK but does not get its
values correctly (neigher from "$assoc->userId" nor from "$assoc->User-

>getId()".

Is it a but in DDC or did I do something wrong?

Nico

On 3 Nov., 14:21, Nico Kaiser <n...@kaiser.me> wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Roman Borschel  
View profile   Translate to Translated (View Original)
 More options 5 Nov, 08:51
From: Roman Borschel <r.borsc...@gmx.net>
Date: Thu, 5 Nov 2009 09:51:24 +0100
Local: Thurs 5 Nov 2009 08:51
Subject: Re: [doctrine-user] Re: Many-To-Many Associations with additional fields
Hi,

On Nov 3, 2009, at 2:21 PM, Nico Kaiser wrote:

Primary keys should never be changed/updated. I would do it like this:

class UserSubscription {
   // map these as appropriate
   private $userId;
   private $subscriptionId;
   private $user;
   private $subscription;

   public function __construct(User $u = null, Subscription $s = null) {
       if ($u !== null) {
          $this->user = $u;
          $this->userId = $u->getId();
       }
       if ($s !== null) {
          $this->subscription = $s;
          $this->subscriptionId = $s->getId();
       }
   }

   public function getUser() {return $this->user;}
   public function getSubscription() {return $this->subscription;}

}

...

$em->persist(new UserSubscription($user, $subscription));

Does the code you wrote actually work? I mean this:

$assoc = new UsersSubscriptions;
$em->persist($assoc);
...

Because persist() should bark at you that the PK is not set.


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Roman Borschel  
View profile   Translate to Translated (View Original)
 More options 5 Nov, 08:58
From: Roman Borschel <r.borsc...@gmx.net>
Date: Thu, 5 Nov 2009 09:58:44 +0100
Local: Thurs 5 Nov 2009 08:58
Subject: Re: [doctrine-user] Re: Many-To-Many Associations with additional fields
Here is some more information on the subject (its Java but should look  
very familar).

http://sieze.wordpress.com/2009/09/04/mapping-a-many-to-many-join-tab...

If you want you can open a ticket about allowing @Id on a @ManyToOne  
field.

On Nov 5, 2009, at 9:51 AM, Roman Borschel wrote:


    Reply    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google