Doctrine 2 – Log изменяет отношение manyToMany

Я использую логическое расширение Loggable для регистрации изменений в моих сущностях. Я хочу также вносить изменения в отношения manyToMany. Я хочу показать пользователю такой журнал изменений:

+--------------------------------------------------+ | Article "My Article" change log: | +-------+------------+-----------------------------+ | Who | When | What | +-------+------------+-----------------------------+ | Admin | 2015-07-01 | Removed tags "tag1", "tag2" | | Admin | 2015-07-01 | Added tags "tag3" | +-------+------------+-----------------------------+ 

Событие

Я думаю, что Doctrine не запускает события, когда изменяется многогольное отношение , поэтому Loggable (прослушивание событий доктрины) не сохраняет запись в журнале. Я могу обойти это, создав собственную таблицу ManyToMany, но вот вторая проблема:

Собственная проблема ManyToMany

Когда я создаю сущность, представляющую отношение manyToMany без аннотации @JoinTable, я не знаю, как написать новый объект, чтобы вести себя как старый JoinTable. Я не хочу разорвать БК. Не могли бы вы дать мне понять, как это делает Учение?

Есть ли у вас рекомендации, как регистрировать изменения в отношениях manyToMany?

Related of "Doctrine 2 – Log изменяет отношение manyToMany"

Решение без создания ваших собственных таблиц соединений.

Я изменил LoggableListener, который я создал, чтобы переопределить Gedmo LoggableListener, моя версия работает, играйте с этим, пока не получите его работу.

В основном, расширьте Gedmo LoggableListener вашей собственной версией и переопределите / добавьте несколько измененных функций:

prePersistLogEntry разрешает вам изменять logEntry, если хотите. Мои объекты logEntry содержат пользовательский объект и полное имя пользователя вместо имени пользователя.

getCollectionsChangeSetData – это новая функция для извлечения коллекции и получения доступа к методам Doctrine PersistentCollections. [ http://www.doctrine-project.org/api/orm/2.1/class-Doctrine.ORM.PersistentCollection.html%5D

stripCollectionArray новая функция для извлечения желаемой информации из объектов коллекции и вставки их в массив php для сохранения в LogEntry.

Для получения информации, если вы планируете пользователю вернуть функциональность расширения доктрины Loggable, вам также потребуется расширить и переопределить метод возврата в LogEntryRepository. Текущий метод возврата не будет распознавать идентификатор из ассоциаций ManyToMany, сохраненных в LogEntry. Вот почему функция stripCollectionArray также сохраняет значения «id» и «class» в LogEntry.

Удачи.

 <?php namespace AppBundle\Listener; use Doctrine\Common\EventArgs; use Gedmo\Loggable\Mapping\Event\LoggableAdapter; use Gedmo\Tool\Wrapper\AbstractWrapper; use Gedmo\Loggable\LoggableListener as GedmoLoggableListener; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use AppBundle\Entity\Clause; use AppBundle\Entity\GuidanceNote; use AppBundle\Entity\Standard; use Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry; use Doctrine\ORM\PersistentCollection; /** * Loggable listener * * Extends the Gedmo loggable listener to provide some custom functionality. * * * @author Mark Ogilvie <mark.ogilvie@specshaper.com> */ class LoggableListener extends GedmoLoggableListener { // Token storage to get user private $tokenStorage; // Injet token storage in the services.yml public function __construct(TokenStorageInterface $token) { $this->tokenStorage = $token; } /** * Manipulate the LogEntry entity prior to persisting. * In this case add a user, and set entity information * according to the custom entity family group. * * @param EventArgs $eventArgs * * @return void */ protected function prePersistLogEntry($logEntry, $object) { $user = $this->tokenStorage->getToken()->getUser(); $logEntry instanceof AbstractLogEntry; $logEntry ->setUser($user) ->setChangedObject('text.default') ->setUsername($user->getFullName()) ; switch (true) { case $object instanceof Clause: $logEntry->setChangedObject('text.clause') ->setEntity($object) ; break; case $object instanceof GuidanceNote: $logEntry->setChangedObject('text.guidanceNote') ->setEntity($object->getClause()) ; break; case $object instanceof Standard: $logEntry->setChangedObject('text.standard') ; break; } } /** * Returns an objects changeset data * * Modified to create an array which has old and new values instead * of just the new. * * Also added reference to UoW collection changes to pick up ManyToMany * relationships * * @param LoggableAdapter $ea * @param object $object * @param object $logEntry * * @return array */ protected function getObjectChangeSetData($ea, $object, $logEntry) { $om = $ea->getObjectManager(); $wrapped = AbstractWrapper::wrap($object, $om); $meta = $wrapped->getMetadata(); $config = $this->getConfiguration($om, $meta->name); $uow = $om->getUnitOfWork(); // Define an array to return as the change set data. $returnArray = array(); foreach ($ea->getObjectChangeSet($uow, $object) as $field => $changes) { if (empty($config['versioned']) || !in_array($field, $config['versioned'])) { continue; } $value = $changes[1]; if ($meta->isSingleValuedAssociation($field) && $value) { if ($wrapped->isEmbeddedAssociation($field)) { $value = $this->getObjectChangeSetData($ea, $value, $logEntry); } else { $oid = spl_object_hash($value); $wrappedAssoc = AbstractWrapper::wrap($value, $om); $value = $wrappedAssoc->getIdentifier(false); if (!is_array($value) && !$value) { $this->pendingRelatedObjects[$oid][] = array( 'log' => $logEntry, 'field' => $field, ); } } } $returnArray[$field]['previous'] = $changes[0]; $returnArray[$field]['new'] = $value; } // For each collection add it to the return array in our custom format. foreach ($uow->getScheduledCollectionUpdates() as $col) { $associations = $this->getCollectionChangeSetData($col); $returnArray = array_merge($returnArray, $associations); } return $returnArray; } /** * New custom function to get information about changes to entity relationships * Use the PersistentCollection methods to extract the info you want. * * @param PersistentCollection $col * @return array */ private function getCollectionChangeSetData(PersistentCollection $col) { $fieldName = $col->getMapping()['fieldName']; // http://www.doctrine-project.org/api/orm/2.1/class-Doctrine.ORM.PersistentCollection.html // $col->toArray() returns the onFlush array of collection items; // $col->getSnapshot() returns the prePersist array of collection items // $col->getDeleteDiff() returns the deleted items // $col->getInsertDiff() returns the inserted items // These methods return persistentcollections. You need to process them to get just the title/name // of the entity you want. // Instead of creating two records, you can create an array of added and removed fields. // Use private a newfunction stripCollectionArray to process the entity into the array $newValues[$fieldName]['new'] = $this->stripCollectionArray($col->toArray()); $newValues[$fieldName]['previous'] = $this->stripCollectionArray($col->getSnapshot()); return $newValues; } /** * Function to process your entity into the desired format for inserting * into the LogEntry * * @param type $entityArray * @return type */ private function stripCollectionArray($entityArray) { $returnArr = []; foreach ($entityArray as $entity) { $arr = []; $arr['id'] = $entity->getId(); $arr['class'] = get_class($entity); if (method_exists($entity, 'getName')) { $arr['name'] = $entity->getName(); } elseif (method_exists($entity, 'getTitle')) { $arr['name'] = $entity->getTitle(); } else { $arr['name'] = get_class($entity); } $returnArr[] = $arr; } return $returnArr; } }