Ok, kolejna lekcja, tym razem wykonujemy soft deletion i przywracanie ręcznie – SQLem „z palca” oraz przez fasadę DB. Do dzieła.
Przypomnijmy sobie te komendy, które napisaliśmy:
Artisan::command('note-is-trashed {id}', function (int $id) {
$note = Note::withTrashed()->findOrFail($id);
$this->comment($note->trashed() ? "true" : "false");
});
Artisan::command('note-delete {id}', function (int $id) {
$note = Note::withoutTrashed()->findOrFail($id);
$note->delete();
$this->comment($note->trashed() ? "true" : "false");
});
Artisan::command('note-force-delete {id}', function (int $id) {
$note = Note::withTrashed()->findOrFail($id);
$note->forceDelete();
$this->comment("Deleted");
});
Artisan::command('note-restore {id}', function (int $id) {
$note = Note::onlyTrashed()->findOrFail($id);
$note->restore();
$this->comment($note->trashed() ? "true" : "false");
});
Ok, za chwilę wykonamy SQL update query, ale najpierw disclaimer. Proszę popatrzeć na przykłady poniżej:
UPDATE Customers
SET ContactName = 'Alfred Schmidt', City= 'Frankfurt'
WHERE CustomerID = 1;
Taki SQL update query ma klauzulę WHERE, a co za tym idzie robi update na konkretnym rekordzie/rekordach. Jednak zobaczmy bez where:
UPDATE Customers
SET ContactName = 'Alfred Schmidt', City= 'Frankfurt';
Taki update query nie jest nieprawidłowy. Mało tego – robi update na wszystkich rekordach. Zawsze pisząc update query trzeba uważać, bo ryzykujemy coś więcej niż tylko wyskoczenie błędu, który mówi, że czegoś zapomnieliśmy.
Ok, teraz spróbujemy usunąć sobie jakiś rekord ręcznie:
UPDATE `notes`
SET deleted_at = current_timestamp
WHERE id = 1;
Ok, teraz sprawdzamy czy jest usunięty komendą:
php artisan note-is-trashed 1
Ta komenda została napisana przez nas, przypominam. Ok, teraz przywróćmy i jeszcze raz sprawdźmy:
php artisan note-restore 1;
php artisan note-is-trashed 1
Działa. Ok, już wiemy jak się zabrać do ręcznego usuwania rekordów. Napiszmy komendę z fasadą DB:
Artisan::command('note-delete-db {id}', function (int $id) {
$affected = DB::table("notes")
->where("id", $id)
->update(["deleted_at" => \Carbon\Carbon::now()]);
$this->comment("updated - deleted");
$this->comment($affected);
});
Jak widać fasada DB jest bardzo podobna do ręcznego SQL, ale powinniśmy pamiętać z poprzednich lekcji pewne jej ograniczenia, które obchodziliśmy przez DB::raw oraz ->toArray() na kolekcji.
Teraz możemy usunąć komendą rekord i sprawdzić, jak komponuje się z komendami używającymi Eloquenta:
php artisan note-is-trashed 1;
php artisan note-delete-db 1;
php artisan note-is-trashed 1;
php artisan note-restore 1;
php artisan note-is-trashed 1
Ok, przywracanie za pomocą bazy danych też jest proste, w sumie łatwiejsze. Napiszmy to z palca:
UPDATE `notes`
SET deleted_at = current_timestamp
WHERE id = 1;
Teraz mamy usunięte, sprawdzamy:
php artisan note-is-trashed 1;
Teraz z palca przywracamy:
UPDATE `notes`
SET deleted_at = NULL
WHERE id = 1;
I jeszcze raz możemy sobie sprawdzić. Ok, jak to napisać z fasadą DB?
Jeszcze łatwiej:
Artisan::command('note-restore-db {id}', function (int $id) {
$affected = DB::table("notes")
->where("id", $id)
->update(["deleted_at" => null]);
$this->comment("updated - restored");
$this->comment($affected);
});
Możemy sprawdzić, działa. Kilka rzeczy do zapamiętania:
- softDeletes sprawia, że mamy kolumnę deleted_at, nullable timestamp
- jeżeli deleted_at to null to model nie jest usunięty
- jeżeli deleted_at ma jakiś timestamp, to jest usunięty + mamy datę usunięcia
- w czystym SQL ustawiamy datę przez SET deleted_at = current_timestamp
- w Laravelu jak chcemy ręcznie ustawić timestamp to używamy \Carbon\Carbon::now() (albo innej metody)
- ustawienie timestampu dla deleted_at oznacza, że jest usunięty
- ustawienie deleted_at na null oznacza, że przywróciliśmy model
- w PHP/Laravelu null piszemy małymi literami
- w SQL konwencja jest taka, że NULL piszemy wielkimi
- inne timestampy (created at, updated at) każdy model zakłada, że są ustawione
- jeżeli migracja nie ma timestamps, to w modelu trzeba określić public $timestamps = false;
- jeżeli robimy update/create za pomocą query buildera a nie Eloquenta, to created_at/updated_at nie będą ustawiane automatycznie
- jeżeli model ma relację belongsTo i jest modyfikowany, to w rodzicu nie zmienia się updated_at
- jeżeli model w relacji belongsTo ma zmieniać na swój update updated_at rodzica trzeba ustawić mu protected $touches = [’nazwa_modelu_rodzica’];
- aby model mógł być soft deleted nie wystarczy dodać softDeletes w migracji (dla timestamps wystarczy migracja), trzeba jeszcze wykorzystać trait use SoftDeletes;
- fabryki działają tak, że zazwyczaj podajemy tylko pola fillable, a inne pola, jak created_at, updated_at i deleted_at są tworzone automatycznie
- mimo wszystko do fabryk możemy podawać pola takie jak te wyżej aby np. seedować bazę danych już usuniętymi rekordami albo rekordami utworzonymi 2 lata temu i modyfikowanymi pół roku temu