Aller au contenu

Versions

phil/doctrine-behaviours
TypeGenreRôle
Contract\VersionableinterfaceLe numéro, l'indicateur de version figée, ce que garde une version
VersionableEntitytraitLes colonnes version_number et version_frozen
VersionListenerécouteur : onFlushCrée la version suivante d'un enregistrement figé quand un champ versionné change
VersionsserviceListe, fige, revient en arrière
Version\RecordVersionentitéUne version figée, dans la table phil_record_version
Contract\VersionRestoreGuardinterfaceLe droit de revenir à une version passée
AllowVersionRestore, RefuseVersionRestoregardesTout le monde, ou personne
Exception\UnknownVersion, Exception\VersionRestoreDenied, Exception\InvalidVersionedStateexceptions

Chaque entité active les versions en implémentant Versionable, et dit ce que garde une version :

#[ORM\Entity]
class Rule implements Versionable
{
    use VersionableEntity;

    public function versionSubjectId(): string { return (string) $this->id; }
    public function versionedState(): array { return ['title' => $this->title, 'body' => $this->body]; }
    public function applyVersionedState(array $state): void { $this->title = $state['title']; $this->body = $state['body']; }
}

Le principe

  • L'enregistrement est la version en cours de travail, numérotée à partir de un.
  • Une version figée n'est jamais modifiée. Versions::freeze() écrit un instantané de l'enregistrement ; dès lors, le changement suivant d'un champ versionné crée le numéro suivant, et l'instantané reste ce qu'il était.
  • Un changement de toute autre chose, un horodatage, un état de publication, ne crée rien.
  • L'événement qui fige une version relève de la décision de l'application. La publication est le cas habituel ; un écouteur sur workflow.<name>.entered.published appelle freeze().
AppelEffet
freeze($record)Vide ce qui est en attente, puis écrit l'instantané, ou rend celui qui existe
list($record, $limit)Les versions figées, la plus récente en premier, 50 par défaut
get($record, $number)Une version figée, ou null
isFrozen($record, $number)Si ce numéro a un instantané
restore($record, $number)Revient à une version passée, sous la garde, et vide

versionedState() a pour clés des champs et des associations mappés, les seuls changements qui peuvent créer une version : une clé que l'entité ne mappe pas est refusée par InvalidVersionedState. Ses valeurs sont ce que tient JSON, et une base peut rendre les clés dans un autre ordre, MySQL par exemple. Un enregistrement nomme ses versions par sa table racine et son identifiant, qui survivent à un renommage de la classe.

Revenir en arrière

restore() ne réécrit aucun historique. La version en cours de travail est d'abord figée, pour que rien ne se perde, et l'état de la version passée devient le numéro suivant : revenir à 1 depuis 2 laisse 1 et 2 figées, et l'enregistrement à 3.

Revenir en arrière demande un droit qui lui est propre, distinct du droit de modifier : VersionRestoreGuard::check() passe d'abord et lève quand il manque. Il n'y a pas de valeur par défaut : Versions prend une garde, RefuseVersionRestore là où personne ne peut être consulté, AllowVersionRestore pour une commande que lance un opérateur.

Vie privée

Le created_by d'une version, qui nomme celui qui l'a figée, est un auteur : BlameEraser l'atteint. Un instantané tient ce que donne versionedState() et dure autant que sa ligne : cette méthode laisse donc de côté tout renseignement personnel qui ne devrait pas survivre à une demande d'effacement.