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

时间:2018-08-27 11:30:43

标签: object controller typo3 extbase typo3-8.x

我在TYPO3 8.7中构建了基于扩展的TYPO3扩展。这是一个后端模块。在控制器中,我编写了自己的操作来克隆对象。 在此示例中,我想克隆/复制对象“ Campaign”并使用修改后的标题对其进行保护,例如在标题中添加“复制”文本。 但是,新对象还应该具有自己的新子元素,这些子元素必须是精确的副本。 调用该操作时,我仅获得对象的副本,而没有任何子对象。有没有一个例子或最好的情况下如何处理此任务?我没有找到,甚至我找到了与同一主题有关的一些问题和答案,但是版本较旧。我希望最新的解决方案更加直接。感谢您为我提供正确想法的每一个提示,也许是最新的版本示例。这是我的控制器。如何实现所有子元素的递归复制(有些子元素也有子元素)?

    /**
     * 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');
    }

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

我知道这个问题已经很久了。但是,我想提供我的解决方案以创建深层副本,以供进一步参考。在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;
        }
        \TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($clone, $propertyName, $v);
    }
    return $clone;
}

答案 2 :(得分:0)

如果您的对象中有“ 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;
    }