randm's avatar

How to automaticly update updated_by, created_by fields using Eloquent?

Hi,

Every table in my database has the following columns "update_at", "created_at", and "removed_at". The cool feature of the Eloquent class will updates these fields automatically.

Additionally, I also have 3 columns, called "created_by", "updated_by", and "removed_by". I would like to update these fields automatically just like the ("update_at", "created_at", "removed_at".) Is there an easy way I can extend Eloquent to update these field with the value of USER_ID "global constant"?

Therefore, when I execute an update query, Eloquent should also update the column "updated_by" with the value of USER_ID. If I create a record then populate "created_by", finally if I do a softDelete then updated removed_by column.

How can I do this? Is there a way for me to do this for all model or will I have to update one model at a time?

0 likes
12 replies
randm's avatar

Thank you @QuentinBontemps. I am trying to apply your suggestion and create an observers model.

first, will I have to create an observer for every model?

second, is that what should I observer look like? How can I improve it?

<?php 

namespace App\Observers;

class AccountObserver {

    $userID;

    public function __construct($userID){
        $this->userID = $userID;
    }

    public function saving($model)
    {
        $model->modfied_by = $this->userID;
    }

    public function saved($model)
    {
        $model->modfied_by = $this->userID;
    }


    public function updating($model)
    {
        $model->modfied_by = $this->userID;
    }

    public function updated($model)
    {
        $model->modfied_by = $this->userID;
    }


    public function creating($model)
    {
        $model->created_by = $this->userID;
    }

    public function created($model)
    {
        $model->created_by = $this->userID;
    }


    public function removing($model)
    {
        $model->purged_by = $this->userID;
    }

    public function removed($model)
    {
        $model->purged_by = $this->userID;
    }
}
QuentinBontemps's avatar

You don't have to create an observer for each model, you can create a Model class, and extends your models to Model class.

Insert something like this your in Model class :

public static function boot()
 {
    $class = get_called_class();
    $class::observe(new Observer());
    
    parent::boot();
  }

1 like
randm's avatar

Hi @QuentinBontemps I am sorry for being so slow. I am very new to laravel/framework.

  1. From the console I executed this command "php artisan make: model Models\RecordFingerPrintModel"
  2. Then I edited the RecordFingerPrintModel.php file located in app\Models and put the following code inside of it
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use app\Observers\RecordFingerPrintObserver;

class RecordFingerPrintModel extends Model
{
    

    public static function boot()
    {
        $class = get_called_class();
        $class::observe(new RecordFingerPrintObserver(2));
    
        parent::boot();
    }
}
  1. I added an observer called "RecordFingerPrintObserver" in "app\Observers" and I put the following code inside of it
<?php 

<?php 

namespace App\Observers;

class RecordFingerPrintObserver {

    $userID;

    public function __construct($userID){
        $user = Auth::user();
        $this->userID = $user->getId();
    }

    public function saving($model)
    {
        $model->modfied_by = $this->userID;
    }

    public function saved($model)
    {
        $model->modfied_by = $this->userID;
    }


    public function updating($model)
    {
        $model->modfied_by = $this->userID;
    }

    public function updated($model)
    {
        $model->modfied_by = $this->userID;
    }


    public function creating($model)
    {
        $model->created_by = $this->userID;
    }

    public function created($model)
    {
        $model->created_by = $this->userID;
    }


    public function removing($model)
    {
        $model->purged_by = $this->userID;
    }

    public function removed($model)
    {
        $model->purged_by = $this->userID;
    }
}

Is this the correct implementation?

luddinus's avatar

Well, you could implement an "empty interface" to the desired model and then listen for eloquent events.

EventProvider

public function boot(DispatcherContract $events)
{
    parent::boot($events);

    $event->listen('eloquent.saving: *', function($model) {
        if ($model instanceof YourContractInterface)
        {
            $user_id = Auth::user()->id;

            $observer = new AccountObserver($user_id);

            // ...
        }
    });
}
mdshaf229's avatar

I have developed a simple package https://github.com/hmshafeeq/userstamps for this.

It handles the following two requirements

  1. Insert userstamps automatically
  2. Autoload the userstamp model along with the original model ( it will execute one query to get all created_by, updated_by, deleted_by, submitted_by etc fields instead of making separate calls). So in your model, you don't need to define the relation with user model multiple times i.e for created_by, updated_by,....

Install the package and use it as below.

use VelitSol\UserStamps\UserStampTrait;

class Example extends Model {

    use UserStampTrait;
     
    protected $userStamps = [
       
       /==============================================
       /  Auto load userstamp model for current model.
       /==============================================
      // If you don't want this pacakge to auto insert the userstamp, 
      // and you just want to  autoload it along with your model
      // This will be the case when userstamp is being set in controller 
      // or some where else at certain action.
       'updated_by',
     
      /=========================================================
      /  Auto insert userstamp & auto load it for current model.
      /  Auto insert depends on,
      /  1. Event ('creating','saving','updating','deleting')
      /  2. Field 
      /  3. Expression
      /=========================================================
          
       // This userstamp should be updated when an event is 
       // invoked i.e 'creating','updating','deleting','saving'.
       'created_by' => [
            'depends_on_event' => 'creating', 
       ],
       'deleted_by' => [
             'depends_on_event' => 'deleting', 
       ],
       
       // This userstamp should be set if "is_archived" is dirty (has some changes in value)
       'archived_by' => [
            'depends_on_field' => 'is_archived' 
       ],
       
       // This userstamp should be set if "updating" event is invoked on this model,
       // and "is_submitted" is dirty (has some changes in value)
       'submitted_by'=> [
            'depends_on_event' => 'updating', 
            'depends_on_field' => 'is_submitted' 
       ],
       
       // This userstamp should be set if "updating" event is invoked on this model,
       // and provided expression evaluates to true
       'suspended_by' => [
          'depends_on_event' => 'updating', 
          // $api_hits is a model field i.e $model->api_hits
          'depends_on_expression' => '$api_hits > 100' 
       ],
       .............,
       ..............,
    ];
}
yash54's avatar

Hey, Just add this function and you can see the user's id or email or whatever you wanna see man.

public static function boot()
    {
       parent::boot();
       static::creating(function($model)
       {
           $user = Auth::user();
           $model->created_by = $user->email;
           $model->updated_by = $user->email;
       });
       static::updating(function($model)
       {
           $user = Auth::user();
           $model->updated_by = $user->email;
       });
   }

Have a great day.

1 like
amitshahc's avatar

@quentinbontemps I tried this

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use app\Observers\RecordFingerPrintObserver;

class RecordFingerPrintModel extends Model
{
    

    public static function boot()
    {
        $class = get_called_class();
        $class::observe(new RecordFingerPrintObserver(2));
    
        parent::boot();
    }
}

but the error says:

ErrorException
Undefined index: Modules\Admin\Entities\Pages

this is actually a model class

Please or to participate in this conversation.