Doctrine requires mappedBy in a OneToMany unidirectional association

15,856

A OneToMany has to be bi-directional and is always the inverse side of a relationship and if you define the inverse side you need to point at the owning side of the relationship with a mappedBy attribute. The owning side is ManyToOne. In your case this would look like this:

In User your association has a mappedBy="user" attribute and points to the owning side Address:

/** ONE-TO-MANY BIDIRECTIONAL, INVERSE SIDE
 * @var Collection
 * @ORM\OneToMany(targetEntity="Address", mappedBy="user")
 */
protected $addresses;

In Address your association has a inversedBy="addresses" attribute and points to the inverse side User:

/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
 * @var User
 * @ORM\ManyToOne(targetEntity="User", inversedBy="addresses")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
protected $user;

The advantage of this solution is that you can find which user owns the Address by doing: $address->getUser(); and that you skip adding an additional join table to your database.


If you want the relationship to be uni-directional you can do as you did in your update; define a ManyToMany relationship with a join table and add a unique constraint on the address_id column.

/**
 * @ORM\ManyToMany(targetEntity="Address")
 * @ORM\JoinTable(name="user_address",
 *     joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *     inverseJoinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id", unique=true)}
 * )
 */

The disadvantage of this solution is that you cannot find out which User owns the address from the Address resource (the Address is not aware of the User). Such example can also be found here in the Doctrine documentation chapter 5.6. One-To-Many, Unidirectional with Join Table.

Share:
15,856

Related videos on Youtube

mevqz
Author by

mevqz

Android Apps

Updated on September 15, 2022

Comments

  • mevqz
    mevqz over 1 year

    When I try to make a OneToMany unidirectional association between this two entities i get this error when i try to update the database schema:

    $ app/console doctrine:schema:update --dump-sql

    [Doctrine\ORM\Mapping\MappingException]
    OneToMany mapping on field 'address' requires the 'mappedBy' attribute.

    /**
     * User
     *
     * @ORM\Table()
     * @ORM\Entity
     */
    class User
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        /**
         * @ORM\OneToMany(targetEntity="Address")
         * @ORM\JoinTable(name="users_address",
         *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
         *      inverseJoinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id", unique=true)}
         *      )
         */
        private $address;
    
        //...
    }
    
    /**
     * Address
     *
     * @ORM\Table()
     * @ORM\Entity
     */
    class Address
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        // ...
    }
    

    Why is "mappedBy" required if the association is unidirectional? Thanks in advance.

    UPDATE: just as mentioned in the comment by @tchap an unidirectional OneToMany can be mapped with a @ManyToMany and a unique constraint on one of the join columns to enforce the onetomany cardinality. Just as the documentation says, but it was a bit confusing for me because there is already a @OneToMay annotation. So I just have to change the above code to this (by only changing the @OneToMany to @ManyToMany):

    /**
     * @ORM\ManyToMany(targetEntity="Address")
     * @ORM\JoinTable(name="users_address",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id", unique=true)}
     * )
     */
    private $address;
    
    • Remi M
      Remi M over 7 years
      @tchap to me A JoinTable for a OneToMany association makes sense if the entities on the Many side can be owned by different type of entities. In this case the typical foreign key on this side won't be enough to distinguish the owner unless you use UUID for all your entities.
  • mevqz
    mevqz over 8 years
    Thanks. You got a typo for the owning side (it should be ManyToOne instead OneToMany), but this is the correct answer.
  • Wilt
    Wilt over 8 years
    @mevqz oops, fixed it :P
  • Remi M
    Remi M over 7 years
    @Wilt In your last example, I suppose you talk about the User entity since the target entity is Address. There's a typo, the address_id with the unique =true property is in the inverseJoinColumns, not the joinColumns :)
  • Wilt
    Wilt over 7 years
    @Azuli42 Very true, thanks! I fixed it and added a link to the documentation. I copied the annotation from the question, so the same mistake was made there.
  • Wilt
    Wilt over 7 years
    @Azuli42 I edited the question and fixed it there too.