2

I got 3 entities:

1st:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * ProactiveSubject
 *
 * @ORM\Table(name="ProactiveSubject")
 * @ORM\Entity
 */
class ProactiveSubject
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @ORM\ManyToMany(targetEntity="ProactiveCheck", inversedBy="p_subjects")
 * @ORM\JoinTable(name="subject_check_operator")
 */
private $checks;

public function __construct() {
    $this->checks = new \Doctrine\Common\Collections\ArrayCollection();
  }
}

2nd:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * ProactiveCheck
 *
 * @ORM\Table(name="ProactiveCheck")
 * @ORM\Entity
 */
class ProactiveCheck
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @ORM\ManyToMany(targetEntity="ProactiveSubject", mappedBy="ProactiveChecks")
 */
private $p_subjects;

public function __construct() {
    $this->p_subjects = new \Doctrine\Common\Collections\ArrayCollection();
  }

}

and 3rd:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * ProactiveOperator
 *
 * @ORM\Table(name="ProactiveOperator")
 * @ORM\Entity
 */
 class ProactiveOperator
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;
/**
 * @ORM\ManyToMany(targetEntity="ProactiveSubject", inversedBy="p_subjects")
 *
 */
private $p_operator;

public function __construct() {
    $this->p_operator = new \Doctrine\Common\Collections\ArrayCollection();
  }

}

In short, 1 subject may have many checks, and these checks may have many operators, so it should look like that:

subject1 ==> check EQUALITY ==> operator =
subject1 ==> check GREATER  ==> operator >
subject2 ==> check AMOUNT ==> operator = or operator > or operator < etc... depending on user input

I need to make something like a a many_tomany_tomany connection in my db so 3 entities should be connected through 1 join table. The problem is that when I run doctrine:schema:update --force, it connects only 2 entities (operator and subject), but does not connect a Check entity. Any Ideas how to fix that and make a table subject_check_operator with these entities? Any ideas would be welcome. Thank you.

Masha
  • 827
  • 1
  • 10
  • 30

4 Answers4

1

You have several issues here and to me it is not clear what your example with EQUALITY, GREATER, etc. means, but if this:

In short, 1 subject may have many checks, and these checks may have many operators, so it should look like that:

and

The problem is that when I run doctrine:schema:update --force, it connects only 2 entities (operator and subject), but does not connect a Check entity.

are your main issues here, than you need to fix your relational mapping, which contains several errors.

You need to decide wether you want the many-to-many relations to be bidirectional or unidirectional. For bidirectional relation please pay special attention to the documentation about owning and inversing sides of a relation

As you have already included mappedBy and inversedBy settings, I assume you aim for bidirectional relations. When working with bidirectional relations, you specify the attribute name for the mappedBy and inversedBy settings in the annotation and not the class names, etc.

ProactiveCheck

For the relation to subject mappedBy="ProactiveChecks" should be mappedBy="checks". And you are missing the relation to the operator completely

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * ProactiveCheck
 *
 * @ORM\Table(name="ProactiveCheck")
 * @ORM\Entity
 */
class ProactiveCheck
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="ProactiveSubject", mappedBy="checks")
     */
    private $p_subjects;

    /**
     * @ORM\ManyToMany(targetEntity="ProactiveOperator", inversedBy="checks")
     */
    private $operators;

    public function __construct() {
        $this->p_subjects = new \Doctrine\Common\Collections\ArrayCollection();
      }

}

ProactiveOperator

If many checks should have many operators, this class is mapped wrong. You are creating a relation to ProactiveSubject and have no relation to the ProactiveCheck entity. That's why it is not connecting when updating the database schema.

It should look like this:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * ProactiveOperator
 *
 * @ORM\Table(name="ProactiveOperator")
 * @ORM\Entity
 */
 class ProactiveOperator
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    /**
     * @ORM\ManyToMany(targetEntity="ProactiveChecks", mappedBy="operators")
     *
     */
    private $p_operator;

    public function __construct() {
        $this->p_operator = new \Doctrine\Common\Collections\ArrayCollection();
      }
}

By the way, you can easily check the validity of your mapping with the following command: bin/console doctrine:schema:validate

lordrhodos
  • 2,689
  • 1
  • 24
  • 37
  • this solution creates 2 tables instead of 1: `subject<->check` and `check<->operator`. Is it intented or there is an error somewhere? Because with these 2 tables I cannot match operators applied to these checks which are applied to this operator at a time. I have created a separate table-as-entity with 3-column-to-tables relations. Is it correct? – Masha Jun 14 '17 at 13:07
  • Two tables are intended. To my knowledge you can not have m:n relations on two sides of an object with only one join table. I think, you really have to rethink your conceptual approach of what you want to achieve here. Maybe you can add an example of how you want to query the objects? I don't see any issue getting the operators for a certain subject or the other way around using custom queries in e.g. a doctrine repository. – lordrhodos Jun 14 '17 at 13:19
0

Maybe you can make an entity that will have the 3 relations you want (the equivalent of link table generated by doctrine, but custom)

t-n-y
  • 1,201
  • 2
  • 13
  • 27
0

So your trying to make an Entity-(many-to-many)-Entity-(many-to-many)-Entity. This is no different than a single many to many. You were well on the way, however in your 3rd entity you sort of link to the first.

What you want is something (pseudo) like:

e1:
    many-to-many: e2

e2:
    many-to-many: e1
    many-to-many: e3

e3:
    many-to-many: e2

What your currently doing is pointing the reverse of e3 to the reverse of e1 (e2 > e1 property), so just make another property with its own many-to-many in e2 pointing to e3.

Just remember that each relation needs a property in the class if you want to access it.

This will however create two join tables. Which could be fine considering the situation. There is also another way where e2 is essentially the join table with some extra fields. Then you have a different situation and for that I would point you to Doctrine2: Best way to handle many-to-many with extra columns in reference table.

Jenne
  • 864
  • 5
  • 11
0

It is fascinating question and answer is interesting too. If you're faced to the issue, it is mean that your vision and understanding of it is wrong. It is not ManuToMany. This will be another entity that will be responsible for this complex relation. Even if you somehow get away with it. Think about what you will do if you need the date of creating such relations.

<?php

namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class ChildrenFatherMotherMapping
{
    #[ORM\Column(type: Types::INTEGER, nullable: false)]
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'IDENTITY')]
    private int $id;

    #[ORM\ManyToOne(inversedBy: 'childFatherMotherMappings')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Child $child = null;

    #[ORM\ManyToOne(inversedBy: 'childFatherMotherMappings')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Father $father = null;

    #[ORM\ManyToOne(inversedBy: 'childFatherMotherMappings')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Mother $mother = null;

    /** TODO: add getters and setters */
}
Taras Hanych
  • 161
  • 4