You can use model observers :
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?
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;
}
}
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();
}
Hi @QuentinBontemps I am sorry for being so slow. I am very new to laravel/framework.
- From the console I executed this command "php artisan make: model Models\RecordFingerPrintModel"
- 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();
}
}
- 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?
Yes, it should work fine.
You can also use touch() method by default.
@QuentinBontemps the code is not updating the modified_by at all.
@jekinney do you mean just use touch() instead of saving/saved/updated/updating....?
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);
// ...
}
});
}
you can use the revisionable package with this https://github.com/fico7489/laravel-revisionable-upgrade upgrade this is more powerful and elegant way that described above
I have developed a simple package https://github.com/hmshafeeq/userstamps for this.
It handles the following two requirements
- Insert userstamps automatically
- 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'
],
.............,
..............,
];
}
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.
@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.