Doctrine 2 - Log changes in manyToMany relation(Doctrine 2 - 在多对多关系中记录更改)
问题描述
我使用 可记录的行为扩展来记录我的更改实体.我也想记录多对多关系的变化.我想向用户展示这种更改日志:
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" |
+-------+------------+-----------------------------+
事件问题
我认为,当多对多关系发生变化时,Doctrine 不会触发事件,因此 Loggable(侦听学说事件)不会保存日志条目.我可以通过创建自己的 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:
当我创建表示没有 @JoinTable 注释的 manyToMany 关系的实体时,我不知道如何编写新实体以使其表现得像旧的 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?
你有什么建议,如何记录多对多关系的变化?
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 是一个新函数,用于提取集合并访问 Doctrine 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 新函数,用于从集合实体中提取所需信息并将它们插入到 php 数组中以持久保存到 LogEntry.
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 中的恢复方法.当前的 revert 方法将无法识别 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 AppBundleListener;
use DoctrineCommonEventArgs;
use GedmoLoggableMappingEventLoggableAdapter;
use GedmoToolWrapperAbstractWrapper;
use GedmoLoggableLoggableListener as GedmoLoggableListener;
use SymfonyComponentSecurityCoreAuthenticationTokenStorageTokenStorageInterface;
use AppBundleEntityClause;
use AppBundleEntityGuidanceNote;
use AppBundleEntityStandard;
use GedmoLoggableEntityMappedSuperclassAbstractLogEntry;
use DoctrineORMPersistentCollection;
/**
* 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;
}
}
这篇关于Doctrine 2 - 在多对多关系中记录更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:Doctrine 2 - 在多对多关系中记录更改
基础教程推荐
- XAMPP 服务器不加载 CSS 文件 2022-01-01
- 超薄框架REST服务两次获得输出 2022-01-01
- 通过 PHP SoapClient 请求发送原始 XML 2021-01-01
- 如何在 PHP 中的请求之间持久化对象 2022-01-01
- Libpuzzle 索引数百万张图片? 2022-01-01
- 在 PHP 中强制下载文件 - 在 Joomla 框架内 2022-01-01
- mysqli_insert_id 是否有可能在高流量应用程序中返回 2021-01-01
- 在多维数组中查找最大值 2021-01-01
- 在 Woocommerce 中根据运输方式和付款方式添加费用 2021-01-01
- WooCommerce 中选定产品类别的自定义产品价格后缀 2021-01-01