Skip to content

feat: add dynamic weight #506

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 4 commits into from
Jun 16, 2025
Merged

Conversation

Bloemendaal
Copy link
Contributor

Hi! We have the following situation in our code:

class OfferOccupationProjector extends Projector
{
    public int $weight = 4;

    public function onContractCartItemAdded(ContractCartItemAddedEvent $event): void
    {
        (new OfferOccupation)
            ->writeable()
            ->insert([
                'id'                    => Str::uuid()->toString(),
                'contract_cart_item_id' => $event->cartItemId,
                'offer_id'              => $event->offerId,
                'start_date'            => $event->startDate,
                'end_date'              => $event->endDate,
            ]);
    }
}

This projector is listening to the ContractCartItemAddedEvent, because once that happens, an offer occupation should be made. The weight of this projector is set to 4, because otherwise this projector would run before the CartProjector, causing this code to throw an exception because the foreign key constraint on contract_cart_item_id fails.

I found that the previous people that worked on this project removed the OfferOccupation in the CartProjector, which is weird, why not do this in the OfferOccupationProjector. Well, it turns out that because of the weight, the removal function below would run after the removal of the cart, meaning the database fails again because of the restrict on the foreign key. This won't work:

class OfferOccupationProjector extends Projector
{
    public int $weight = 4;

    public function onContractCartItemRemoved(ContractCartItemRemovedEvent $event): void
    {
        OfferOccupation::query()
            ->where('contract_cart_item_id', '=', $event->cartItemId)
            ->delete();
    }
}

So I know there's probably a better way to implement this with events, but the issue is that I cannot really change this anymore, so I figured: why not make the weight property a function with the event in it. Now, everyone with this same situation can base the order of the projectors (and reactors) on the StoredEvent (and null when it is resetting the state).

The change to the getWeight is completely optional, the weight property still works.

In my case, this would be the implementation:

class OfferOccupationProjector extends Projector
{
    public function getWeight(?StoredEvent $event): int
    {
         return match ($event?->event_class) {
              ContractCartItemAddedEvent::class => 4,
              ContractCartItemRemovedEvent::class => -4,
              default => 0,
         };
    }
}

Verified

This commit was signed with the committer’s verified signature.
taisph Tais P. Hansen
@freekmurze
Copy link
Member

freekmurze commented Jun 16, 2025

Thanks for your work on this! We'd like to merge this in, put could you add tests and docs first?

@Bloemendaal
Copy link
Contributor Author

@freekmurze Thanks for the feedback! I updated the existing tests to also include a dynamic variant, but decided to keep the weight property on the projectors the same to maintain backwards compatibility. I also added a new example to the documentation. :)

@freekmurze freekmurze merged commit ac92007 into spatie:main Jun 16, 2025
9 checks passed
@freekmurze
Copy link
Member

Thanks!

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.

None yet

4 participants