• Czas czytania ~4 min
  • 25.08.2022

Podczas pracy z modelami elokwentnymi często sięga się do zdarzeń wywoływanych w cyklu życia modeli. Można to zrobić na kilka różnych sposobów, a w tym samouczku omówię je i wyjaśnię zalety i wady każdego z nich.

Będę używał ten sam przykład dla każdego podejścia, aby można było zobaczyć bezpośrednie porównanie.Ten przykład przypisze właściwość UUID modelu do UUID podczas tworzenia samego modelu.

Nasze pierwsze podejście wykorzystuje metodę statycznego rozruchu modelu do zarejestrowania zachowania. To pozwala nam pracować bezpośrednio na modelu i zarejestrować wywołanie zwrotne, które chcemy uruchomić podczas tworzenia modelu.

declare(strict_types=1);
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
 
class Office extends Model
{
	public static function boot(): void
	{
		static::creating(fn (Model $model) =>
			$model->uuid = Str::uuid(),
		);
	}
}

To podejście doskonale sprawdza się w przypadku małych i prostych reakcji na zdarzenia modelu, takie jak dodanie UUID, ponieważ jest dość łatwe do zrozumienia i można dokładnie zobaczyć, co się dzieje w modelu. Największym problemem związanym z tym podejściem jest powtarzanie kodu, a jeśli masz wiele modeli wymagających przypisania identyfikatorów UUID, będziesz robić to samo wielokrotnie.

To prowadzi nas ładnie do drugiego podejścia, wykorzystującego cechę. W Laravelu twoje modele mogą dziedziczyć cechy i automatycznie je uruchamiać, jeśli utworzysz metodę na swojej cesze, która zaczyna się od boot i kończy się nazwą cechy. Oto przykład:

declare(strict_types=1);
 
namespace App\Models\Concerns;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
 
trait HasUuid
{
	public static function bootHasUuid(): void
	{
		static::creating(fn (Model $model) =>
			$model->uuid = Str::uuid(),
		);
	}
}

Użycie cechy pozwala dodać to zachowanie do każdego modelu, który tego wymaga i jest łatwy do wdrożenia.Moją największą wadą jest to, że układanie tych zachowań może powodować problemy, gdy wiele cech chce wykorzystać to samo zdarzenie modelu. Zaczynają walczyć o pierwszeństwo i dość szybko mogą się bałaganić.

To prowadzi nas do następnej opcji, Model Observers.Model Observers to oparte na klasach podejście do reagowania na zdarzenia modelu, w którym metody odpowiadają konkretnym uruchamianym zdarzeniom.

declare(strict_types=1);
 
namespace App\Observers;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
 
class OfficeObserver
{
	public function creating(Model $model): void
	{
		$model->uuid = Str::uuid();
	}
}

Ta klasa będzie musiała być gdzieś zarejestrowana, u Usługodawcy lub samego Modelu (tu polecam).Zarejestrowanie tego obserwatora w modelu zapewnia widoczność na poziomie modelu skutków ubocznych, które zmieniają elokwentne zachowanie. Problem z ukrywaniem tego u Usługodawcy polega na tym, że o ile wszyscy nie wiedzą, że tam jest - trudno o tym wiedzieć. Największą wadą tego podejścia jest jego widoczność. Moim zdaniem takie podejście jest fantastyczne, gdy jest używane prawidłowo.

Jeszcze jednym sposobem rozwiązania tego problemu jest skorzystanie z właściwości $dispatchesEvents w samym modelu elokwentnym. Jest to właściwość każdego modelu elokwentnego, która umożliwia wypisanie zdarzeń, których chcesz nasłuchiwać, oraz klasy wywoływanej dla tych zdarzeń.

declare(strict_types=1);
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
 
class Office extends Model
{
	protected $dispatchesEvents = [
		'creating' => SetModelUuid::class,
	];
}

SetModelUuid< /kod>zostanie utworzona podczas cyklu życia modelu Eloquent i jest twoją szansą na dodanie zachowania i właściwości do modelu.

declare(strict_types=1);
 
namespace App\Models\Events;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
 
class SetModelUuid
{
	public function __construct(Model $model)
	{
		$model->uuid = Str::uuid();
	}
}

To podejście jest jednym z najczystszych i najłatwiejszych do zrozumienia , ponieważ model jest dobrze widoczny i można łatwo udostępniać tę klasę w różnych modelach. Największym problemem, z jakim się zmierzysz, jest konieczność wywołania wielu działań na zdarzeniu modelowym.

Podsumowując, szczerze mówiąc, nie ma na to właściwego sposobu. Możesz wybrać dowolną z powyższych metod i będą działać, ale powinieneś wybrać tę, która jest odpowiednia dla Ciebie i Twojego konkretnego przypadku użycia. Chciałbym zobaczyć więcej opcji dotyczących tej konkretnej funkcji.

Na przykład obserwator jest dobrym rozwiązaniem, jeśli chcesz dodać wiele właściwości do modelu na zdarzeniach modelu .Czy to jednak najlepsza opcja? Co powiesz na to, gdybyśmy użyli właściwości dispatch events do uruchomienia niestandardowego potoku dla tego modelu?

declare(strict_types=1);
 
namespace App\Models\Pipelines;
 
use App\Models\Office
 
class OfficeCreatingPipeline
{
	public function __construct(Office $model)
	{
		app(Pipeline::class)
			->send($model)
			->through([
				ApplyUuidProperty::class,
				TapCreatedBy::class,
			]);
	}
}

Jak widać, możemy zacząć używać potoków do dodawania wielu zachowań do modelowe wydarzenia. Nie jest to testowane, więc nie wiem w 100%, czy to zadziała - ale jako koncepcja może otworzyć komponowalne podejście do reagowania na zdarzenia modelowe.

Jak radzisz sobie ze zdarzeniami modeli w projektach Laravel? Daj nam znać na Twitterze!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297