1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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対象を表す識別子として任意の文字列を指定することもできる)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?