创建自己的操作以克隆/复制具有嵌套子元素的 TYPO3 8.7 extbase 对象



我在TYPO38.7中构建了我的扩展TYPO3扩展。它是一个后端模块。在控制器中,我编写自己的操作来克隆对象。 在此示例中,我想克隆/复制对象"广告系列"并使用修改后的标题来保护它,例如将"复制"文本添加到标题中。 但是新对象也应该有自己的新子元素,这些子元素必须是精确副本。 调用该操作时,我只得到对象的副本,但没有子项。是否有示例或最佳案例如何处理此任务?我没有找到,甚至我找到了一些关于同一主题的问题和答案,但版本较旧。我希望更新到目前为止,有一个更直接的解决方案。感谢您为我指出正确想法的每一个提示,也许还有一个最新的版本示例。 这是我的控制器。如何实现所有子元素的递归复制(有些子元素也有子元素(?

/**
* action clone
* @param ABCCopytestDomainModelCampaign $campaign
* @return void
* @var ABCCopytestDomainModelCampaign $newCampaign
*/
public function cloneAction(ABCCopytestDomainModelCampaign $campaign) {
$newCampaign = $this->objectManager->get("ABCCopytestDomainModelCampaign");
$properties = $campaign->_getProperties();
unset($properties['uid']);
foreach ($properties as $key => $value) {
$newCampaign->_setProperty($key, $value);
}
$newCampaign->_setProperty('title', $properties['title']. ' COPY');
$this->campaignRepository->add($newCampaign);
$this->addFlashMessage('Clone was created', '', TYPO3CMSCoreMessagingAbstractMessage::OK);
$this->redirect('list');
}

我知道这个问题很久以前就已经回答了。但我想提供我的解决方案来创建深层副本以供进一步参考。在 TYPO3 9.5.8 上测试。

private function deepcopy($object)
{
$clone = $this->objectManager->get(get_class($object));
$properties = TYPO3CMSExtbaseReflectionObjectAccess::getGettableProperties($object);
foreach ($properties as $propertyName => $propertyValue) {
if ($propertyValue instanceof TYPO3CMSExtbasePersistenceObjectStorage) {
$v = $this->objectManager->get(TYPO3CMSExtbasePersistenceObjectStorage::class);
foreach($propertyValue as $subObject) {
$subClone = $this->deepcopy($subObject);
$v->attach($subClone);
}
} else { 
$v = $propertyValue;
}
if ($v !== null) {
TYPO3CMSExtbaseReflectionObjectAccess::setProperty($clone, $propertyName, $v);
}
}
return $clone;
}

有一种方法可以从不同的 POV 处理此用例,即没有标识的请求参数值会自动放入新对象中,然后可以持久化。这基本上克隆了原始对象。这是您需要做的:

  1. 添加一个视图,其中包含对象所有属性的字段,隐藏字段也可以。例如,这可以是一个edit视图,其中包含一个单独的提交按钮来调用您的clone操作。
  2. 添加一个initializeCloneAction()并通过$this->request->getArguments()获取原始请求参数。
  3. 现在做unset($arguments[<argumentName>]['__identity']);,如果你想要副本而不是共享引用,对你的对象具有的每个关系做同样的事情。
  4. 通过$this->request->setArguments($arguments)再次存储原始请求参数。
  5. 最后,允许在参数的属性映射配置中创建新对象,并可能在所有关系属性中创建新对象。

完整的initializeCloneAction()如下所示:

public function initializeCloneAction()
{
$arguments = $this->request->getArguments();
unset(
$arguments['campaign']['__identity'],
$arguments['campaign']['singleRelation']['__identity'],
);
foreach (array_keys($arguments['campaign']['multiRelation']) as $i) {
unset($arguments['campaign']['multiRelation'][$i]['__identity']);
}
$this->request->setArguments($arguments);
// Allow object creation now that we have new objects
$this->arguments->getArgument('campaign')->getPropertyMappingConfiguration()
->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true)
->allowCreationForSubProperty('singleRelation')
->getConfigurationFor('multiRelation')
->allowCreationForSubProperty('*');
}

现在,如果您使用clone操作提交表单,您的clone操作将获得一个完全填充的新对象,您可以像往常一样将其存储在存储库中。然后,您的cloneAction()将非常简单:

public function cloneAction(Campaign $campaign)
{
$this->campaignRepository->add($campaign);
$this->addFlashMessage('Campaign was copied successfully!');
$this->redirect('list');
}

如果你的对象中有"LazyLoadProxy"实例,你需要再添加一个条件。

if ($propertyValue instanceof TYPO3CMSExtbasePersistenceGenericLazyLoadingProxy) {
$objectStorage = $propertyValue->_loadRealInstance();
}

这是我对"深拷贝"功能的解决方案:

private function deepcopy($object)
{
$clone = $this->objectManager->get(get_class($object));
$properties = TYPO3CMSExtbaseReflectionObjectAccess::getGettableProperties($object);
foreach ($properties as $propertyName => $propertyValue) {
if ($propertyValue instanceof TYPO3CMSExtbasePersistenceObjectStorage) {
$objectStorage = $this->objectManager->get(TYPO3CMSExtbasePersistenceObjectStorage::class);
foreach ($propertyValue as $subObject) {
$subClone = $this->deepcopy($subObject);
$objectStorage->attach($subClone);
}
} elseif ($propertyValue instanceof TYPO3CMSExtbasePersistenceGenericLazyLoadingProxy) {
$objectStorage = $propertyValue->_loadRealInstance();
} else {
$objectStorage = $propertyValue;
}
if ($objectStorage !== null) {
TYPO3CMSExtbaseReflectionObjectAccess::setProperty($clone, $propertyName, $objectStorage);
}
}
return $clone;
}

我认为一个好的解决方案是模拟后端函数。 请参阅代码示例(德语文本( http://blog.marcdesign.ch/2015/05/27/typo3-extbase-objekte-kopieren/

一般的想法是扩展TYPO3CMSCoreDataHandlingDataHandler并使用父方法copyRecord。将预定义的后端最终用户声明为扩展类中的$this->BE_USER。预定义后端最终用户的 obejct 可以通过使用类TYPO3\CMS\Backend\FrontendBackendUserAuthentication和预定义后端用户的已知名称来获取。您的用户应具有管理员权限,您应该定义$BE_USER->uc_default['copyLevels']= '9999';并声明$BE_USER->uc = $BE_USER->uc_default。 我没有检查,是否真的需要声明$GLOBALS['PAGES_TYPES'][254]['allowedTables'] = '*';。 方法本身copyRecord主要需要表名、uid值、pid值和语言对象作为参数。语言对象可以得到$GLOBALS['lang'],虽然可以通过实例化TYPO3CMSLangLanguageService$GLOBALS['lang']TYPO3CMSCoreCharsetCharsetConverter$GLOBALS['LANG']->csConvObj来生成。

对不起,我的英语很差。

最新更新