Details

    • Type: New Feature New Feature
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Duplicate
    • Affects Version/s: 2.0-ALPHA3
    • Fix Version/s: None
    • Component/s: ORM
    • Security Level: All
    • Labels:
      None

      Description

      I've come across the need for this in a scenario which is analogous to orders / products.

      I've got an "order" which is linked to one or more "products". However the order (position) in which the products appear in the order is significant. Therefore I have a "position" field in the join table.

      The requirement is:

      • To map the order of the array to values of the "position" field in the join table upon save.
      • To hydrate the array ordered by the position field when hydrating the object.

        Issue Links

          Activity

          Amir Abiri created issue -
          Hide
          Roman S. Borschel added a comment -

          I think is a more general improvement for persisting the order / the keys of any collection.

          This is not trivial though. I dont think it can make it into 2.0 but I will schedule it for 2.1.

          Show
          Roman S. Borschel added a comment - I think is a more general improvement for persisting the order / the keys of any collection. This is not trivial though. I dont think it can make it into 2.0 but I will schedule it for 2.1.
          Roman S. Borschel made changes -
          Field Original Value New Value
          Fix Version/s 2.1 [ 10022 ]
          Affects Version/s 2.0-ALPHA3 [ 10029 ]
          Component/s ORM [ 10012 ]
          Hide
          Amir Abiri added a comment -

          How about a temporary solution? At the moment to work around this limitation I need to keep two collections in my object:

          1. A collection of the join table records, which I am forced to raise to an entity status. This collection persists.
          2. A collection of the actual entities on the other end of the many-to-many relationship.

          Perhaps a possible temporary solution is to create a collection that basically does that but in an encapsulated way? If I had such a collection all I would really need is to re-order it with a map and a closure on @PostLoad. That would still mean that Doctrine saves me a hell of a lot.

          I think properties of a join link are a very common need. I came across this limitation more than once with 1.x as well. Even with Propel before that.

          Show
          Amir Abiri added a comment - How about a temporary solution? At the moment to work around this limitation I need to keep two collections in my object: A collection of the join table records, which I am forced to raise to an entity status. This collection persists. A collection of the actual entities on the other end of the many-to-many relationship. Perhaps a possible temporary solution is to create a collection that basically does that but in an encapsulated way? If I had such a collection all I would really need is to re-order it with a map and a closure on @PostLoad. That would still mean that Doctrine saves me a hell of a lot. I think properties of a join link are a very common need. I came across this limitation more than once with 1.x as well. Even with Propel before that.
          Hide
          Roman S. Borschel added a comment -

          Your current solution is not bad at all if I understand it right. It is normal that when you have additional fields in the join table you need to map the join table as an association class. Call it a "many-to-many between A and B through C". Its how you would do it in plain OOP, too.

          Given:

          A manytomany B
          

          Now you want to add a field to the association. You can not add it to class A or B. You need to create a class in-between (C, the association class).

          That results in:

          A onetomany C
          C manytoone A
          B onetomany C
          C manytoone B
          

          I agree, however, that the order is something special, since it is implicitly represented by the order of the elements in the collection. You dont need an association class in plain OOP for that and Doctrine should be able to handle that transparently, too. I agree there, its just not yet possible.

          Right now the only way to order a collection-valued association is in a DQL query or in PHP, I guess you know that already.

          Additionally, we want to add something like: @OrderBy("foo ASC, bar DESC") that you can apply on a collection-valued association so that the collection is always ordered, whether you use DQL or find()/findAll()/lazy loading/ ...

          However, this does not yet cover the "persistence" of the order in the relational database which is the more complicated part.

          I dont understand your proposed temporary solution. Can you show some (peudo)code that shows how you imagine that?

          Thanks!

          Show
          Roman S. Borschel added a comment - Your current solution is not bad at all if I understand it right. It is normal that when you have additional fields in the join table you need to map the join table as an association class. Call it a "many-to-many between A and B through C". Its how you would do it in plain OOP, too. Given: A manytomany B Now you want to add a field to the association. You can not add it to class A or B. You need to create a class in-between (C, the association class ). That results in: A onetomany C C manytoone A B onetomany C C manytoone B I agree, however, that the order is something special, since it is implicitly represented by the order of the elements in the collection. You dont need an association class in plain OOP for that and Doctrine should be able to handle that transparently, too. I agree there, its just not yet possible. Right now the only way to order a collection-valued association is in a DQL query or in PHP, I guess you know that already. Additionally, we want to add something like: @OrderBy("foo ASC, bar DESC") that you can apply on a collection-valued association so that the collection is always ordered, whether you use DQL or find()/findAll()/lazy loading/ ... However, this does not yet cover the "persistence" of the order in the relational database which is the more complicated part. I dont understand your proposed temporary solution. Can you show some (peudo)code that shows how you imagine that? Thanks!
          Hide
          Amir Abiri added a comment -

          I am not disputing what you say, I'm just saying that Doctrine can offer a collection class that encapsulates the repeating pattern of join properties.

          Consider the following code snippet:
          (Assume that each article has a different priority in each category it is in, where the priority can be only "Low", "Medium" or "High")

          /**
           * @Entity
           * @Table(name="article")
           */
          class Article
          {
            // ...
          }
          
          /**
           * @Entity
           * @Table(name="article_category")
           */
          class ArticleCategory
          {
              const PRI_LOW  = 1;
              const PRI_MED  = 2;
              const PRI_HIGH = 3;
          
              /**
               * @OneToMany(targetEntity="Article",...)
               */
              private $article;
          
              /**
               * @OneToMany(targetEntity="Category",...)
               */
              private $category;
          
              /**
               * @Column(type="integer")
               */
              private $priority;
          }
          
          /**
           * @Entity
           * @Table(name="category")
           */
          class Category
          {
              /**
               * @ManyToMany(targetEntity="Article",...,joinEntity="ArticleCategory")
               */
              private $articles;
          
          
          
              public function getArticles()
              {
                  return $this->articles->toArray(); // Returns an array of articles.
              }
          
              public function getArticleByIndex($idx)
              {
                  return $this->articles[$idx];
              }
          
              public function getArticlePriority($idx)
              {
                  return $this->articles->getJoinEntity($idx)->getPriority();
                  // or alternatively:
                  return $this->articles->joinEntities[$idx]->getPriority();
              }
          }
          

          Since Doctrine 2.0 is already being a bit invasive with the PersistentCollection class anyway, this does not present any additional invasiveness. You just get a subclass of PersistentCollection called PersistentManyToManyCollection if you specify the joinEntity in the @ManyToMany tag.

          If you provide this functionality, the users can take care of such things like the order by themselves. They can also do it in a less invasive manner: they have full control over how to store the order property.

          Show
          Amir Abiri added a comment - I am not disputing what you say, I'm just saying that Doctrine can offer a collection class that encapsulates the repeating pattern of join properties. Consider the following code snippet: (Assume that each article has a different priority in each category it is in, where the priority can be only "Low", "Medium" or "High") /** * @Entity * @Table(name= "article" ) */ class Article { // ... } /** * @Entity * @Table(name= "article_category" ) */ class ArticleCategory { const PRI_LOW = 1; const PRI_MED = 2; const PRI_HIGH = 3; /** * @OneToMany(targetEntity= "Article" ,...) */ private $article; /** * @OneToMany(targetEntity= "Category" ,...) */ private $category; /** * @Column(type= "integer" ) */ private $priority; } /** * @Entity * @Table(name= "category" ) */ class Category { /** * @ManyToMany(targetEntity= "Article" ,...,joinEntity= "ArticleCategory" ) */ private $articles; public function getArticles() { return $ this ->articles->toArray(); // Returns an array of articles. } public function getArticleByIndex($idx) { return $ this ->articles[$idx]; } public function getArticlePriority($idx) { return $ this ->articles->getJoinEntity($idx)->getPriority(); // or alternatively: return $ this ->articles->joinEntities[$idx]->getPriority(); } } Since Doctrine 2.0 is already being a bit invasive with the PersistentCollection class anyway, this does not present any additional invasiveness. You just get a subclass of PersistentCollection called PersistentManyToManyCollection if you specify the joinEntity in the @ManyToMany tag. If you provide this functionality, the users can take care of such things like the order by themselves. They can also do it in a less invasive manner: they have full control over how to store the order property.
          Hide
          Roman S. Borschel added a comment - - edited

          Thats not how you map a many-to-many with an association class. There is no @ManyToMany at all.

          See below:

          class Article {
             private $id;
             /** @OneToMany(targetEntity="ArticleCategorization", ....) */
              private $categorizations;
          }
          
          class Category {
             private $id;
             /** @OneToMany(targetEntity="ArticleCategorization", ...) */
             private $categorizations;
          }
          
          class ArticleCategorization {
              private $id;
             /**
              * @ManyToOne(targetEntity="Article", ...)
              * @JoinColumn(...) 
              */
              private $article;
             /** 
              * @ManyToOne(targetEntity="Category", ...)
              * @JoinColumn(...)
              */
              private $category;
             /**
              * @Column(type="integer")
              */
              private $priority;
          }
          

          Now, order is a different thing. Just to persist the order of a collection should not require an association class (currently it does), I guess we agree on that.

          I dont see how PersistentCollection is invasive, at least its the best we can do. You program to the Collection interface, you never need to care about PersistentCollection and should certainly not use methods that are not on the Collection interface. Collections of entities can not be arrays. PHP's arrays are not suitable as collections of entities. (We're still waiting for something like SplArray that implements sth like an SplCollection interface, the OO version of arrays .

          Show
          Roman S. Borschel added a comment - - edited Thats not how you map a many-to-many with an association class. There is no @ManyToMany at all. See below: class Article { private $id; /** @OneToMany(targetEntity= "ArticleCategorization" , ....) */ private $categorizations; } class Category { private $id; /** @OneToMany(targetEntity= "ArticleCategorization" , ...) */ private $categorizations; } class ArticleCategorization { private $id; /** * @ManyToOne(targetEntity= "Article" , ...) * @JoinColumn(...) */ private $article; /** * @ManyToOne(targetEntity= "Category" , ...) * @JoinColumn(...) */ private $category; /** * @Column(type= "integer" ) */ private $priority; } Now, order is a different thing. Just to persist the order of a collection should not require an association class (currently it does), I guess we agree on that. I dont see how PersistentCollection is invasive, at least its the best we can do. You program to the Collection interface, you never need to care about PersistentCollection and should certainly not use methods that are not on the Collection interface. Collections of entities can not be arrays. PHP's arrays are not suitable as collections of entities. (We're still waiting for something like SplArray that implements sth like an SplCollection interface, the OO version of arrays .
          Hide
          Amir Abiri added a comment -

          My point was that instead of breaking it down like that, it can be defined as a @ManyToMany with a twist.

          I wasn't criticizing you about PersistentCollection, I was merely repeating what you guys wrote in the manual. I completely agree with your design decision there and I also blame PHP for not implementing the whole Array / Object fiasco properly. All I'm saying is that what I'm proposing would not introduce any new constraints to the entities.

          Show
          Amir Abiri added a comment - My point was that instead of breaking it down like that, it can be defined as a @ManyToMany with a twist. I wasn't criticizing you about PersistentCollection, I was merely repeating what you guys wrote in the manual. I completely agree with your design decision there and I also blame PHP for not implementing the whole Array / Object fiasco properly. All I'm saying is that what I'm proposing would not introduce any new constraints to the entities.
          Hide
          Guilherme Blanco added a comment -

          Both expose same enhancement.

          Show
          Guilherme Blanco added a comment - Both expose same enhancement.
          Guilherme Blanco made changes -
          Link This issue duplicates DDC-195 [ DDC-195 ]
          Hide
          Benjamin Eberlei added a comment -

          This is a duplicate of DDC-213

          Show
          Benjamin Eberlei added a comment - This is a duplicate of DDC-213
          Benjamin Eberlei made changes -
          Status Open [ 1 ] Resolved [ 5 ]
          Assignee Roman S. Borschel [ romanb ] Benjamin Eberlei [ beberlei ]
          Fix Version/s 2.1 [ 10022 ]
          Resolution Duplicate [ 3 ]
          Benjamin Eberlei made changes -
          Link This issue duplicates DDC-213 [ DDC-213 ]
          Benjamin Eberlei made changes -
          Workflow jira [ 10523 ] jira-feedback [ 14189 ]
          Benjamin Eberlei made changes -
          Workflow jira-feedback [ 14189 ] jira-feedback2 [ 16053 ]
          Benjamin Eberlei made changes -
          Workflow jira-feedback2 [ 16053 ] jira-feedback3 [ 18306 ]

          This list may be incomplete, as errors occurred whilst retrieving source from linked applications:

          • Request to http://www.doctrine-project.org/fisheye/ failed: Error in remote call to 'FishEye 0 (http://www.doctrine-project.org/fisheye/)' (http://www.doctrine-project.org/fisheye) [AbstractRestCommand{path='/rest-service-fe/search-v1/crossRepositoryQuery', params={query=DDC-181, expand=changesets[0:20].revisions[0:29],reviews}, methodType=GET}] : Received status code 503 (Service Temporarily Unavailable)

            People

            • Assignee:
              Benjamin Eberlei
              Reporter:
              Amir Abiri
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: