silverstripe - 是否可以对 many_many 关系进行版本控制?

标签 silverstripe

我已经在 DataObjects 上使用了版本控制当它们包含大量内容时,现在我想知道是否可以将版本控制应用于 many_many关系?

假设我有以下内容:

class Page extends SiteTree
{
    private static $many_many = array(
        'Images' => 'Image' 
    );
}

然后 ORM 会创建一个 Page_Images表供我存储关系。为了拥有版本化关系,需要更多的表(例如 Page_Images_Live )。

有没有办法告诉 ORM 创建版本化关系?当使用 Page * – * Images 查看上面的示例时关系,我不想要 Image要版本化的类,而是关系。例如。像这样:
Version Stage:
---
    PageA
        Images ( ImageA, ImageB, ImageC )

Version Live:
---
    PageA
        Images ( ImageA, ImageC, ImageD, ImageE )

这甚至可以开箱即用吗?

最佳答案

我花了很多时间研究这个,但没有从根本上修改 ManyManyList (因为它没有通过扩展系统公开必要的钩子(Hook)),没有太多选择。

我是甜品第一的人,怎么办?

我完成这个壮举的唯一建议本质上是一个多对多的桥接对象(即一个单独的实体加入 PageImage )通过 $has_many虽然它仍然需要相当多的修改。

这是 partially discussed on the forum其中通过将版本化项目存储在实际对象而不是连接表中来破坏实际关系的解决方案。那会奏效,但我认为我们仍然可以做得更好。

我个人倾向于将这种关系的版本与 Page 联系起来。本身和我下面的部分解决方案涵盖了这一点。阅读下面的折叠以获取更多信息,尝试将此作为对 ManyManyList 的更新.

像这样的事情是一个开始:

class PageImageVersion extends DataObject
{
    private static $db = array(
        'Version' => 'Int'
    );

    private static $has_one = array(
        'Page' => 'Page',
        'Image' => 'Image'
    );
}

这包含我们的 2 向关系以及我们存储的版本号。您需要指定 getCMSFields功能添加所需的正确字段,允许您将其与现有图像相关联或上传新图像。我避免覆盖这一点,因为与实际版本处理部分相比,它应该相对简单。

现在,我们有一个 has_manyPage像这样:
private static $has_many = array(
    'Images' => 'PageImageVersion' 
);

在我的测试中,我还为 Image 添加了扩展名添加匹配 $has_many也像这样:
class ImageExtension extends DataExtension
{
    private static $has_many = array(
        'Pages' => 'PageImageVersion'
    );
}

Honestly, not sure if this is necessary beyond adding the Pages function on the Image side of the relationship. As far as I can see, it won't really matter for this particular usecase.



不幸的是,由于这种版本控制方式,我们不能使用调用 Images 的标准方式。 ,我们需要有点创意。像这样的东西:
public function getVersionedImages($Version = null)
{
    if ($Version == null)
    {
        $Version = $this->Version;
    }
    else if ($Version < 0)
    {
        $Version = max($this->Version - $Version, 1);
    }

    return $this->Images()->filter(array('Version' => $Version));
}

当您拨打 getVersionedImages() ,它将返回所有具有 Version 的图像设置它与当前页面的版本对齐。还支持通过 getVersionedImages(-1) 获取以前的版本对于最后一个版本,甚至通过传递任何位置编号来获取页面特定版本的图像。

好的,到目前为止一切顺利。我们现在需要确保每个页面写入我们都会为这个新版本的页面获得一个重复的图像列表。

onAfterWrite功能在 Page , 我们可以完成这个:
public function onAfterWrite()
{
    $lastVersionImages = $this->getVersionedImages(-1);
    foreach ($lastVersionImages as $image)
    {
        $duplicate = $image->duplicate(false);
        $duplicate->Version = $this->Version;
        $duplicate->write();
    }
}

For those playing at home, this is where things get a bit iffy relating to how restoring previous versions of Page would affect this.



因为我们将在 GridField 中编辑它,我们需要做一些事情。首先是确保我们的代码可以处理 Add New功能。

我的想法是 onAfterWritePageImageVersion目的:
public function onAfterWrite()
{
    //Make sure the version is actually saved
    if ($this->Version == 0)
    {
        $this->Version = $this->Page()->Version;
        $this->write();
    }
}

让您的版本项目显示在 GridField ,你会像这样设置它:
$gridFieldConfig = GridFieldConfig_RecordEditor::create();
$gridField = new GridField("Images", "Images", $this->getVersionedImages(), $gridFieldConfig);
$fields->addFieldToTab("Root.Images", $gridField);

您可能希望直接从 GridField 链接到图像。通过 GridFieldConfig_RelationEditor然而,这是事情变得糟糕的时候。

蔬菜的时间...

最大的困难之一是GridField ,用于链接和取消链接这些实体。使用标准 GridFieldDeleteAction没有正确的版本会直接更新关系。

您将需要扩展 GridFieldDeleteAction并覆盖 handleAction写下您的 Page对象(以触发另一个版本),为上一个版本复制我们版本化图像对象的每个版本,同时使其跳过新版本中您不想要的那个。

I'll admit, this last bit is just guesswork by me. From my understanding and debugging, it should work but simply there is a lot of fiddling to get it right.



您的分机号 GridFieldDeleteAction然后需要添加到您的特定 GridField .

这基本上是使此解决方案起作用的最后一步。一旦你完成了添加、删除、复制、版本更新的部分,这真的只是使用 getVersionedImages() 的问题。以获得正确的图像。

结论

避免。 我明白你为什么要这样做,但我真的没有看到一种干净的方法来处理这个,而没有对如何进行适当的更新 many_many关系在 Silverstripe 中处理。

但我真的想要它作为 ManyManyList !

我认为 ManyManyList 所需的更改有一个 3 路 key (外键、本地 key 、版本 key )和用于添加/删除/获取等的各种方法更新。

如果 add 中有钩子(Hook)和 remove功能,您可以将功能作为扩展(通过 Silverstripe 的扩展系统)潜入,并将所需的数据添加到 many_many 的额外字段中。关系允许。

虽然我可以通过扩展 ManyManyList 来实现这一点直接然后强制ManyManyList将通过 Object::useCustomClass 替换为我的自定义类,这将是一个更困惑的解决方案。

对我来说,对一个纯粹的 ManyManyList 给出完整的答案实在是太长/太复杂了。在这个阶段的解决方案(尽管我稍后可能会回到这个问题并试一试)。

免责声明:我不是 Silverstripe Core 开发人员,这整个事情可能有一个更简洁的解决方案,但我根本看不到如何解决。

关于silverstripe - 是否可以对 many_many 关系进行版本控制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17896869/

相关文章:

Silverstripe 3.1.5 - 上传错误 SyntaxError : Unexpected token <

javascript - 银纹 + 无限 Ajax 卷轴 : None Left keeps being added to page

php - SilverStripe 3.x 中是否推荐命名空间?

php - SilverStripe 唯一 URL(调试)

php - 如何在 Silverstripe 中搜索表数据?

php - 从脚本更新 SiteTree_Localized 表

silverstripe - 修改 GridFieldFilterHeader 以使用自定义汇总字段

php - require_once(PHPExcel/类/PHPExcel.php): failed to open stream

php - 在带有枚举的 DataObject 的 ModelAdmin 过滤器中使用脚手架列表框进行多项选择

silverstripe - 如何获取子页面并在另一个页面类型上循环遍历它的数据?