Poznajemy kilka rzeczy, takich jak dissociate, delete, onDelete – cascade i set null. Są to pewne puste obszary, które musimy poznać, aby mieć pełnię rozeznania w sytuacji.

Ok, a zatem tak:

  • Person jest w relacji hasOne do Address
  • Address jest w relacji belongsTo do Person

W moim przypadku mam adres o ID 4 przypisany do jakiejś osoby, oraz osobę o ID 10 bez żadnego adresu. Spróbujemy ukraść adres o ID 4 osobie, do której jest przypisany i dopisać go do osoby o ID 10.

Tak to robimy:

Artisan::command('dissociate-try', function () {

    $addr = Address::find(4);
    $new_person = Person::find(10);

    $addr->person()->dissociate();
    $addr->person()->associate($new_person)->save();
    
});

Po wykonaniu komendy powinniśmy zauważyć, że adres o nazwie 4 wskazuje teraz (w swoim person_id) na osobę o id 10. Dissociate go rozłączyło z poprzednią osobą, associate przypisało do osoby o ID 10.

Ok, inny przykład – mam adres o ID 23. Jest on do kogoś przypisany. Chcę ten adres usunąć, ktokolwiek go miał, teraz adresu mieć nie będzie.

Robimy to tak:

Artisan::command('delete-try', function () {

    $addr = Address::find(23);
    $addr->delete();
    
});

I już widzimy zmianę w bazie danych. Ok, inna rzecz – chcę usunąć osobę, która ma jakiś adres przypisany. W moim przypadku osoba o ID 53 istnieje w tabeli people zaś w tabeli addresses istnieje adres z person_id ustawionym na 53.

Czy uda się usunąć tę osobę? Zobaczmy:

 Artisan::command('delete-hasone', function () {
    $person = Person::find(53);
    $person->delete();   
 });

Jeżeli wykonywaliśmy ten projekt razem ze mną to nasza migracja wygląda tak:

public function up(): void
    {
        Schema::create('addresses', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->unsignedBigInteger('person_id');
            $table->string('city');
            $table->string('street');
            $table->foreign('person_id')->references('id')->on('people');
        });
    }

I nie możemy usunąć osoby posiadającej adres (to jest osoby mającej pk id w tabeli people, któremu odpowiada fk person_id w tabeli addresses) bez usunięcia wcześniej adresu.

Robimy to tak:

Artisan::command('delete-hasone', function () {

    $person = Person::with('address')->find(53);
    $person->address()->delete();
    $person->delete();
    
});

Natomiast gdybyśmy chcieli mieć możliwość usuwania modeli, które mają coś hasOne to musimy to w migracji określić, bo jak patrzyliśmy na error, to mieliśmy foreign key constraint, który dał fail.

Oto prosty przykład:

$table->foreign('term_id')
      ->references('term_id')->on('terms')
      ->onDelete('cascade');

To oznacza, że po usunięciu usuwanie ma lecieć „kaskadowo” czyli „CSSowo” 🙂 Nie, żartuję, to oznacza, że leci jak kaskada, usuwa model-rodzic i powiązane z nim dzieci, też uproszczenie.

Można też zrobić, aby przy usuwaniu rodzica dziecko miało foreign key ustawiony na null. Przykład:

$table->...->onDelete('set null');

Musimy tylko pamiętać, aby foreignKey był nullable:

$table->integer('foreign_id')->unsigned()->nullable();

Cóż, dużo z tym zachodu, ale nikt nie obiecywał, że programowanie będzie łatwe jak hello worldy w Pythonie.

Obiecywano, że nie będziemy pisać kodu maszynowego ani przesuwać bitów, chyba że z własnej chęci poznania programowania lepiej