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.