お久しぶりです、今日は眠いので大量にブログを投稿しようかと思います。
本日は、Table間のJoinを自動化してくれる、というかきちんと設定しないとまったくDAOの意味をなさないリレーションの設定に関してです。
詳しい中身はこちらの英語ドキュメントを参照しましょう。
http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html
具体例として以下の2つのエンティティを考えてみましょう
TblTicket.php
<?php namespace Hoge\FugaBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; /** * * @ORM\Table(name="tbl_ticket") * @ORM\Entity(repositoryClass="Hoge\FugaBundle\Repository\TicketRepository") */ class TblTicket { /** * @var bigint $id * * @ORM\Column(name="id", type="bigint", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ protected $id; /** * @ORM\ManyToOne(targetEntity="TblTicket", inversedBy="childs") * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true) */ private $parent; /** * @ORM\OneToMany(targetEntity="TblTicket", mappedBy="parent") */ private $childs; /** * @ORM\OneToOne(targetEntity="TblTicketData", mappedBy="ticket", cascade={"persist"}) */ private $data; public function __construct(){ $this->childs = new ArrayCollection(); } /** * Get id * * @return bigint */ public function getId() { return $this->id; } /** * get parent * @return TblTicket */ public function getParent () { return $this->parent; } /** * setparent * @param TblTicket $parent * @return TblTicket */ public function setParent ($parent) { $this->parent = $parent; return $this; } /** * get data * @return TblTicketData */ public function getData () { return $this->data; } /** * set data * @param TblTicketData $parentData * @return TblTicketData */ public function setParentData ($parentData) { $this->parentData = $parentData; return $this; } /** * Add childs * * @param TblTicket $childs */ public function addChild(TblTicket $childs) { $this->childs[] = $childs; } /** * Get childs * * @return Doctrine\Common\Collections\Collection */ public function getChilds() { return $this->childs; } }
TblTicketData.php
<?php namespace Hoge\FugaBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; /** * TblTicketData * * @ORM\Table(name="tbl_ticket_data") * @ORM\Entity */ class TblTicketData { /** * @var bigint $id * * @ORM\Column(name="id", type="bigint", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @ORM\OneToOne(targetEntity="TblTicket", inversedBy="data") * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false) */ private $ticket; /** * @var string $content * * @ORM\Column(name="content", type="string", length=256, nullable=true) */ private $content; /** * Get id * * @return bigint */ public function getId() { return $this->id; } /** * get parent * @return TblTicket */ public function getTicket () { return $this->ticket; } /** * setparent * @param TblTicket $parent * @return TblTicket */ public function setTicket ($ticket) { $this->ticket = $ticket; return $this; } /** * get content * @return string */ public function getContent () { return $this->content; } /** * set content * @param string $content * @return TblTicket */ public function setContent ($content) { $this->content = $content; return $this; } }
さて、まずは中身の説明に移る前に、このエンティティから生成されるテーブル定義を見てみましょう。
CREATE TABLE tbl_ticket ( id bigint(20) NOT NULL AUTO_INCREMENT, parent_id bigint(20) DEFAULT NULL, PRIMARY KEY (id), KEY IDX_1A06C41C727ACA70 (parent_id), ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE tbl_ticket_data ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, ticket_id bigint(20) unsigned NOT NULL, content varchar(255) DEFAULT NULL, UNIQUE KEY id (id), UNIQUE KEY ticket_id (ticket_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ここから、判断できるポイントを列挙していきましょう。
- tbl_ticketは木構造となっており、親チケットを自身のテーブルに持っている
- tbl_ticketは一番親のチケットを判断するために、parent_idにNullを許可している
- tbl_ticketはそのチケットに付随するデータticket_dataを1つ持っている
それでは、エンティティの中身を見ていきましょう、
@OneToOne
TblTicket.php と TblTicketData.phpにはそれぞれ@OneToOneアノテーションがありますが中身が少々違うようです。
TblTicket.php
@ORM\OneToOne(targetEntity="TblTicketData", mappedBy="ticket", cascade={"persist"})
TblTicketData.php
@ORM\OneToOne(targetEntity="TblTicket", inversedBy="data") @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false)
ポイントは
- 所属する側のデータ(TblTicketData)にはinversedByとJoinColumnを付与する
- 所属される側のデータ(TblTicket)にはmappedByを付与する
- JoinColumnによってTblTicketDataのparent_idとTblTicketのid間のリレーションが作られる
- nullable=falseによって、すべてのデータは必ずTblTicketへの所属が保障される
です。
では、続いて、このデータのInsertに関して考えてみましょう。
パターン1
public function createAction() { $ticket = new TblTicket(); $data = new TblTicketData(); $data->setContent("aaaa"); $data->setTicket($ticket); $em = $this->getDoctrine()->getEntityManager(); $em->persist($ticket); $em->persist($data); $em->flush(); return new Response('Created product'); }
$dataには、ticket_idが必要なので、TblTicketオブジェクトをセットしないとErrorとなってしまいます。
ただし今回は、cascade={"persist"}をTblTicketに付与しているため、以下のコードによってもInsert可能です。
パターン2
public function createAction() { $ticket = new TblTicket(); $data = new TblTicketData(); $data->setContent("aaaa"); $data->setTicket($ticket); $ticket->setData($data); $em = $this->getDoctrine()->getEntityManager(); $em->persist($ticket); $em->flush(); return new Response('Created product'); }
これは、persistの際にエンティティマネージャーが管理していないオブジェクトに関してもまとめてpersistするという設定によるものです。
注意
public function errorAction() { $em = $this->getDoctrine()->getEntityManager(); $repo = $em->getRepository("HogeFugaBundle:TblTicket"); $ticket = $repo->find(1); // エラー出るかも echo $ticket->getData()->getContent(); return new Response('get content'); }
mappedByのOneToOneでは、対象エンティティのデータの存在を保証できない(逆の場合は保証される)ので、DataがNullの場合がありえます
OneToManyもやりたかったのですが、量が多くなってしまったのでそれは次回に回します。
投稿者プロフィール
-
中の人には主に、
PHP・Symfony2系の人と
Ruby・Rails系の人がいます。
ときどきJavascript・データベースにも手を出すかもしれません。
最新の投稿
- データベース2015年2月3日Symfony2 Doctrine2の小ネタ(OneToMany,ManyToOneリレーション)
- データベース2015年1月28日Symfony2 Doctrine2の小ネタ(OneToOneリレーション)
- 開発2015年1月21日Symfony2でもデザインパターン(PHPクラス編)
- 開発2014年11月26日Google検索結果画面にパンくずリストを表示する方法 (リッチスニペット対応)