Vazby v Eloquentu: Polymorfismus many to many

V minulém díle jsme si vysvětlili jak polymorfimus vlastně vypadá a jak ho lze využít. V tomto posledním díle seriálu o relacích budeme opět pokračovat polymorfismem, avšak tentokrát ve vazbě n:m, čili many to many. Opět si vše ukážeme na příkladě.

Podobně jako ve vazbě 1:n je i tato relace o něco složitější pro vysvětlení. Mějme například příspěvky, videa a štítky, které potřebujeme mezi sebou propojit, jelikož každý příspěvek může mít několik štítků a naopak, stejně tak je to u videa. Pokud bychom toto potřebovali vyřešit bez polymorfismu, pak bychom museli pro každou vazbu m:n vytvořit i třetí tabulku, která by vlastně sloužila jako průchozí, pivot, tabulka. Najednou tak máte dohromady pět tabulek, z toho dvě pivotní slouží pouze jako prostředník a jsou prakticky totožné, jen odkazují na jiné klíče. Díky polymorfismu můžeme počet zredukovat na čtyři, kdy namísto pivotních tabulek použijeme pouze jednu.

Schema::create('videos', function (Blueprint $table) {
    $table->increments('id'); 
    $table->string("name"); 
    $table->timestamps();
});
Schema::create('tags', function (Blueprint $table) {
    $table->increments('id');
    $table->string("name");
    $table->timestamps();
});

Tyto tři migrace nám vytvoří jednoduché tabulky postsvideos a tags. Jako další si vytvoříme polymorfickou tabulku, která bude sloužit pro všechny jako pivotní. Název se většinou odvozuje od schopnosti, tedy anglicky "able". V našem případě se jedná o štítky, takže tabulku pojmenujeme jako taggables:

Schema::create('taggables', function (Blueprint $table) {
    $table->integer("tag_id");
    $table->morphs('taggable'); // prida sloupce unsigned integer taggable_id a string taggable_type
);

use Illuminate\Database\Eloquent\Model;

class Post extends Model { /** * Pro ziskani vsech stitku prispevku */ public function tags() { return $this->morphToMany(Tag::class, 'taggable'); } }


```php
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Video extends Model
{
    /**
     * Pro ziskani vsech stitku pro video
     */
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Tag extends Model
{
    /**
     * Pro ziskani vsech prispevku ktere jsou prirazeny k danemu stitku
     */
    public function posts()
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }
 
    /**
     * Pro ziskani vsech videi ktere jsou prirazeny k danemu stitku
     */
    public function videos()
    {
        return $this->morphedByMany(Video::class, 'taggable');
    }
}

$tag = new Tag; $tag->name = "someTag";

$post->tags()->save($tag);


```php
// pro ulozeni stitku pro dane video
$video = Video::find(1);	
 
$tag = new Tag;
$tag->name = "someTag";
 
$video->tags()->save($tag);

Více štítků můžeme uložit i naráz:

$post = Post::find(1);	
 
$tag1 = new Tag;
$tag1->name = "someTag";
 
$tag2 = new Tag;
$tag2->name = "anotherTag";
 
$post->tags()->saveMany([$tag1, $tag2]);

Stejně tak je můžeme i přidat pomocí metody attach(), kdy nekontrolujeme, jestli daná vazba již existuje:

$video = Video::find(1);	
 
$tag1 = Tag::find(3);
$tag2 = Tag::find(4);
 
$video->tags()->attach([$tag1->id, $tag2->id]);

Nebo můžeme využít metody sync() pro smazání všech vazeb modelu a sesynchronizovat ho znovu s novými záznamy:

$post = Post::find(1);	
 
$tag1 = Tag::find(3);
$tag2 = Tag::find(4);
 
$post->tags()->sync([$tag1->id, $tag2->id]);

$tags = $post->tags;


```php
$tag = Tag::find(1);	
 
$videos = $tag->videos;

Není vám něco jasné, mám někde chybu nebo chcete přidat svůj názor na článek? Napište do komentářů pod článkem.