На главную
  Назад в „Symfony2“  

Организация отношения один-ко-многим в Symfony2 с использованием ORM Doctrine

Думаю в начале нужно пару слов сказать об отношении один-ко-многим. В общем случае, это когда у одной записи в основной таблиц может быть несколько подчинённых в другой таблице. В моём случае имеет место быть следующее отношение:

Отношение один-ко-многим
Т.е. у каждого пользователя есть описания на каждом из поддерживаемых системой языков.

В терминах Symfony2 / Doctrine, для каждой таблицы из базы данных создаётся своя сущность (а бывает, что и несколько разных сущностей). Класс Entity, для таблицы Customer он выглядит примерно так:

namespace QR1000\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \Doctrine\Common\Collections\ArrayCollection as ArrayCollection;

/**
 * QR1000\MainBundle\Entity\Customer
 *
 * @ORM\Table(name="customer")
 * @ORM\Entity(repositoryClass="QR1000\MainBundle\StoreBundle\Repository\CustomerRepository")
 */

class Customer
{
    /**
     * @var integer $id
     *
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */

    private $id;
.
.
.
    /**
     * @ORM\OneToMany(targetEntity="CustomerInfo", mappedBy="customer", cascade={"all"}, orphanRemoval=true)
     */

    protected $infoArray;

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

    /**
     * Add infoArray
     *
     * @param QR1000\MainBundle\Entity\CustomerInfo $infoArray
     */

    public function addCustomerInfo(\QR1000\MainBundle\Entity\CustomerInfo $infoArray)
    {
        $this->infoArray[] = $infoArray;
    }

    /**
     * Get infoArray
     *
     * @return Doctrine\Common\Collections\Collection
     */

    public function getInfoArray()
    {
        return $this->infoArray;
    }
}
 Как Вы возможно видите, тут требуемая связь уже описана, но детальнее об этом позже.

Также опишем сущность для customer_info:

namespace QR1000\MainBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * QR1000\MainBundle\Entity\CustomerInfo
 *
 * @ORM\Table(name="customer_info")
 * @ORM\Entity(repositoryClass="QR1000\MainBundle\StoreBundle\Repository\CustomerInfoRepository")
 */

class CustomerInfo
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */

    private $id;
.
.
.
    /**
     *
     * @ORM\ManyToOne(targetEntity="Customer", inversedBy="infoArray")
     * @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
     */

    protected $customer;
.
.
.

    /**
     * Set customer
     *
     * @param QR1000\MainBundle\Entity\Customer $customer
     */

    public function setCustomer(\QR1000\MainBundle\Entity\Customer $customer)
    {
        $this->customer = $customer;
    }

    /**
     * Get customer
     *
     * @return QR1000\MainBundle\Entity\Customer
     */

    public function getCustomer()
    {
        return $this->customer;
    }
}
 Тут также уже всё описано.

Кстати, все геттеры/сеттеры не пишутся в ручную, а генерируются автоматически, с помощью одного из удобных консольных инструментов, а именно командой: sudo php app/console doctrine:generate:entities QR1000

Теперь непосредственно к тому, как тут всё работает.
Для сущности Customer мы добавляем свойство infoArray, которое будет содержать массив элементов CustomerInfo. В его аннотации описан тип связи, условие и источник данных:
@ORM\OneToMany(targetEntity="CustomerInfo", mappedBy="customer", cascade={"all"}, orphanRemoval=true)

Тут:
targetEntity — название класса содержащего n-элементов.
mappedBy — название класса содержащего 1-элемент, обратите внимание, название начинается с маленькой буквы..
cascade — тип действия при удалении родительского элемента. В данном случае будут автоматически удалены все дочерние.

В конструкторе для Customer добавляем строку:
$this->infoArray = new ArrayCollection();
для инициализации массива хранящего найденные описания.

Для сущности CustomerInfo связь с родительской сущностью описывается уже двумя строчками в аннотации:
@ORM\ManyToOne(targetEntity="Customer", inversedBy="infoArray")
  @ORM\JoinColumn(name="customer_id", referencedColumnName="id")

Тут:
targetEntity — название класса родительской сущности (уже с большой буквы).
inversedBy — название свойства родительского класса, в котором хранятся найденные экземпляры описаний.
name — имя поля описания, по которому находятся связанные объекты.
referencedColumnName — имя поля в родительской сущности, по которому находятся связанные объекты.

Вот собственно и всё, теперь, получив из ORM экземпляр сущности Customer:

$customer = $em -> getRepository( "QR1000MainBundle:Customer" )
                    -> findOneById( $customerID );

и передав его в шаблон:

$this -> render( "QR1000MainBundle::customer.html.twig", array( "customer" => $customer ) );

мы можем обращаться к его массиву infoArray с использованием такого синтаксиса:

{% for descr in customer.infoArray %}


А в РНР коде, это тоже свойство для Customer:
    $descriptions = $customer -> getInfoArray();
    foreach ( $descriptions as $descr ) {
        ...
    }
   

Это конечно не полное руководство, но как обещано, описал только неочевидные моменты.
Надеюсь кому-нибудь пригодится.



Успешных проектов!

Павел Осипов
2012.09.23



  Наверх