Skip to content

[12.x] Add Boot and Initialize Model attributes #56518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 3, 2025

Conversation

inxilpro
Copy link
Contributor

@inxilpro inxilpro commented Aug 1, 2025

Right now, if you want to hook into Eloquent's boot or initialize phases, you need to use magically named methods. For example:

trait Locatable {
  public static function bootLocatable() { ... }
  public function initializeLocatable() { ... }
}

If you ever rename the trait, you need to remember to also rename the methods. And if you misspell the method name, it can be hard to debug.

This introduces two new traits that add the same behavior:

use Illuminate\Database\Eloquent\Attributes\Boot;
use Illuminate\Database\Eloquent\Attributes\Initialize;

trait Locatable {
  #[Boot]
  public static function namedWhatever() { ... }

  #[Initialize]
  public function alsoNamedWhatever() { ... }
}

A couple additional "features" that we get for free with this approach:

  • You can now add boot and initialize hooks in the model directly if you want to
  • You can add multiple hooks in the same trait if you need to for some reason

@shaedrich
Copy link
Contributor

This is similar to local scopes: Until now you had to follow the naming convention—if not, Laravel wouldn't and couldn't tell you. So, this definitely is one annoyance less—well done 👍🏻

Is there a reason why there is a Boot attribute but not a Booted one?

Comment on lines 369 to 381

foreach ((new ReflectionClass($class))->getMethods() as $method) {
if (
! in_array($method->getName(), $booted) &&
if (! in_array($method->getName(), $booted) &&
$method->isStatic() &&
(
in_array($method->getName(), $conventionalBootMethods) ||
$method->getAttributes(Boot::class) !== []
)
) {
(in_array($method->getName(), $conventionalBootMethods) ||
$method->getAttributes(Boot::class) !== [])) {
$method->invoke(null);

$booted[] = $method->getName();
}

if (
in_array($method->getName(), $conventionalInitMethods) ||
$method->getAttributes(Initialize::class) !== []
) {
if (in_array($method->getName(), $conventionalInitMethods) ||
$method->getAttributes(Initialize::class) !== []) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make the ifs a whole lot more readable if we moved logic out of there:

Suggested change
foreach ((new ReflectionClass($class))->getMethods() as $method) {
if (
! in_array($method->getName(), $booted) &&
if (! in_array($method->getName(), $booted) &&
$method->isStatic() &&
(
in_array($method->getName(), $conventionalBootMethods) ||
$method->getAttributes(Boot::class) !== []
)
) {
(in_array($method->getName(), $conventionalBootMethods) ||
$method->getAttributes(Boot::class) !== [])) {
$method->invoke(null);
$booted[] = $method->getName();
}
if (
in_array($method->getName(), $conventionalInitMethods) ||
$method->getAttributes(Initialize::class) !== []
) {
if (in_array($method->getName(), $conventionalInitMethods) ||
$method->getAttributes(Initialize::class) !== []) {
$isBootedMethod = in_array($method->getName(), $booted);
$isConventionalBootMethod = in_array($method->getName(), $conventionalBootMethods);
$hasBootAttributes = $method->getAttributes(Boot::class) !== [];
$isConventionalInitMethod = in_array($method->getName(), $conventionalInitMethods);
$hasInitMethods = $method->getAttributes(Initialize::class) !== [];
foreach ((new ReflectionClass($class))->getMethods() as $method) {
if (!$isBootedMethod &&
$method->isStatic() &&
($isConventionalBootMethod || $hasBootAttributes)) {
$method->invoke(null);
$booted[] = $method->getName();
}
if ($isConventionalInitMethod || $hasInitMethods) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #56524

@taylorotwell taylorotwell merged commit 3aaba97 into laravel:12.x Aug 3, 2025
52 of 60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants