diff --git a/src/Illuminate/Database/Eloquent/Attributes/Boot.php b/src/Illuminate/Database/Eloquent/Attributes/Boot.php new file mode 100644 index 000000000000..f57da7af9450 --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Attributes/Boot.php @@ -0,0 +1,11 @@ + 'boot'.class_basename($trait), $uses); + $conventionalInitMethods = array_map(static fn ($trait) => 'initialize'.class_basename($trait), $uses); - $booted[] = $method; - } + foreach ((new ReflectionClass($class))->getMethods() as $method) { + if (! in_array($method->getName(), $booted) && + $method->isStatic() && + (in_array($method->getName(), $conventionalBootMethods) || + $method->getAttributes(Boot::class) !== [])) { + $method->invoke(null); - if (method_exists($class, $method = 'initialize'.class_basename($trait))) { - static::$traitInitializers[$class][] = $method; + $booted[] = $method->getName(); + } - static::$traitInitializers[$class] = array_unique( - static::$traitInitializers[$class] - ); + if (in_array($method->getName(), $conventionalInitMethods) || + $method->getAttributes(Initialize::class) !== []) { + static::$traitInitializers[$class][] = $method->getName(); } } + + static::$traitInitializers[$class] = array_unique(static::$traitInitializers[$class]); } /** diff --git a/tests/Integration/Queue/ModelSerializationTest.php b/tests/Integration/Queue/ModelSerializationTest.php index e13cbb4ec293..5fc6557740c8 100644 --- a/tests/Integration/Queue/ModelSerializationTest.php +++ b/tests/Integration/Queue/ModelSerializationTest.php @@ -2,6 +2,8 @@ namespace Illuminate\Tests\Integration\Queue; +use Illuminate\Database\Eloquent\Attributes\Boot; +use Illuminate\Database\Eloquent\Attributes\Initialize; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Pivot; @@ -210,16 +212,26 @@ public function testItCanRunModelBootsAndTraitInitializations() $model = new ModelBootTestWithTraitInitialization(); $this->assertTrue($model->fooBar); + $this->assertTrue($model->initializedViaAttributeInClass); + $this->assertTrue($model->initializedViaAttributeInTrait); $this->assertTrue($model::hasGlobalScope('foo_bar')); + $this->assertTrue($model::hasGlobalScope('booted_attr_in_class')); + $this->assertTrue($model::hasGlobalScope('booted_attr_in_trait')); $model::clearBootedModels(); $this->assertFalse($model::hasGlobalScope('foo_bar')); + $this->assertFalse($model::hasGlobalScope('booted_attr_in_class')); + $this->assertFalse($model::hasGlobalScope('booted_attr_in_trait')); $unSerializedModel = unserialize(serialize($model)); $this->assertFalse($unSerializedModel->fooBar); + $this->assertFalse($unSerializedModel->initializedViaAttributeInClass); + $this->assertFalse($unSerializedModel->initializedViaAttributeInTrait); $this->assertTrue($model::hasGlobalScope('foo_bar')); + $this->assertTrue($model::hasGlobalScope('booted_attr_in_class')); + $this->assertTrue($model::hasGlobalScope('booted_attr_in_trait')); } /** @@ -402,6 +414,8 @@ public function test_serialization_types_empty_custom_eloquent_collection() trait TraitBootsAndInitializersTest { + public bool $initializedViaAttributeInTrait = false; + public $fooBar = false; public function initializeTraitBootsAndInitializersTest() @@ -414,11 +428,39 @@ public static function bootTraitBootsAndInitializersTest() static::addGlobalScope('foo_bar', function () { }); } + + #[Boot] + public static function nonConventionalBootFunctionInTrait() + { + static::addGlobalScope('booted_attr_in_trait', function () {}); + } + + #[Initialize] + public function nonConventionalInitFunctionInTrait() + { + $this->initializedViaAttributeInTrait = ! $this->initializedViaAttributeInTrait; + } } class ModelBootTestWithTraitInitialization extends Model { use TraitBootsAndInitializersTest; + + public static bool $bootedViaAttributeInClass = false; + + public bool $initializedViaAttributeInClass = false; + + #[Boot] + public static function nonConventionalBootFunctionInClass() + { + static::addGlobalScope('booted_attr_in_class', function () {}); + } + + #[Initialize] + public function nonConventionalInitFunctionInClass() + { + $this->initializedViaAttributeInClass = ! $this->initializedViaAttributeInClass; + } } class ModelSerializationTestUser extends Model