5
votes

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;
1
I don't really understand why you want a JoinTable for a OneToMany association, but I may miss something ? - tchap
For nothing specific. I am just learning about doctrine. - mevqz
Then you can drop it I guess. OneToMany association are bidirectional by default as well, so you need a mappedBy and inversedBy, see doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/…. Your unidirectional OneToMany is just a ManyToMany in disguise - tchap
thanks, now I get it. - mevqz
@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. - Remi M

1 Answers

11
votes

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.