day18(から3日遅れた)今日はDoctrineとEloquentそれぞれに用意された1つのテーブルに複数の種類のデータをもたせる方法を見ていきます。
Doctrine(SingleTableInheritance)
Entity/Reaction.php
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'reactions')]
#[ORM\Entity]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap([
'like' => Like::class,
'sad' => Sad::class,
])]
abstract class Reaction
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\ManyToOne(targetEntity: Book::class, inversedBy: 'reactions')]
#[ORM\JoinColumn(onDelete: 'cascade')]
private ?Book $book;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): void
{
$this->id = $id;
}
public function getBook(): ?Book
{
return $this->book;
}
public function setBook(?Book $book): void
{
$this->book = $book;
}
abstract public function getType(): string;
}
Entity/Like.php
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Like extends Reaction
{
public function getType(): string
{
return 'like';
}
}
Entity/Sad.php
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Sad extends Reaction
{
public function getType(): string
{
return 'sad';
}
}
<?php
declare(strict_types=1);
use App\Entity\Author;
use App\Entity\Book;
use App\Entity\Like;
use App\Entity\Sad;
use Doctrine\ORM\EntityManagerInterface;
require __DIR__.'/../vendor/autoload.php';
/** @var EntityManagerInterface $entityManager */
$entityManager = require __DIR__.'/bootstrap.php';
$author = new Author();
$author->setName('tanakahisateru');
$book = new Book();
$book->setTitle('ちょうぜつソフトウェア設計入門');
$book->setAuthor($author);
$book->setPrice(3080);
$book->setDescription('可愛い表紙で釣ってオブジェクト指向をガッツリ解説する本');
$book->addReaction(new Like());
$book->addReaction(new Sad());
$book->addReaction(new Like());
$entityManager->persist($author);
$entityManager->persist($book);
$entityManager->flush();
$entityManager->refresh($book);
echo $book->getReactions()->count().PHP_EOL; // 3件
echo $book->getReactions()[0]->getType().PHP_EOL; // Likeのインスタンス
echo $book->getReactions()[1]->getType().PHP_EOL; // Sadのインスタンス
- SingleTableInheritanceパターン( https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html )が実装されています。
- Bookエンティティに対する2種類のリアクション(Like, Sad)をDB上は1つのreactionsテーブルに保存することができます。
- 呼び出し時はtype=likeのreactionsレコードは
Like
エンティティのインスタンスとして、type=sadのreactionsレコードはSad
エンティティのインスタンスとして取り出されます。
Eloquent(PolymorphicRelationship)
Models/Book.php
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* App\Models\Book
*
* @property int $id
* @property string $title
* @property int $price
* @property Author $author
* @property string|null $description
*/
class Book extends Model
{
public $fillable = [
'title',
'price',
'author_id',
'description',
];
public $timestamps = false;
public function author()
{
return $this->belongsTo(Author::class);
}
public function likes()
{
return $this->morphMany(Like::class, 'likable');
}
}
Models/Author.php
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
/**
* App\Models\Author
*
* @property int $id
* @property string $name
* @property Collection<Book> $books
*/
class Author extends Model
{
public $fillable = [
'name',
];
public function books()
{
return $this->hasMany(Book::class);
}
public function likes()
{
return $this->morphMany(Like::class, 'likable');
}
}
Models/Like.php
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Like extends Model
{
public $timestamps = false;
public function likable()
{
return $this->morphTo();
}
}
<?php
declare(strict_types=1);
use App\Models\Author;
use App\Models\Book;
use App\Models\Like;
require __DIR__.'/../vendor/autoload.php';
require __DIR__.'/bootstrap.php';
/** @var Book $book1 */
$book1 = Book::find(11);
$like1 = new Like();
$book1->likes()->save($like1);
/** @var Author $author1 */
$author1 = Author::find(1);
$like2 = new Like();
$author1->likes()->save($like2);
$book1->refresh();
echo count($book1->likes).PHP_EOL; // like1件
$author1->refresh();
echo count($author1->likes).PHP_EOL; // like1件
- EloquentにはSingleTableInheritanceは実装されていません。SingleTableInheritanceがやりたい場合はサードパーティのプラグインを使用する必要があります。代わりに、ポリモーフィックリレーションが実装されています。
- Bookモデルに対するLikeとAuthorモデルに対するLikeをDB上で1つの
likes
テーブルに保存することができます。 - likable_type, likeable_idというカラムを持ち、likable_typeにはLike対象のクラス名が保存されます(Like対象を表す識別子として任意の文字列を指定することもできる)