Skip to content

Aktivitätsprotokoll und Undo ​

Das PIM protokolliert Änderungen mit spatie/laravel-activitylog im Log pim. Darauf bauen zwei Undo-Wege auf. Die Benutzersicht steht in PIM → Aktivitäten.

Aufzeichnen: TracksActivity ​

admin/app/Concerns/TracksActivity.php ist ein Trait für Models. Er hängt sich selbst an die Model-Events created, updated und deleted. Spaties eigenes LogsActivity wird bewusst nicht verwendet, weil es bei $guarded = [] und übersetzbaren Feldern Werte verliert.

Eventproperties
created{ attributes: alle Attribute }
updated{ attributes: geänderte Felder, old: deren alte Werte }. Kein Eintrag, wenn sich nur ignorierte Felder geändert haben
deleted{ old: alle Attribute }

Nie protokolliert werden created_at, updated_at, alle $hidden-Attribute des Models (Passwörter, 2FA-Geheimnisse) und was das Model in $activityIgnore nennt.

Jeder Eintrag hat subject (das Model), causer (angemeldeter Benutzer, sonst „System“) und event.

Neues Model protokollieren:

php
use App\Concerns\TracksActivity;

final class Example extends Model
{
    use TracksActivity;

    /** @var list<string> Technical columns that only add noise. */
    protected array $activityIgnore = ['last_synced_at'];
}

Damit es in der Liste lesbar erscheint, braucht die Klasse eine Bezeichnung (ActivitySubject::titleOf und die Labels in lang/de/pim.php).

Verknüpfungen: RelationActivity ​

Pivot-Änderungen lösen keine Model-Events aus. admin/app/Support/RelationActivity.php schreibt dafür eigene Einträge:

php
RelationActivity::record($group, RelationActivity::ATTACHED, 'spareParts', [$articleId => ['position' => 3]]);
  • Events attached und detached, properties = { relation, pivots: { relatedId: pivotWerte } }.
  • reverse() kehrt sie um: attached → detach, detached → syncWithoutDetaching mit den gespeicherten Pivot-Werten. Nur für BelongsToMany.
  • Derzeit nur in app/Livewire/ArticleGroupArticleTables.php verwendet (Hauptartikel und Ersatzteile in Projekten). Andere Pivot-Änderungen erscheinen nicht als eigene Einträge.

Undo aus der Aktivitätenliste: ActivityUndo ​

admin/app/Support/ActivityUndo.php, aufgerufen aus Filament/Resources/Activities/Tables/ActivitiesTable.php. Die Aktion braucht das Recht Restore:Activity.

blocker(Activity, ?User): ?string liefert den Grund, warum es nicht geht (Lang-Keys pim.activity.undo_blocked.*), sonst null:

EventGesperrt, wenn
alleproperties.reverted_at gesetzt (already); Subject-Klasse unbekannt (unsupported)
updatedDatensatz weg (gone); es gibt einen neueren updated/reverted/restored-Eintrag (newer); keine wiederherstellbaren Spalten (nothing); update nicht erlaubt (forbidden)
deletedDatensatz existiert und ist nicht im Papierkorb (exists); keine alten Werte (nothing); create auf der Klasse nicht erlaubt
createdDatensatz weg (gone); seitdem geändert (newer); delete nicht erlaubt
attached/detachedDatensatz weg; update nicht erlaubt

undo() läuft in einer Transaktion:

  • updated: alte Werte, gefiltert auf echte Tabellenspalten, per forceFill()->saveQuietly() zurück. Neuer Eintrag reverted mit undid_activity_id und restored. saveQuietly verhindert einen zusätzlichen updated-Eintrag.
  • deleted: Soft-Deleted → restore(). Sonst wird aus old (nur echte Spalten) ein neuer Datensatz gebaut und saveQuietly gespeichert. Eintrag restored. Gelöschte Relationen kommen nicht zurück.
  • created: Eintrag reverted mit deleted: true, dann delete().
  • attached/detached: RelationActivity::reverse(), dann reverted.
  • Zum Schluss bekommt der ursprüngliche Eintrag properties.reverted_at, damit er nicht zweimal zurückgenommen wird.

Datensätze werden ohne Mandanten-Scope und einschließlich Papierkorb gesucht.

Die Filter auf echte Spalten sind wichtig: Ältere Einträge enthalten Formularfelder wie taxonomy_12, die keine Spalten sind. Ein forceFill damit würde das UPDATE scheitern lassen.

Undo am Datensatz: ActivityRevisions ​

admin/app/Support/ActivityRevisions.php liefert die Aktionen „Rückgängig“ und „Historie“ für Produkte, Artikel und Projekte.

  • undoable(): die letzten bis zu 5 updated-Einträge des Datensatzes, neueste zuerst, ohne zurückgenommene und ohne solche ohne alte Werte.
  • undoLast(): nimmt den neuesten davon, schreibt die alten Werte (nur echte Spalten) still zurück, markiert ihn mit reverted_at und schreibt einen reverted-Eintrag.
  • „Historie“: die letzten 30 Einträge.
  • Keine eigene Rechteprüfung über die der Seite hinaus.

Zusammenspiel: Nach einem „Rückgängig“ am Datensatz gibt es einen neuen reverted-Eintrag. In der Aktivitätenliste zählt dieser als „neuere Änderung“. Ältere updated-Einträge lassen sich dort deshalb erst wieder zurücknehmen, wenn man in der richtigen Reihenfolge vorgeht; am Datensatz geht es weiter, bis 5 Schritte erreicht sind.

Aufbewahrung ​

logs:archive löscht Einträge älter als 30 Tage (siehe Betrieb). Danach gibt es für diese Änderungen kein Undo mehr.

Tests ​

admin/tests/Feature/ActivityLogTest.php.