-
Notifications
You must be signed in to change notification settings - Fork 11.7k
[10.x] Introduce Observe attribute for models #49843
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
Conversation
|
Not saying I'm totally sold on the feature, but I wonder if the attribute should be on the observer itself? |
|
I'd prefer auto discovery for observers, just like the current auto discovery for policies. |
|
@taylorotwell the point of having it on the model is to show right away that there are side effects for this model. |
|
I agree with @emargareten. Many times I've encountered unexpected behavior and spent a considerable amount of time debugging, only to realize that there is an observer on the class. Placing them at the top of the model will immediately clarify where to look when something unexpected happens. |
|
Updated to |
|
Is there the possibility to improve upon this feature? I know it was merged 2 months ago, but the solution is somewhat hacky, and obfuscates some of the functionality behind magic, and arguably invalid code. The primary issue is that the code doesn't really use the attribute itself, instead it uses the /**
* Create a new attribute instance.
*
* @param array|string $classes
* @return void
*/
public function __construct(array|string $classes)
{
}In reality, this should be: public readonly array $classes;
/**
* Create a new attribute instance.
*
* @param string ...$classes
* @return void
*/
public function __construct(string ...$classes)
{
$this->classes = $classes;
}Then the handling code: return collect($reflectionClass->getAttributes(ObservedBy::class))
->map(fn ($attribute) => $attribute->getArguments())
->flatten()
->all();Would be: return collect($reflectionClass->getAttributes(ObservedBy::class))
->map(fn ($attribute) => $attribute->newInstance()?->classes)
->flatten()
->all();I appreciate that this may be nitpicking, but introducing unused constructor parameters that are only there so that they can be pulled out with reflection is a messy solution, and introducing code like this can, as a whole, bring down the overall standard of the frameworks code. The same for the typing used on the constructor parameter, which actually introduces two different syntaxes for using this attribute. #[OrderedBy(MyObserver::class)]
class SomeModel extends Model {}and #[OrderedBy([MyObserver::class, MyOtherObserver::class])]
class SomeModel extends Model {}When you also consider that the attribute is repeatable, you end up with a third way. #[OrderedBy(MyObserver::class), OrderedBy(MyOtherObserver::class)]
class SomeModel extends Model {}Which makes the following code valid within Laravel. #[OrderedBy(MyObserver::class)]
#[OrderedBy([MyOtherObserver::class, YetAnotherObserver::class]), OrderedBy(MoreObservers::class)]
class SomeModel extends Model {}In reality, the only syntax that should be accepted here, is: #[OrderedBy(MyObserver::class, MyOtherObserver::class, YetAnotherObserver::class)]
class SomeModel extends Model {}Since attributes are not inherited, and the attribute itself does nothing and is used for nothing but a marker that contains a bit of data, there's no need for it to be repeatable. It should also be noted that the documentation makes no reference to the fact that it's repeatable, or that there's a second syntax for providing multiple observers in one. Ideally, the method public static function resolveObserveAttributes()
{
$reflectionClass = new ReflectionClass(static::class);
$attribute = $reflectionClass->getAttributes(ObservedBy::class)[0] ?? null;
return $attribute?->newInstance()->classes;
}I'm more than happy to PR these changes myself if necessary. |
This pull request introduces a new attribute class for registering model observers.
Example:
This change means you don't have to register the observers in a service provider. It just makes the code clearer and less confusing, making it easier to understand what's going on.