主义2 - 在许多关系中记录变化 [英] Doctrine 2 - Log changes in manyToMany relation

查看:132
本文介绍了主义2 - 在许多关系中记录变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用可追溯行为扩展来记录更改我的实体我想记录manyToMany关系中的变化。我想向用户显示这种更改日志:

I use Loggable behavioral extension to log changes in my entities. I want to log changes in manyToMany relations too. I want to show to user this kind of change log:

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



事件问题



我想,当manyToMany关系改变时,教义不会触发事件,所以Loggable(listen doctrine events)不保存日志条目。我可以通过创建我自己的manyToMany表来解决这个问题,但是这是第二个问题:

Event problem

I think, Doctrine doesn't fire events when manyToMany relation changes, so Loggable (listening doctrine events) doesn't save log entry. I can work around it by creating my own manyToMany table, but here's go the second problem:

当我创建表示manyToMany关系而没有@JoinTable注释的实体时,我不知道怎么写新的实体,就像旧的JoinTable一样。我想没有BC休息。你可以给我一个线索,Doctrine如何处理这个?

When I create entity representing manyToMany relation without @JoinTable annotation, I don't know, how to write the new entity to behave like the old JoinTable one. I want no BC break. Can you give me a clue, how Doctrine handles this?

你有什么建议,如何记录manyToMany关系中的变化?

Do you have any recommendation, how to log changes in manyToMany relations?

推荐答案

解决方案不创建自己的连接表。

Solution without creating your own join tables.

我已经修改了我创建的LoggableListener来覆盖Gedmo LoggableListener,我的版本的作品,玩这个,直到你得到它的工作。

I have modified the LoggableListener that I created to override the Gedmo LoggableListener, my version works, play around with this till you get it working.

基本上,用你自己的版本扩展Gedmo LoggableListener,并覆盖/添加一些修改的函数:

Basically, extend the Gedmo LoggableListener with your own version and override /add a few modified functions:

prePersistLogEntry 已启用,允许您修改logEntry(如果需要)。我的logEntry实体包含用户实体和用户全名,而不是用户名。

prePersistLogEntry is enabled to allow you to modify the logEntry if you want to. My logEntry entities contain a user entity and the users Full Name instead of their username.

getCollectionsChangeSetData 是一个新的函数,用于提取集合并获取访问教义PersistentCollections方法。
[ http:// www .doctrine-project.org / api / orm / 2.1 / class-Doctrine.ORM.PersistentCollection.html]

getCollectionsChangeSetData is a new function to extract the collection and get access to the Doctrine PersistentCollections methods. [http://www.doctrine-project.org/api/orm/2.1/class-Doctrine.ORM.PersistentCollection.html]

stripCollectionArray 新功能从收集实体中提取所需的信息,并将其插入到用于持久化到LogEntry的php数组中。

stripCollectionArray new function to extract the desired information from the collection entities and insert them into a php array for persisting to the LogEntry.

有关信息,如果您计划将用户恢复Loggable原则扩展的功能,那么您还需要扩展并覆盖LogEntryRepository中的还原方法。当前的还原方法将无法识别保存在LogEntry中的ManyToMany关联的id。这就是为什么stripCollectionArray函数还将id和class值保存到LogEntry中。

For information, if you are planning to user the revert functionality of the Loggable doctrine extension then you will also need to extend and override the revert method in the LogEntryRepository. The current revert method will not recognise the id from the ManyToMany associations saved in the LogEntry. That is why stripCollectionArray function also saves the 'id' and 'class' values to the 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;
    }

}

这篇关于主义2 - 在许多关系中记录变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆