Cały czas ćwiczymy migracje i relacje. Tabele, które utworzymy będą nam potrzebne do następnych ćwiczeń, dobrze też wypracować sobie pewną rutynę w tworzeniu ich.

Ok, najpierw komenda:

php artisan make:model Movie -mfs

Teraz migracja:

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('movies', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('title')->unique();
            $table->string('director');
            $table->integer('length')->unsigned();
            $table->dateTime('release_date');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('movies');
    }
};

Mam nadzieję, że nadążamy. Teraz migrujemy i tworzymy tak samo model o nazwie Review:

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('reviews', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('author');
            $table->text('content');
            $table->tinyInteger('rating')->unsigned();
            $table->foreignId('movie_id')->constrained(
                table: 'movies', indexName: 'reviews_movie_id'
            )->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('reviews');
    }
};

Później omówimy sobie bardzo dokładnie, co tu się dzieje, na razie mam nadzieję, że nadążamy. Ok, migrujemy.

Teraz model Movie, fillable i relacja:

use App\Models\Review;
class Movie extends Model
{
    use HasFactory;

    protected $fillable = ["title", "director", "length", "release_date"];

    public function reviews(){
        return $this->hasMany(Review::class);
    }
}

Druga strona relacji w modelu Review plus fillable:

use App\Models\Movie;

class Review extends Model
{
    use HasFactory;
    protected $fillable = ["author", "content", "rating"];

    public function movie(){
        return $this->belongsTo(Movie::class);
}
}

Wielokrotnie to omawialiśmy, pewne szczegóły jeszcze sobie wyjaśnimy. Powinno to być bardzo zrozumiałe i oczywiste. Teraz Factory dla Movie:

class MovieFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'title' => fake()->unique()->sentence(3),
            'director' => fake()->name(),
            'release_date' => fake()->dateTimeBetween('-10 years'),
            'length' => fake()->numberBetween(60, 150),

        ];
    }
}

Oraz Factory dla Review:

class ReviewFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'author' => fake()->name(),
            'content' => fake()->paragraph(10),
            'rating' => fake()->numberBetween(1,10)
        ];
    }
}

I relacyjny seeder dla Movie:

use App\Models\Movie;
use App\Models\Review;
class MovieSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Movie::factory()
        ->count(15)
        ->create()
        ->each(function($film){

            $revs = Review::factory()
            ->count(random_int(1,5))
            ->make();

            $film->reviews()->saveMany($revs);
        });
    }
}

Jedyna rzecz, której mamy prawo nie pamiętać to komenda odpalająca seedera, ja to zawsze zapominam:

php artisan db:seed --class=MovieSeeder

Ok, z rzeczy jeszcze nieomawianych mamy:

  • indeks unique
  • nowy zapis foreignFor
  • tinyInteger zamiast integera
  • własne pole dateTime

Mimo wszystko mamy wszelkie podstawy SQLowe, aby szybko przyswoić sobie, co tu się dzieje plus temat będę rozwijać w następnych lekcjach.