V této sérii článků se budu věnovat databázovým vazbám v Laravelu, respektive v Eloquentu. Jako první se zaměříme na dvě nejjednodušší vazby, 1:1 (one to one) a 1:n (one to many). Vše si vysvětlíme na konkrétním případě s definicí vazby v modelu, s migracemi, vytahování dat a jejich ukládání.

Vazba 1:1

Jak už jsem psal, vazba "jedna ku jedné" patří mezi ty jednodušší. Přeložit si to můžeme tak, že například jedno auto má (vždy a pouze) jednoho vlastníka. Vztah funguje i obráceně, tedy vlastník má (vždy a pouze) jedno auto. Podobných příkladů najdete v praxi mnoho, například uživatel a profil, manžel a manželka, ředitel a škola apod. V našem případě mějme příklad osoba (model Person ) a pas (model Passport ).

Migrace

Prvně si vytvoříme migraci:

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

Schema::create('passports', function (Blueprint $table) { $table->increments('id'); $table->date('valid_date'); $table->integer('person_id')->unsigned()->index()->nullable(); $table->foreign('person_id')->references('id')->on('persons'); });

Jako první si vytvoříme tabulku persons, kde primárním klíčem je sloupce id . Dále si vytvoříme tabulku passports , která také obsahuje sloupec id , ale co je nejdůležitější na této migraci je definice sloupce person_id a vytvoření cizího klíče, který odkazuje na sloupec id v tabulce persons . Tato migrace však pouze vytvoří vazbu v databázi, ale Laravel s ní stále nedokáže nijak pracovat ani ji využívat. Toho docílíme definováním požadované vazby v modelech.

Eloquent modely

Při definování vazby vždy záleží na konkrétní situaci. Při vazbě 1:1 nemusí jeden model vždy mít záznam v druhé tabulce (ale když už má, tak musí mít pouze jeden, has one), ale obráceně záznam ve druhé tabulce musí vždy patřit k jednomu záznamu v první tabulce (belongs to). Na základě toho pak můžeme definovat naše vazby v modelech:

class Person
{
    public function passport()
    {
       return $this->hasOne(Passport::class);
    }
}

class Passport { public function person() { return $this->belongsTo(Person::class); } }

Jako první parametr je název modelu, pro který chceme použít vazbu. Eloquent určuje cizí klíč na základě názvu modelu. V tomto případě se předpokládá, že model Passport má automaticky cizí klíč person_id . Pokud by jste chtěli přepsat tuto konvenci, pak je potřeba ho vložit do metody hasOne jako druhý parametr:


return $this->hasOne(Passport::class, 'cizi_klic');

Dále Eloquent předpokládá, že cizí klíč odkazuje na sloupec id (nebo na vlastní $primaryKey ) v rodičovské tabulce. Jinak řečeno se Eloquent podívá na id  osoby, na nějž person_id  odkazuje v tabulce Passport . Pokud bychom chtěli pro vazbu použít jiný sloupec než id , pak ho můžeme specifikovat jako třetí argument:


return $this->hasOne(Passport::class, 'cizi_klic', 'lokalni_klic');

Získávání záznamu

Díky vazbám které jsme si vytvořili pak můžeme velmi jednoduše vytahovat a zpracovávat data z jednotlivých tabulek:

$passport = Person::find(1)->passport;

// a obracene treba s definovanim konkretni vazby // napr. jmeno vlastnika pasu pak vytahneme $passport->person->name; $passport = Passport::with('person')->find(10);

Uložení záznamu

Abychom vytvořili záznam ve vztahu Person  a Passport (definice hasOne ), stačí nám pouze přes rodiče zavolat vazbu a pomocí metody save() uložit:

$person->passport()->save($passport);

Pokud ukládáme (nebo editujeme) model, který jsme definovali jako belongsTo , pak můžeme použít metodu associate() . Tato metoda nastaví cizí klíč na model dítěte (ve vztahu rodič-dítě):


$passport->person()->associate($person)->save();

Vazba 1:n

Pravděpodobně nejčastěji používané vazby jsou 1:n, neboli "jedna ku mnoha". Použijeme například autor a článek, tedy článek musí mít vždy jedno autora a autor může mít nekonečně mnoho (n) článků.

Migrace

Migrace bude mít stejné principy jako vztah 1:1. Je tedy potřeba definovat dvě tabulky (authors  a posts ) a u jedné (posts ) vytvořit cizí klíč (author_id ), který bude odkazovat na sloupec (id ) v rodičovské tabulce (author ).

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

Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->integer('author_id')->unsigned()->index()->nullable(); $table->foreign('author_id')->references('id')->on('authors'); });

Eloquent vazby


class Author
{
    public function posts()
    {
       return $this->hasMany(Post::class);
    }
}

class Post { public function author() { return $this->belongsTo(Author::class); } }

Pro definici vazby 1:n jsme použili metodu hasMany  na rodičovskou tabulku. Opět zde funguje stejná konvence, tedy že se předpokládá, že primární klíč v rodičovské tabulce bude sloupec id  a cizí klíč bude vycházet z názvu modelu, tedy author_id . Pokud bychom chtěli konvenci změnit, pak je potřeba přidat druhý (vlastní cizí klíč), případně třetí parametr (vlastní primární klíč).

V případě, kdy vazba může mít více záznamů, je běžnou praxí psát název vazby v množném čísle, v tomto případě posts . Naopak pokud má záznam vždy pouze jednoho rodiče, pak se název vazby píše v jednotném čísle - author .

Získávání záznamu

Záznamy lze získat úplně stejně, jako ve vazbě 1:1. Je jen potřeba počítat s tím, že se nám výsledek může vrátit jako kolekce, respektive u jednoho autora můžeme získat více článků.

// ziskame autorovy clanky
$author->posts;

// ziskame autora clanku $post->author;

Uložení záznamu


// vytvoreni vazby mezi autorem a vice clanky
$author->posts()->saveMany([
   $post1, 
   $post2,
]);

// nebo pouzijeme metodu save() pro ulozeni jednoho modelu $author->posts()->save($post);

// vytvoreni vazby mezi clankem a autorem $post->author()->associate($author)->save();

V dalším díle se více podíváme na vazbu n:n (many to many) a také na Eloquent specifickou vazbu "has many through".

Není vám něco jasné nebo mám někde chybu? Napište do komentářů pod článkem.