This bundle add History management for entities.
Symfony ^5.4
PHP ^8.1
composer require atournayre/historique-bundle
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
// ...
public function registerBundles()
{
$bundles = array(
// ...
new \Atournayre\Bundle\HistoriqueBundle\HistoriqueBundle(),
// ...
);
}
// ...
}
Replace App\Model\History
by your History entity.
Replace App\Model\User
by your User entity.
# config/packages/doctrine.yaml
doctrine:
orm:
resolve_target_entities:
Atournayre\Bundle\HistoriqueBundle\Interfaces\History: App\Model\History
Symfony\Component\Security\Core\User\UserInterface: App\Model\User
# config/packages/atournayre_historique.yaml
atournayre_historique:
history_class: App\Model\History
Add History entity to your application.
<?php
namespace App\Model;
use Atournayre\Bundle\HistoriqueBundle\Entity\History as BaseHistory;
use Atournayre\Bundle\HistoriqueBundle\EventSubscriber\HistoryEventSubscriber;
use Atournayre\Bundle\HistoriqueBundle\Interfaces\History as HistoryInterface;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\EntityListeners([HistoryEventSubscriber::class])]
class History extends BaseHistory implements HistoryInterface
{
// It is recommended not to extend this entity!
}
- Create/Update entity
- Create a factory for the entity you want to log
- Map your entity to the factory
You can add History to existing entities or add it to new ones.
To add history to an entity, it needs to implements HistorycableInterface
.
Then use HistorycableTrait
to add a relation between YourEntity and History.
<?php
use Atournayre\Bundle\HistoriqueBundle\Traits\HistorycableInterface;
use Atournayre\Bundle\HistoriqueBundle\Traits\HistorycableTrait;
class YourEntity implements HistorycableInterface
{
//...
use HistorycableTrait;
//...
}
Factories are where magic happens.
You have to create a Factory that implements how you want to store the history.
<?php
namespace App\Factory;
use App\Entity\Utilisateur;
use Atournayre\Bundle\HistoriqueBundle\DTO\HistoryDTO;
use Atournayre\Bundle\HistoriqueBundle\Exception\HistoriqueException;
use Atournayre\Bundle\HistoriqueBundle\Factory\AbstractFactory;
use Atournayre\Bundle\HistoriqueBundle\Interfaces\History;
use Symfony\Component\Security\Core\User\UserInterface;
class YourEntityHistoryFactory extends AbstractFactory
{
/**
* @throws HistoriqueException
*/
public function create(array $changeSet): ?History
{
// Create how many methods you want for each node in your change set.
$this->user($changeSet);
// You must call this method (it will convert $changeSet and create the History entity).
return parent::createHistory();
}
// This method implement how information are stored when a user is changed.
private function user(array $changeSet): void
{
/** @var UserInterface[]|null $currentChangeSet */
$currentChangeSet = $changeSet['user'] ?? null;
if (is_null($currentChangeSet)) return;
$this->changeSet->set('user', HistoryDTOFactory::createFromChangeSet(
'New username',
$currentChangeSet,
fn (Utilisateur $utilisateur) => $utilisateur?->getUsername()
));
}
}
Once you factory is created, you need to add a mapping to the config file, so the listener can automatically get the right factory for the right entity.
# config.packages/atournayre_historique.yaml
atournayre_historique:
mappings:
'App\Entity\YourEntity': App\Factory\YourEntityFactory
With this, you can locate entities and factories anywhere in your project.
use Doctrine\Common\Collections\Criteria;
$yourEntity = ...
// Get all the history (the most recent first)
$allPreviousValues = $yourEntity->getEntityChangeSet();
$allPreviousValues = $yourEntity->getEntityChangeSetAsArray();
Of course, open source is fueled by everyone's ability to give just a little bit of their time for the greater good. If you'd like to see a feature or add some of your own happy words, awesome! Tou can request it - but creating a pull request is an even better way to get things done.
Either way, please feel comfortable submitting issues or pull requests: all contributions and questions are warmly appreciated :).