Add Laravel 13 ObservedBy observer migration rectors#528
Conversation
|
Things may have been updated since the last time I dealt with this, but I have run into situations where converting to the class File extends Model
{
#[Override]
protected static function booted(): void
{
// cant' use ObserveredBy attribute here because
// it doesn't work with inherited classes
static::observe(FileObserver::class);
}
}
class UserAvatar extends File {} // please ensure that the hooks defined in FileObserver are actually called when dealing with UserAvatar instances |
This rule should not be concerned about this cleanup---there's other rector dead-code rules that perform this and we should let them clean it up |
| /** | ||
| * @see RemoveModelObserveCallsFromBootRectorTest | ||
| */ | ||
| final class RemoveModelObserveCallsFromBootRector extends AbstractRector |
There was a problem hiding this comment.
Having two separate, but dependent rules like this is a bad architecture---we should only have one rector rule that removes the call from boot/booted and adds the ObservedBy attribute, this will have to operate at the class level
What if someone skipped the other rule but forgot to skip this? Now their codebase is broken
Summary
Adds Laravel 13 support for migrating manual model observer registration such as:
User::observe(UserObserver::class);into model attributes:
What changed
ObserveCallsToObservedByAttributeRectorto add or merge#[ObservedBy([...])]on Eloquent modelsRemoveModelObserveCallsFromBootRectorto remove safe direct top-levelobserve()calls fromboot()andbooted()User::observe(...),self::observe(...), andstatic::observe(...)boot()/booted()methods after cleanup, including cases with method attributes such as#[Override]ObservedBy(...)shapes conservativelydocs/rector_rules_overview.mdPrecision / safety rules
This change is intentionally conservative:
observe()statements are removedObservedBy(...)attributes are merged and deduplicated when they are in a supported formImplementation notes
ObservedByand Eloquent model observationTiny confession: rule started a bit vibe-coded, but ended up heavily constrained and aggressively tested. Cave paintings, but with guard rails.
Tests
Added targeted PHPUnit coverage for:
ObservedByboot()methods entirelybooted()methods entirelystatic::observe(...)inside modelbooted()ObservedBy(...)forms untouchedAlso ran the full local CI-equivalent verification on the repo's original tool configuration:
composer validatecomposer lintcomposer rector-dry-runcomposer phpstancomposer structarmedvendor/bin/phpunit