• Время чтения ~4 мин
  • 25.08.2022

При работе с моделями Eloquent обычно используются события, отправляемые в течение жизненного цикла моделей. Есть несколько различных способов сделать это, и в этом руководстве я рассмотрю их и объясню преимущества и недостатки каждого из них.

Я буду использовать один и тот же пример для каждого подхода, чтобы вы могли увидеть прямое сравнение.В этом примере свойство UUID модели будет присвоено UUID во время создания самой модели.

Наш первый подход использует метод статической загрузки модели для регистрации поведения. Это позволяет нам работать непосредственно с моделью и регистрировать обратный вызов, который мы хотим запустить при создании модели.

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(),
		);
	}
}

Этот подход идеально подходит для небольших и простых реакций на события модели, таких как добавление UUID, поскольку его довольно легко понять, и вы можете точно видеть, что происходит в модели. Самая большая проблема с этим подходом — повторение кода, и если у вас есть несколько моделей, которым нужно назначать UUID, вы будете делать одно и то же неоднократно.

Это прекрасно подводит нас ко второму подходу, использующему трейт. В Laravel ваши модели могут наследовать трейты и автоматически их загружать, если вы создадите для своего трейта метод, который начинается с boot и заканчивается именем трейта. Вот пример:

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(),
		);
	}
}

Использование трейта позволяет добавить это поведение к каждой модели, которая требует этого и легко реализуема.Мой самый существенный недостаток заключается в том, что объединение этих поведений может вызвать проблемы, когда несколько признаков хотят подключиться к одному и тому же событию модели. Они начинают бороться за приоритет и могут довольно быстро запутаться.

Это подводит нас к следующему варианту, Model Observers.Model Observers — это основанный на классах подход к реагированию на события модели, где методы соответствуют конкретным запускаемым событиям.

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();
	}
}

Этот класс нужно будет где-то зарегистрировать, в поставщике услуг или в самой модели (вот где я рекомендую это).Регистрация этого наблюдателя в модели обеспечивает видимость на уровне модели побочных эффектов, которые изменяют красноречивое поведение. Проблема с сокрытием этого в поставщике услуг заключается в том, что, если все не знают, что это там, об этом трудно узнать. Самым большим недостатком этого подхода является его наглядность. На мой взгляд, этот подход просто фантастический при правильном использовании.

Еще один способ решить эту проблему — воспользоваться свойством $dispatchesEvents в самой модели Eloquent. Это свойство каждой модели Eloquent, которое позволяет вам перечислить события, которые вы хотите прослушивать, и класс, вызываемый для этих событий.

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,
	];
}

The SetModelUuid< /код>будет создан в течение жизненного цикла модели Eloquent, и это ваш шанс добавить в модель поведение и свойства.

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();
	}
}

Этот подход является одним из самых чистых и простых для понимания. , так как в модели достаточно видимости, и вы можете легко использовать этот класс для разных моделей. Самая большая проблема, с которой вы столкнетесь, — это если вам нужно инициировать несколько действий для события модели.

В заключение, честно говоря, нет правильного способа сделать это. Вы можете выбрать любой из вышеперечисленных методов, и они будут работать, но вам следует выбрать тот, который подходит именно вам и вашему конкретному варианту использования. Я хотел бы видеть больше вариантов для этой конкретной функции.

Например, наблюдатель — хороший вариант, если вам нужно добавить в модель несколько свойств для событий модели. .Однако лучший ли это вариант? Как насчет того, чтобы использовать свойство событий отправки для запуска пользовательского конвейера для этой модели?

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,
			]);
	}
}

Как видите, мы можем начать использовать конвейеры для добавления нескольких вариантов поведения в модельные события. Теперь это не тестировалось, поэтому я не знаю на 100%, будет ли это работать, но в качестве концепции это может открыть компонуемый подход к реагированию на события модели.

Как вы обрабатываете модельные события в своих проектах Laravel? Дайте нам знать в Твиттере!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

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...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297