Trochę ostatnio się bawiłem z monitorowaniem zmian dokonywanych przez użytkowników. Głównym wyzwaniem było przedstawienie logów dla innych administratorów w sposób zrozumiały i czytelny i w języku polskim.
Paczka laravel-activitylog
Instalacja laravel-activitylog jest prosta. Wystarczy postępować zgodnie z instrukcją na stronie repozytorium, czyli.
composer require spatie/laravel-activitylog
Następnie
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="migrations"
I tutaj jeśli z jakiś względów wasza baza danych nie obsługuję typu danych JSON, należy przejść do pliku z migracją czyli, create_activity_log_table i zamienić
$table->json('properties')->nullable();
na
$table->text('properties')->nullable();
Następnie migracja
php artisan migrate
W modelach, które chcemy monitorować pod względem zmian dodajemy
protected static $logAttributes = [TU NASZE ATRYBUTY,KTÓRE CHCEMY ZAPISYWAĆ]
Możemy oczywiście zrobić, w szybszy i mniej bezpieczny sposób, wrzucając wszystko jak leci:
protected static $logAttributes = [*]
oraz nazwę loga i $logOnlyDirty = true, czyli zapisujemy, tylko te atrybuty, które zostały rzeczywiście zmienione.
protected static $logName = 'Przyjazna nazwa loga np. specyfikacja klienta'; protected static $logOnlyDirty = true;
Do kolejnych modeli, które chcemy monitorować, wykonujemy tą samą operację.
Wyświetlamy poprzez wywołanie
Activity::all()
i tutaj, zaczyna się to, co należało naprawić, czyli formatowanie logów, które będą zrozumiałe dla innych administratorów.
Do formatowania stworzymy nowy model php artisan make:model Activity
Model Activity.php
Do formatowania najlepiej było użyć mutatorów.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Activity extends Model
{
protected $table = 'activity_log';
/**
* Użyto mutatora dla Properties, aby edytowane lub nowo utworzone atrybuty, które są przechowywane jako JSON,
* byłby przedstawione w sposób:
* Zmianiono atrybut x z *stara wartość* na *nową wartość*
* @param $value
* @return string
*/
public function getPropertiesAttribute($value)
{
$array = json_decode($value, true);
if(isset($array["old"])) {
$allKeys = array_keys($array["old"]);
for($i = 0; $i<sizeof($array["old"]);$i++) {
$result[] = [
'key'=>$allKeys[$i],
'old'=> array_values($array["old"])[$i],
'new'=> array_values($array["attributes"])[$i]
];
}
return $result;
} else if(isset($array["attributes"])) {
$allKeys = array_keys($array["attributes"]);
for($i = 0; $i<sizeof($array["attributes"]);$i++) {
$result[] = [
'key'=>$allKeys[$i],
'new'=> array_values($array["attributes"])[$i]
];
}
return $result;
}
return null;
}
/**
* Użyto mutatora, aby przetłumaczyć nazwę operacji, której dokonał użytkownik.
* @param $eventName
* @return string
*/
public function getDescriptionAttribute($eventName) {
if ($eventName == 'created') {
return 'Uwtorzył(a) rekord w bazie';
}else if ($eventName == 'updated') {
return 'Zaktualizował(a) rekord w bazie';
}else if ($eventName == 'deleted') {
return 'Usunął/eła rekord w bazie';
}
return '';
}
/**
* Relacja, z użytkownikiem, który dokonał zmian
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function causer() {
return $this->belongsTo('App\User');
}
}
I właściwie gotowe. Wystarczy, że wywołamy w view {{$activit->description}} czy {!!$activit->properties!!}
Przykład
<ul>
@if($activity)
@foreach($activity as $singleActivity)
<li class="left clearfix">
<span class="img pull-left">
<img src="https://via.placeholder.com/50?text={{$singleActivity->causer->name[0]}}">
</span>
<div class="body clearfix">
<div class="header">
<strong>{{$singleActivity->causer->name }}:</strong>
{{$singleActivity->description}}
<small class="pull-right">
<i class="fa fa-clock-o fa-fw"></i>
{{$singleActivity->created_at->diffForHumans()}}
</small>
</div>
@if($singleActivity->properties)
<p>Atrybuty:
@foreach($singleActivity->properties as $properties)
<b>{{$properties['key']}}</b>: {{$properties['new']}}
@if(isset($properties['old']))
z {{$properties['old']}}
@endif
<br>
@endforeach
</p>
@endif
</div>
</li>
@endforeach
@endif
</ul>
Więcej informacji na ten temat w dokumentacji.