お久しぶりです、今日は眠いので大量にブログを投稿しようかと思います。
本日は、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検索結果画面にパンくずリストを表示する方法 (リッチスニペット対応)



