使用嵌套的 child 元素对 clone/duplicate TYPO3 8.7 extbase object 创建自己的操作

Create own action to clone/duplicate TYPO3 8.7 extbase object with nested child elements

我在 TYPO3 8.7 中构建基于 extbased 的 TYPO3 扩展。这是一个Backend-Module。在控制器中,我编写了自己的操作来克隆 object。 在此示例中,我想 clone/duplicate object 'Campaign' 并使用修改后的标题对其进行保护,例如将 'copy' 文本添加到标题中。 但是新的 object 也应该有它自己的新的 child 元素,这些元素必须是精确的副本。 调用操作时,我只得到 Object 的副本,但没有 child。是否有如何处理此任务的示例或最佳案例?我没有找到,甚至我发现了一些关于同一主题但旧版本的问题和答案。我希望更新到最新,有一个更直接的解决方案。感谢您给我指出正确想法的每一个提示,也许还有一个最新版本的示例。这是我的控制器。我如何实现所有 child 元素的递归复制(有些 child 也有 child )?

    /**
     * action clone
     * @param \ABC\Copytest\Domain\Model\Campaign $campaign
     * @return void
     * @var \ABC\Copytest\Domain\Model\Campaign $newCampaign
     */

    public function cloneAction(\ABC\Copytest\Domain\Model\Campaign $campaign) {
        $newCampaign = $this->objectManager->get("ABC\Copytest\Domain\Model\Campaign");
        $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', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::OK);
        $this->redirect('list');
    }

有一种方法可以从不同的 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');
}

我知道这个问题很久以前就有人回答了。但我想提供我的解决方案来创建深拷贝以供进一步参考。在 TYPO3 9.5.8.

上测试
private function deepcopy($object)
{
    $clone = $this->objectManager->get(get_class($object));
    $properties = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($object);
    foreach ($properties as $propertyName => $propertyValue) {
        if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
            $v = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\ObjectStorage::class);
            foreach($propertyValue as $subObject) {
                $subClone = $this->deepcopy($subObject);
                $v->attach($subClone);
            }
        } else { 
            $v = $propertyValue;
        }
        if ($v !== null) {
            \TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($clone, $propertyName, $v);
        }
    }
    return $clone;
}

如果您的对象中有“LazyLoadingProxy”实例,您需要再添加一个条件。

if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
   $objectStorage = $propertyValue->_loadRealInstance();
}

这是我对“深度复制”功能的解决方案:

    private function deepcopy($object)
    {
        $clone = $this->objectManager->get(get_class($object));
        $properties = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($object);
        foreach ($properties as $propertyName => $propertyValue) {
            if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
                $objectStorage = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\ObjectStorage::class);
                foreach ($propertyValue as $subObject) {
                    $subClone = $this->deepcopy($subObject);
                    $objectStorage->attach($subClone);
                }
            } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
                $objectStorage = $propertyValue->_loadRealInstance();
            } else {
                $objectStorage = $propertyValue;
            }
            if ($objectStorage !== null) {
                \TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($clone, $propertyName, $objectStorage);
            }
        }

        return $clone;
    }