๐Ÿ”ฅ Fire events on attribute changes of your Eloquent model

Overview

Laravel Attribute Events

Build Status Last Updated Latest Stable Version License

class Order extends Model
{
    protected $dispatchesEvents = [
        'status:shipped' => OrderShipped::class,
        'note:*' => OrderNoteChanged::class,
    ];
}

Eloquent models fire several handy events throughout their lifecycle, like created and deleted. However, there are usually many more business meaningful events that happen during a model's life. With this library you can capture those, by mapping attribute changes to your own event classes.

Installation

composer require jpkleemans/attribute-events

How to use it

Use the Kleemans\AttributeEvents trait in your model and add the attributes to the $dispatchesEvents property:

class Order extends Model
{
    use AttributeEvents;

    protected $dispatchesEvents = [
        'created'         => OrderPlaced::class,
        'status:canceled' => OrderCanceled::class,
        'note:*'          => OrderNoteChanged::class,
    ];
}

The attribute events will be dispatched after the updated model is saved. Each event receives the instance of the model through its constructor.

For more info on model events and the $dispatchesEvents property, visit the Laravel Docs

Listening

Now you can subscribe to the events via the EventServiceProvider $listen array, or manually with Closure based listeners:

Event::listen(function (OrderCanceled $event) {
    // Restock inventory
});

Or push realtime updates to your users, using Laravel's broadcasting feature:

Echo.channel('orders')
    .listen('OrderShipped', (event) => {
        // Display a notification
    })

JSON attributes

For attributes stored as JSON, you can use the -> operator:

protected $dispatchesEvents = [
    'payment->status:completed' => PaymentCompleted::class,
];

Accessors

For more complex state changes, you can use attributes defined by an accessor:

class Product extends Model
{
    use AttributeEvents;

    protected $dispatchesEvents = [
        'low_stock:true' => ProductReachedLowStock::class,
    ];

    public function getLowStockAttribute(): bool
    {
        return $this->stock <= 3;
    }
}

For more info on accessors, visit the Laravel Docs

Sponsors

Nexxtmove Logo

Thanks to Nexxtmove for sponsoring the development of this project.

License

Code released under the MIT License.

Comments
  • Doesn't work with attributes cast

    Doesn't work with attributes cast

    When casting attributes with protected $casts = [] the changes are not picked up and the relative events are not fired, for example I am using spatie/laravel-model-states to keep the status of a request with the following $casts:

    protected $casts = [
         'status' => RequestStatus::class,
    ];
    

    And the following $dispatchesEvents:

    protected $dispatchesEvents = [
          'created' => RequestCreated::class,
          'status:confirmed' => RequestConfirmed::class,
          'status:repaired' => RequestCompletedIntervention::class,
    ];
    

    In this case the events are not fired because the Trait AttributeEvents compares the values like this on line 64: $value === $expected, but because of the cast my value is not a string but an instance of a RequestStatus. Comparing the values with $value == $expected works because the RequestStatus can be cast to a string. I tried adding $this->castAttribute($attribute, $expected) but I keep getting the current value of the attribute and not the $expected value I want to cast, I don't know if it's how laravel is supposed to work. For now I ended up doing $value = $this->getAttributes()[$attribute]; on line 32 and I get the expected resault

    opened by denisgoryaynov 4
  • improved phpunit configurations and added github actions for php cs fixer

    improved phpunit configurations and added github actions for php cs fixer

    • renamed test folder to tests
    • added phpunit configuration file
    • added phpcs cache and test cache files to gitignore
    • fixed paths to make tests pass again
    • added github action for the PHP CS FIXER
    opened by bhushan 4
  • Update AttributeEvents.php

    Update AttributeEvents.php

    Add support for PHP 8.1 Enums by converting the value to a string if the attribute is casted to a backed enum.

    Related: https://github.com/jpkleemans/attribute-events/issues/11

    opened by jeffreyvanhees 2
  • Package doesn't support PHP Enums

    Package doesn't support PHP Enums

    I see that the package supports castables however, it doesn't support enums.

    Here's example model:

    <?php
    
    namespace App\Models;
    
    use App\Enums\OrderFinancialStatus;
    use App\Events\Orders\OrderCreated;
    use App\Events\Orders\OrderPaid;
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use Kleemans\AttributeEvents;
    
    class Order extends Model
    {
        use HasFactory,
            AttributeEvents;
    
        protected $dispatchesEvents = [
            'created' => OrderCreated::class,
            'status:financial_status' => OrderPaid::class,
        ];
    
        protected $casts = [
            'financial_status' => OrderFinancialStatus::class,
        ];
    }
    

    When financial_status is changed to paid, the package fails with the following error:

    1) Tests\Feature\Models\OrderTest::test_it_dispatched_order_paid_event
    Error: Object of class App\Enums\OrderFinancialStatus could not be converted to string
    
    /app/vendor/jpkleemans/attribute-events/src/AttributeEvents.php:64
    /app/vendor/jpkleemans/attribute-events/src/AttributeEvents.php:15
    /app/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:421
    /app/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:249
    /app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/EventFake.php:262
    /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php:189
    /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1078
    /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:989
    /app/tests/Feature/Models/OrderTest.php:33
    

    Here's the test case I used:

        public function test_it_dispatched_order_paid_event()
        {
            Event::fake([
                OrderPaid::class,
            ]);
    
            $order = Order::withoutEvents(fn () => Order::factory()->createOne());
            $order->financial_status = OrderFinancialStatus::PAID;
            $order->save();
    
            Event::assertDispatched(fn (OrderPaid $event) => $event->order->id === $order->id);
        }
    

    I tried finding a workaround for this, though with no luck. I tried:

    • adding __toString() to enum - not possible
    • using a custom getter

    // Edit

    I'm attaching the missing enum:

    enum OrderFinancialStatus: string
    {
        case PAID = 'paid';
    }
    
    opened by radmen 2
  • JSON-columns

    JSON-columns

    Hi! Great package and well documented, thanks a lot!

    Question: Do you have an idea how to dispatch an event when a value in a json-column changes? Like:

    protected $dispatchesEvents = [
      'settings->color:*' => ColourChanged::class,
    ]
    

    The example above doesn't seems to work.

    Jeffrey

    opened by jeffreyvanhees 2
  • Multiple column checks for a single event

    Multiple column checks for a single event

    We want to fire an event when any one of several DB columns are updated on our User model. This event reaches out to an external API and updates other columns on the User model. The problem we're running into is that the event is being fired multiple times if multiple attribute events are a hit. Here is what our definition looks like:

    protected $dispatchesEvents = [
            'birthdate:*'         => UserBirthInfoChanged::class,
            'birthtime:*'         => UserBirthInfoChanged::class,
            'birthplace:*'    => UserBirthInfoChanged::class,
        ];
    

    Obviously we want to fire UserBirthInfoChanged when ANY of those columns are updated. But only once for any given update. I'm not sure if this is currently possible, but looking at the fireAttributeEvents method, it doesn't seem like it is.

    opened by techdaddies-kevin 1
  • Bump illuminate/database from 8.39.0 to 8.40.0

    Bump illuminate/database from 8.39.0 to 8.40.0

    Bumps illuminate/database from 8.39.0 to 8.40.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump illuminate/database from 8.22.0 to 8.22.1

    Bump illuminate/database from 8.22.0 to 8.22.1

    Bumps illuminate/database from 8.22.0 to 8.22.1.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • How to get the attribute value before changed

    How to get the attribute value before changed

    Hello I have an event fired when the image attribute was changed. I want to remove the old image from S3 bucket when a resource was updated.

    How is the best way to do that?

    opened by gotzsys 1
Releases(1.4.0)
Owner
Jan-Paul Kleemans
Jan-Paul Kleemans
Generate UUID for a Laravel Eloquent model attribute

Generate a UUIDv4 for the primary key or any other attribute on an Eloquent model.

Alex Bouma 4 Mar 1, 2022
Add eloquent model events fired after a transaction is committed or rolled back

Laravel Transactional Model Events Add transactional events to your eloquent models. Will automatically detect changes in your models within a transac

Mark van Duijker 62 Dec 22, 2022
PHP 8 attribute to register Laravel model observers.

PHP 8 attribute to register Laravel model observers. Instead of defining observers inside service providers this package offers an alternative way to

gpanos 15 May 30, 2022
Laravel-model-mapper - Map your model attributes to class properties with ease.

Laravel Model-Property Mapper This package provides functionality to map your model attributes to local class properties with the same names. The pack

Michael Rubel 15 Oct 29, 2022
Watch your Laravel app for unwanted changes when working with third-party packages.

Project Secure This package installs a Composer plugin that reports unwanted changes to your Laravel project code after installing or updating a third

The Laravel Hacker 3 Nov 3, 2021
Is an Extension of Laravel View Class which compiles String Template on the fly. It automatically detects changes on your string template and recompiles it if needed.

Laravel-fly-view Is an Extension of Laravel View Class which compiles String Template on the fly. It automatically detects changes on your string temp

John Turingan 16 Jul 17, 2022
Cast your Eloquent model attributes to Value Objects with ease.

Laravel Value Objects Cast your Eloquent model attributes to value objects with ease! Requirements This package requires PHP >= 5.4. Using the latest

Red Crystal Code 23 Dec 30, 2022
Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Rubik 4 May 12, 2022
The idea of this package is using PHP Attribute in a laravel project

Laravel #[Annotation] Introduction PHP 8.0 release was a revolution for the language. It brings cool features like Named arguments, Attributes, Constr

null 13 Apr 29, 2022
A laravel package to handle sanitize process of model data to create/update model records.

Laravel Model UUID A simple package to sanitize model data to create/update table records. Installation Require the package using composer: composer r

null 66 Sep 19, 2022
A laravel package to generate model hashid based on model id column.

Laravel Model Hashid A package to generate model hash id from the model auto increment id for laravel models Installation Require the package using co

Touhidur Rahman 13 Jan 20, 2022
A package to filter laravel model based on query params or retrieved model collection

Laravel Filterable A package to filter laravel model based on query params or retrived model collection. Installation Require/Install the package usin

Touhidur Rahman 17 Jan 20, 2022
Automatic multi-tenancy for Laravel. No code changes needed.

Tenancy for Laravel โ€” stancl/tenancy Automatic multi-tenancy for your Laravel app. You won't have to change a thing in your application's code. โœ”๏ธ No

Samuel ล tancl 2.7k Jan 3, 2023
Turn any Eloquent model into a list!

Listify Turn any Eloquent model into a list! Description Listify provides the capabilities for sorting and reordering a number of objects in a list. T

Travis Vignon 138 Nov 28, 2022
Collection of the Laravel/Eloquent Model classes that allows you to get data directly from a Magento 2 database.

Laragento LAravel MAgento Micro services Magento 2 has legacy code based on abandoned Zend Framework 1 with really ugly ORM on top of outdated Zend_DB

Egor Shitikov 87 Nov 26, 2022
Laravel Quran is static Eloquent model for Quran.

Laravel Quran ุจูุณู’ู…ู ูฑู„ู„ู‘ูฐู‡ู ุงู„ุฑูŽู‘ุญู’ู…ูฐู†ู ุงู„ุฑูŽู‘ุญููŠู’ู…ู Laravel Quran is static Eloquent model for Quran. The Quran has never changed and never will, bec

Devtical 13 Aug 17, 2022
A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache.

Laravel OTP Introduction A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache. The ca

Lim Teck Wei 52 Sep 6, 2022
This package provides a trait that will generate a unique uuid when saving any Eloquent model.

Generate slugs when saving Eloquent models This package provides a trait that will generate a unique uuid when saving any Eloquent model. $model = new

Abdul Kudus 2 Oct 14, 2021
Eloquent model-caching made easy.

Model Caching for Laravel Supporting This Package This is an MIT-licensed open source project with its ongoing development made possible by the suppor

GeneaLabs, LLC 2k Dec 26, 2022