Blok 3 · rozdział 8

Modele, relacje, seeder

Model to klasa PHP, która odpowiada tabeli. Zamiast sklejać SQL w kontrolerze, piszesz Szczyt::where(...). Seeder wstawia 28 szczytów z pliku kursowego, więc cała grupa ma te same dane.

Po tym rozdziale powinieneś umieć

  • utworzyć modele Pasmo i Szczyt oraz ustawić $fillable i $table;
  • opisać hasMany i belongsTo;
  • załadować listę Korony seederem;
  • sprawdzić liczbę rekordów w Tinkerze i w phpMyAdmin.

1. Model Eloquent

php artisan make:model Pasmo
php artisan make:model Szczyt

app/Models/Pasmo.php:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Pasmo extends Model
{
    protected $table = 'pasma';

    protected $fillable = ['nazwa', 'slug'];

    public function szczyty(): HasMany
    {
        return $this->hasMany(Szczyt::class);
    }
}

app/Models/Szczyt.php:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Szczyt extends Model
{
    protected $table = 'szczyty';

    protected $fillable = ['pasmo_id', 'nazwa', 'slug', 'wysokosc_m', 'opis'];

    public function pasmo(): BelongsTo
    {
        return $this->belongsTo(Pasmo::class);
    }
}
Eloquent Mapowanie obiektowo-relacyjne w Laravelu (ORM). Wiersz tabeli jest obiektem. Szczyt::find(1) czyta rekord, $szczyt->nazwa czyta kolumnę.
$table i $fillable Laravel od klasy Szczyt zgadłby tabelę szczyts. Dlatego nazwa jest jawna. $fillable to lista kolumn, które wolno ustawić hurtowo (create, update). Bez niej masowe przypisanie jest blokowane — to ochrona przed tym, że ktoś dopisze do formularza pole id albo cudze uprawnienie.

2. Relacja 1:N

Pasmo hasMany Szczyt

$pasmo->szczyty — kolekcja szczytów tego pasma.

1 : N

Szczyt belongsTo Pasmo

$szczyt->pasmo — jedno pasmo. W bazie stoi pasmo_id.

Na liście Korony każde pasmo ma dokładnie jeden szczyt, ale w schemacie związek jest 1:N. Metoda hasMany mówi, że pasmo może mieć wiele szczytów. Nie jest to jedna kolumna w tabeli pasm.

3. Seeder Korony

  1. Utwórz katalog database/seeders/data.
  2. Skopiuj tam plik kursu materialy/korona.php (zostaw nazwę korona.php).
  3. Wygeneruj seeder: php artisan make:seeder KoronaSeeder.
namespace Database\Seeders;

use App\Models\Pasmo;
use App\Models\Szczyt;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;

class KoronaSeeder extends Seeder
{
    public function run(): void
    {
        $wiersze = require database_path('seeders/data/korona.php');

        foreach ($wiersze as $wiersz) {
            $pasmo = Pasmo::query()->firstOrCreate(
                ['slug' => Str::slug($wiersz['pasmo'])],
                ['nazwa' => $wiersz['pasmo']],
            );

            Szczyt::query()->updateOrCreate(
                ['slug' => Str::slug($wiersz['nazwa'])],
                [
                    'pasmo_id' => $pasmo->id,
                    'nazwa' => $wiersz['nazwa'],
                    'wysokosc_m' => $wiersz['wysokosc'],
                ],
            );
        }
    }
}

W database/seeders/DatabaseSeeder.php w metodzie run zostaw wywołanie:

$this->call(KoronaSeeder::class);
php artisan db:seed

Str::slug('Śnieżka') daje sniezka, Str::slug('Góry Świętokrzyskie') daje gory-swietokrzyskie. firstOrCreate nie dubluje pasma przy drugim uruchomieniu. updateOrCreate poprawia wysokość, jeśli odpalisz seeder jeszcze raz.

Czysty start Gdy namieszasz w danych i chcesz je wgrać od zera (na zajęciach, nie na produkcji z prawdziwymi kontami): php artisan migrate:fresh --seed. To kasuje wszystkie tabele tej bazy i stawia je ponownie. Nie rób tego później na bazie, w której są już konta kolegów, jeśli ich potrzebujesz.

4. Tinker i phpMyAdmin

php artisan tinker
Szczyt::count();
Szczyt::query()->orderByDesc('wysokosc_m')->first()->nazwa;
Pasmo::count();

Wyjście z Tinkera: exit. W phpMyAdmin tabela szczyty ma mieć 28 wierszy, a pasma tyle, ile różnych pasm w pliku (też 28 — po jednym szczycie na pasmo).

ORM a SQL Szczyt::count() kończy się zapytaniem SELECT COUNT(*). Eloquent nie zastępuje bazy. Układa zapytanie za Ciebie i oddaje obiekty. Gdy coś nie gra, w phpMyAdmin nadal możesz odpalić SELECT i porównać wynik.
Commit

Gdy Szczyt::count() zwraca 28:

git add .
git status
git commit -m "Modele i seeder Korony"

Przed commitem git status nie pokazuje .env.