1

I have a problem with Symfony3/doctrine2 and many-to-many with extra columns. I just start to develop with it

Yes, I see the post : Doctrine2: Best way to handle many-to-many with extra columns in reference table But honestly, I don't understand why it's not working with my code...

I'm stuck since 2 days, looking on Google (he's not my friend), reading documentations... my brain doesn't understand...

Please, help me to understand why.

Problem

I have member and address entities. I would like to have all addresses by member OR all members by address. So, ManyToMany in Doctrine. On my join Table, I need to have an extra column favorite_address So, I need to have 3 entities : Member, Address, MemberAddress

Entity Member :

class Member implements AdvancedUserInterface
{

....

/**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="member", cascade={"all"})
 * @ORM\JoinTable(name="member_address",
 *      joinColumns={@ORM\JoinColumn(name="member_id", referencedColumnName="id")})
 */
private $addresses;

....

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

/**
 * Add an address
 * @param Address $address
 */
public function addAddress(\AppBundle\Entity\Address\Address $address)
{
    // Ici, on utilise l'ArrayCollection vraiment comme un tableau
    $this->addresses[] = $address;
}

/**
 * Remove an address
 * @param Address $address
 */
public function removeAddress(\AppBundle\Entity\Address\Address $address)
{
    // Ici on utilise une méthode de l'ArrayCollection, pour supprimer la catégorie en argument
    $this->addresses->removeElement($address);
}

/**
 * Get all addresses
 * @return ArrayCollection
 */
public function getAddresses()
{
    return $this->addresses;
}

Entity Address :

class Address
{
....

/**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="address", cascade={"all"})
 * @ORM\JoinTable(name="member_address",
 *      joinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id")})
 */
private $members;

....

/**
 * Add a member
 * @param Member $member
 */
public function addMember(\AppBundle\Entity\Member\Member $member)
{
    // Ici, on utilise l'ArrayCollection vraiment comme un tableau
    $this->members[] = $member;
}

/**
 * Remove a member
 * @param Member $member
 */
public function removeMember(\AppBundle\Entity\Member\Member $member)
{
    // Ici on utilise une méthode de l'ArrayCollection, pour supprimer la catégorie en argument
    $this->members->removeElement($member);
}

/**
 * Get all members
 * @return ArrayCollection
 */
public function getMembers()
{
    return $this->members;
}

And the last Entity : MemberAddressReference

class MemberAddress
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/** @ORM\ManyToOne(targetEntity="AppBundle\Entity\Member\Member", inversedBy="addresses")
 *  @ORM\JoinColumn(name="member_id", referencedColumnName="id")
 */
protected $member;

/** @ORM\ManyToOne(targetEntity="AppBundle\Entity\Address\Address", inversedBy="members")
 *  @ORM\JoinColumn(name="address_id", referencedColumnName="id")
 */
protected $address;

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

To finish, the controller

class MemberAddressController extends Controller
{

public function createAction(Request $request){
    ....
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $currentDate = new \DateTime("now");

        $em = $this->getDoctrine()->getManager();
        $address = new Address();
        $memberAddress = new MemberAddress();

        $address->setType($form['type']->getData());
        $address->setCreated($currentDate);
        $address->setModified($currentDate);

        $memberAddress->setMember($member);
        $memberAddress->setAddress($address);
        $memberAddress->setFavorite(1);

        $em->persist($member);
        $em->persist($address);
        $em->persist($memberAddress);

        $member->addAddress($address);

        $em->flush();

        dump($member);
        die();
    }

So, what's wrong

I get this error :

Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Member\Member#$addresses", got "AppBundle\Entity\Address\Address" instead.

Yup, type is not good, I understand, but why he's not good ?

public function addAddress(\AppBundle\Entity\Address\Address $address)
{
    // Ici, on utilise l'ArrayCollection vraiment comme un tableau
    $this->addresses[] = $address;
}

addAddress take Address object, no ? So why he's waiting an array ?

Please help me, I'm going crazy...

Community
  • 1
  • 1
Skyd
  • 535
  • 1
  • 4
  • 20
  • The `@JoinTable` annotation is incorrect and should be removed. It’s applicable only for the simplest many-to-many relationships without extra attributes. – Lumen Jan 26 '17 at 12:14
  • ok, thank you. I removed it. But still error on save. – Skyd Jan 26 '17 at 12:42

1 Answers1

0

I don't understand the goal of your memberAdress entity. If you want to create a OneToMany bidirectional relationship, you don't need to create a third entity. According to doctrine documentation :

   /** @Entity */
class Product
{
    // ...
    /**
     * One Product has Many Features.
     * @OneToMany(targetEntity="Feature", mappedBy="product")
     */
    private $features;
    // ...

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

/** @Entity */
class Feature
{
    // ...
    /**
     * Many Features have One Product.
     * @ManyToOne(targetEntity="Product", inversedBy="features")
     * @JoinColumn(name="product_id", referencedColumnName="id")
     */
    private $product;
    // ...
}

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

EDIT

If you want to use a third entity, you don't have a relationship between your adress and your member. They are just linked by the third party. So you should do the mapping this way :

Member Entity :

class Member implements AdvancedUserInterface
{
/**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="member", cascade={"all"})
 */
private $addresses;

Adress Entity

class Address
{
/**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="address", cascade={"all"})
 */
private $members;

If you want all the adresses of a member you will need to create a custom repository to join your adress table with the two other tables.

actarus
  • 46
  • 1
  • 6
  • Because 1 address can have N members and not only 1 member. (If i want the list of member who works at this address), So, I need to use ManyToMany, no ? It's not correct ? And, if I need to add an extra columns for favorite address, I need this 3rd entity, no ? – Skyd Jan 26 '17 at 09:55
  • Ok, so use a ManyToMany relationship as the documentation link i provided explain. You don't need a third entity, doctrine will create a joinTable itself and manage the relationship. – actarus Jan 26 '17 at 10:02
  • doctrine will create a joinTable with ManyToMany, sure, I'm agree with you, but not if I need to add an extra Column to it. I need to know the favorite address between my members and their addresses. So, I see on Google and here [link]http://stackoverflow.com/questions/3542243/doctrine2-best-way-to-handle-many-to-many-with-extra-columns-in-reference-table[/link], that I need a 3rd Entity to add an extra Column. There is an other method to do what I would like to do ? – Skyd Jan 26 '17 at 10:03
  • ok, i'm sorry, i haven't understand your question. But are you using a third entity, link to two other entity with oneToMany relationship. You don't need a join table like you are using in the member entity for adress attribute... I think the problem is here – actarus Jan 26 '17 at 10:08
  • Sorry if it was not clear at the beginning. _"But are you using a third entity, link to two other entity with oneToMany relationship"._ Yes, it's memberAddress. _"You don't need a join table like you are using in the member entity for adress attribute... I think the problem is here"_ Sorry, I don't understand. So, how can i do it to have all adresses by member OR all members by address ? – Skyd Jan 26 '17 at 10:27