Loggable behavior tracks your record changes and is able to manage versions.
Features:
- Automatic storage of log entries in database
- ORM and ODM support using same listener
- Can be nested with other behaviors
- Objects can be reverted to previous versions
- Annotation, Yaml and Xml mapping support for extensions
Update 2011-04-04
- Made single listener, one instance can be used for any object manager and any number of them
Portability:
- Loggable is now available as Bundle ported to Symfony2 by Christophe Coevoet, together with all other extensions
This article will cover the basic installation and functionality of Loggable behavior
Content:
- Including the extension
- Entity example
- Document example
- Yaml mapping example
- Xml mapping example
- Basic usage examples
Read the documentation or check the example code on how to setup and use the extensions in most optimized way.
- @Gedmo\Mapping\Annotation\Loggable(logEntryClass="my\class") this class annotation will store logs to optionally specified logEntryClass. You will still need to specify versioned fields with the following annotation.
- @Gedmo\Mapping\Annotation\Versioned tracks annotated property for changes
In order to set the username, when adding the loggable listener you need to set it this way:
$loggableListener = new Gedmo\Loggable\LoggableListener;
$loggableListener->setAnnotationReader($cachedAnnotationReader);
$loggableListener->setUsername('admin');
$evm->addEventSubscriber($loggableListener);
Note: that Loggable interface is not necessary, except in cases there you need to identify entity as being Loggable. The metadata is loaded only once when cache is active
<?php
namespace Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* @Entity
* @Gedmo\Loggable
*/
class Article
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @Gedmo\Versioned
* @ORM\Column(length=8)
*/
private $title;
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
If you want to add some of yours logic to store logs in DB, you can create your own Entity (for example, Logs), where you can override some methods or add your own, in Entity you should extends AbstractLogEntry.
<?php
namespace AppBundle\Entity;
use Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="logs")
* @ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository")
*
*/
class Logs extends AbstractLogEntry
{
}
And then add to you Entity (ex. Article) next annotations:
- @Gedmo\Loggable(logEntryClass="AppBundle\Entity\Logs")
Choose fields that you need to logging in next way:
- @Gedmo\Versioned
Add don't forget to update scheme.
<?php
namespace Document;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* @ODM\Document(collection="articles")
* @Gedmo\Loggable
*/
class Article
{
/** @ODM\Id */
private $id;
/**
* @ODM\Field(type="string")
* @Gedmo\Versioned
*/
private $title;
public function __toString()
{
return $this->title;
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
Yaml mapped Article: /mapping/yaml/Entity.Article.dcm.yml
---
Entity\Article:
type: entity
table: articles
gedmo:
loggable:
# using specific personal LogEntryClass class:
logEntryClass: My\LogEntry
# without specifying the LogEntryClass class:
# loggable: true
id:
id:
type: integer
generator:
strategy: AUTO
fields:
title:
type: string
length: 64
gedmo:
- versioned
content:
type: text
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:gedmo="http://Atlantic18.github.io/DoctrineExtensions/schemas/orm/doctrine-extensions-3.0.xsd">
<entity name="Mapping\Fixture\Xml\Loggable" table="loggables">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
</id>
<field name="title" type="string" length="128">
<gedmo:versioned/>
</field>
<many-to-one field="status" target-entity="Status">
<join-column name="status_id" referenced-column-name="id"/>
<gedmo:versioned/>
</many-to-one>
<gedmo:loggable log-entry-class="Gedmo\Loggable\Entity\LogEntry"/>
</entity>
</doctrine-mapping>
<?php
$article = new Entity\Article;
$article->setTitle('my title');
$em->persist($article);
$em->flush();
This inserted an article and inserted the logEntry for it, which contains all new changeset. In case if there is OneToOne or ManyToOne relation, it will store only identifier of that object to avoid storing proxies
Now lets update our article:
<?php
// first load the article
$article = $em->find('Entity\Article', 1 /*article id*/);
$article->setTitle('my new title');
$em->persist($article);
$em->flush();
This updated an article and inserted the logEntry for update action with new changeset Now lets revert it to previous version:
<?php
// first check our log entries
$repo = $em->getRepository('Gedmo\Loggable\Entity\LogEntry'); // we use default log entry class
$article = $em->find('Entity\Article', 1 /*article id*/);
$logs = $repo->getLogEntries($article);
/* $logs contains 2 logEntries */
// lets revert to first version
$repo->revert($article, 1/*version*/);
// notice article is not persisted yet, you need to persist and flush it
echo $article->getTitle(); // prints "my title"
$em->persist($article);
$em->flush();
// if article had changed relation, it would be reverted also.
Easy like that, any suggestions on improvements are very welcome