Poznajemy tą naprawdę prostą relację w Laravelu. Korzystamy z już istniejącego projektu. Do dzieła.
Ok, tworzymy model Profile, razem z migracją, fabryką, seederem. Migracja:
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->unsignedBigInteger('person_id')->nullable(true);
$table->string('profile_name')->unique(true);
$table->text('description')->nullable(true);
$table->foreign('person_id')
->references('id')
->on('people')
->onDelete('cascade')
->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('profiles');
}
};
Person już mamy. Ok, migrujemy, tworzymy model Contact, -mfs. Migracja:
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->unsignedBigInteger('profile_id')->nullable(true);
$table->string('email')->unique(true);
$table->string('webpage')->nullable(true);
$table->foreign('profile_id')
->references('id')
->on('profiles')
->onDelete('cascade')
->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contacts');
}
};
Ok, migrujemy, tworzymy naszą piramidkę. W person:
public function profile(){
return $this->hasOne(Profile::class);
}
Pamiętajmy o importach, teraz w Profile:
class Profile extends Model
{
use HasFactory;
protected $fillable = ['profile_name', 'description'];
public function contact(){
return $this->hasOne(Contact::class);
}
public function person(){
return $this->belongsTo(Person::class);
}
}
Fillable też trzeba wypełniać, wiemy dlaczego. Ok, teraz Contact:
class Contact extends Model
{
use HasFactory;
protected $fillable = ['email', 'webpage'];
public function profile(){
return $this->belongsTo(Profile::class);
}
}
Ok, spróbujemy zrobić testowego gościa z profilem i kontaktem zapisanym. Komenda:
Artisan::command('test1234', function () {
$person = new Person();
$person->firstName = "Blabla";
$person->lastName = "Bla bla bla";
$person->age = 23;
$person->save();
$profile = new Profile();
$profile->profile_name = "Blabla profile";
$profile->description = "bla bla bla";
$person->profile()->save($profile);
$contact = new Contact();
$contact->email = "blabla@blabla.com";
$contact->webpage = "www.blabla.com";
$profile->contact()->save($contact);
$this->comment("done");
});
Pamiętamy o importach. Ok, rzućmy okiem na bazę danych czy wszystko działa jak należy.
Jest ok? To teraz czas na relację hasOneThrough (w Person):
public function profileContact()
{
return $this->hasOneThrough(Contact::class, Profile::class);
}
I teraz cała magia:
Artisan::command('person-profileContact {id}', function (int $id) {
$p = Person::with('profileContact')->findOrFail($id);
$this->comment($p->full_name);
$this->comment($p->profileContact->email);
});
Czyli Person ma Profil, profil ma Contact, ale Person ma Contact przez Profil. Tak ta układanka działa. Podobnie z relacjami jeden do wielu.
My raczej będziemy pamiętać manyToManyThrough i dzikie SQLe (group concat i wiele innych!), które pisaliśmy, aby dostać się do tagów bez powtórzeń, które występują pod recenzjami danego filmu, zaś recenzje i tagi spina nie klucz obcy, ale pivot table review_tag.
Cóż, tu jest łatwiej. Jeżeli has one of many też pamiętamy, to dobrze, coraz więcej umiemy.