I have observer trait MainObserver
for any model.
<?
namespace App\Observers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Models\AppEvent;
use App\Models\AppEventData;
trait MainObserver
{
public function handleCreatedEvent($model) {
$event = new AppEvent;
$event->event_type = 'CREATE';
$event->model_type = $model::getModelType();
$event->model_id = $model->id;
$event->creator_id = Auth::user() ? Auth::user()->id : null;
$event->save();
}
public function handleUpdatedEvent($model) {
$originalData = $model->getOriginal();
$newData = $model->getAttributes();
$modelType = $model::getModelType();
$modelFields = $model->getFieldsKeys();
$event = new AppEvent;
$event->event_type = 'EDIT';
$event->model_type = $modelType;
$event->model_id = $model->id;
$event->creator_id = Auth::user() ? Auth::user()->id : null;
$eventDataArray = [];
foreach($modelFields as $modelAttribute) {
if(!$model->shouldSaveEventForField($modelAttribute)) continue;
if(!isset($originalData[$modelAttribute])) {
$originalData[$modelAttribute] = null;
}
if(!isset($newData[$modelAttribute])) {
$newData[$modelAttribute] = null;
}
if($originalData[$modelAttribute] !== $newData[$modelAttribute]) {
$eventData = new AppEventData;
$eventData->field = $modelAttribute;
$fieldData = $model->getField($modelAttribute);
if($fieldData['type'] === 'password') {
$originalData[$modelAttribute] = '***';
$newData[$modelAttribute] = '******';
}
$eventData->old_value = $originalData[$modelAttribute];
$eventData->new_value = $newData[$modelAttribute];
$eventDataArray[] = $eventData;
}
}
if(!count($eventDataArray)) return;
DB::transaction(function() use($event, $eventDataArray) {
$event->save();
$event->eventData()->saveMany($eventDataArray);
});
}
}
And it is used like this
<?
namespace App\Observers;
use App\Models\User;
class UserObserver
{
use MainObserver;
public function created(User $user) {
$this->handleCreatedEvent($user);
}
public function updated(User $user) {
$this->handleUpdatedEvent($user);
}
}
namespace App\Observers;
use App\Models\User;
class UserObserver
{
use MainObserver;
public function created(User $user) {
$this->handleCreatedEvent($user);
}
public function updated(User $user) {
$this->handleUpdatedEvent($user);
}
// in AppServiceProvider
User::observe(UserObserver::class);
The problem is: it does not store foreign relations (belongsToMany).
So I wrote this trait for my models
public function setAttribute($key, $value) {
$pivot = static::getFieldPivotField($key);
if( $pivot ) {
$this->delayedSave[] = [
'key' => $key, 'value' => $value, 'pivot' => $pivot
];
return;
}
return parent::setAttribute($key, $value)
}
public function save(array $options = array()) {
$exists = $this->exists;
$dirty = $this->getDirty();
$saved = parent::save($options);
if($saved) {
foreach($this->delayedSave as $v) {
$this->savePivot($v['key'], $v['value'], $v['pivot']);
}
$this->delayedSave = [];
}
}
And, finally, savePivot
: it takes old ids
and new ids
for pivot and store them, if something changed.
private function savePivot($key, $value, $pivot) {
$ids = array_column($value, 'id');
$old = $this->{$key}()->get()->pluck('pivot.' . $pivot)->toArray();
if($old === $ids) return;
$event = new AppEvent;
$event->event_type = 'EDIT';
$event->model_type = $this::getModelType();
$event->model_id = $this->id;
$event->creator_id = Auth::user() ? Auth::user()->id : null;
$eventDataArray = [];
$eventData = new AppEventData;
$eventData->field = $key;
$eventData->old_value = join('|', $old);
$eventData->new_value = join('|', $ids);
$eventDataArray[] = $eventData;
DB::transaction(function() use($event, $eventDataArray, $key, $ids) {
$this->{$key}()->sync($ids);
$event->save();
$event->eventData()->saveMany($eventDataArray);
});
}
May be it is too much and there is a simple way to store log of foreign relations updates?
via Jackson J