Silverstripe 4 升级 - ModelAdmin 中未版本化的数据对象丢失其图像对象

Silverstripe 4 Upgrade - Unversioned DataObjects in ModelAdmin lose their image objects

我为此苦恼了一天,累坏了我的 google foo。我继承了一个 Silverstripe 3.4 网站,我们已经升级到 4.4。但是在 运行 MigrateFilesTask 之后某些图像发生了一些奇怪的事情。

认为 这与附加到通过 ModelAdmin 访问的未版本化对象的文件有关。但是我一直没能找到确定的解决方案。

下面这个对象的代码。遇到的问题都在下面。

<?php

use SilverStripe\Assets\Image;
use gorriecoe\Link\Models\Link;
use SilverStripe\Security\Member;
use SilverStripe\Control\Controller;
use SilverStripe\View\Parsers\URLSegmentFilter;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\FieldGroup;
use gorriecoe\LinkField\LinkField;
use SilverStripe\TagField\TagField;
use SilverStripe\ORM\DataObject;
use SilverStripe\SelectUpload\SelectUploadField;

class Person extends DataObject
{

  private static $db = array(
    'FirstName'        => 'Varchar(128)',
    'LastName'         => 'Varchar(128)',
    'Role'             => 'Varchar(128)',
    'DirectDialNumber' => 'Varchar(128)',
    'Email'            => 'Varchar(128)',
    'CellphoneNumber'  => 'Varchar(30)',
    'DirectDial'       => 'Varchar(30)',
    'UrlSegment'       => 'Varchar(255)',
    'Blurb'            => 'HTMLText',
    'SortOrder'        => 'Int'
  );

  private static $has_one = array(
    'Image' => Image::class,
    'Office' => 'Office',
    'LinkedIn' => Link::class,
    'Member' => Member::class
  );

  private static $many_many = array(
    'Interests' => 'Section'
  );

  private static $belongs_many_many = array(
    'ElementCollection' => 'ElementCollection'
  );

  static $sort_fields = array(
    'FirstName' => 'First name',
    'LastName' => 'Last name',
    'Role' => 'Role'
  );

  private static $summary_fields = array(
    'Name' => 'Name',
    'Role' => 'Role',
    'Office.Name' => 'Office'
  );

  private static $searchable_fields = array(
    'FirstName',
    'LastName',
    'Role'
  );

  // For use with the ElementCollection
  public static $templates = array(
    'ElementPeople' => 'Default',
    'ElementPeopleAlternative' => 'Alternative'
  );


  public function getCMSFields() {
    $fields = parent::getCMSFields();
    $fields->removeByName( ['SortOrder', 'ElementCollection', 'FirstName', 'LastName', 'Interests'] );

    $firstname = TextField::create('FirstName', 'First name');
    $lastname = TextField::create('LastName', 'Last name');

    $fields->addFieldsToTab('Root.Main', FieldGroup::create($firstname, $lastname)->setTitle('Name')->setName('Name'), 'Role');

    $image = UploadField::create('Image', 'Photo');
    $image->setFolderName('Uploads/People');
    $image->setCanSelectFolder(false);
    $fields->addFieldToTab('Root.Main', $image);

    $linkedin = LinkField::create('LinkedIn', 'LinkedIn', $this);
    $fields->addFieldToTab('Root.Main', $linkedin);

    $interests = TagField::create(
      'Interests',
      'Interests Tags',
      Section::get(),
      $this->Interests()
    )->setShouldLazyLoad(true)
     ->setCanCreate(false);

    $fields->addFieldToTab('Root.Main', $interests);

    return $fields;
  }

  public function onBeforeWrite()
  {

    $count = 1;
    $this->UrlSegment = $this->generateURLSegment();
    while (!$this->validURLSegment()) {
      $this->UrlSegment = preg_replace('/-[0-9]+$/', null, $this->UrlSegment) . '-' . $count;
      $count++;
    }
    parent::onBeforeWrite();
  }
}

问题 #1 在 运行 MigrateFileTask 之后,所有附加到此 class 实例的现有图像都从 /assets/Uploads/People 移动到 /资产/.protected/Uploads/People。这里令人困惑的是,还有另一个 class 称为 Company,它在结构上几乎相同,但它的图像如预期的那样保留在 /assets/Uploads/Companies 中。

问题 #2 如果我创建一个新的 Person 对象并附上一张图片,该图片在草稿中,位于 /assets/.protected/Uploads/People 中,没有实际发布它的方法。同时,如果我对 Company 对象执行相同操作,图像仍处于草稿状态,但我可以在 CMS 中看到它。

有人可以就以上内容提供一些指导吗?在这一点上,我很高兴能够在 DO 完成时发布图像,我将手动检查每条 Person 记录并点击“保存”以完成此升级。

您应该可以通过将图像添加到 DataObejct 的 owns 属性 来解决此问题。基本上加上这个:

    private static $owns = [
        'Image'
    ];

基本上 owns 告诉 DataObject 保存时要发布哪些对象:

文档中的更多信息:https://docs.silverstripe.org/en/4/developer_guides/model/versioning/#defining-ownership-between-related-versioned-dataobjects

已找到问题 #1 的原因。把这个留在这里以防将来对某人有帮助:

数据库table 文件在系统中的每个文件和文件夹都有一行。此 table 有一个名为 "CanViewType" 的列。它存在于 Silverstripe 3 和 4 中。

对于在迁移过程中引起问题的特定文件夹,我发现它是唯一一个将该列设置为 "OnlyTheseUsers" 的文件夹。其余设置为 "Inherit"。这是 table 升级前的状态。

我不确定该行是如何更改的或通过什么机制更改的,但是问题 #1 的解决方案是在 运行 FileMigrationTask 之前将该字段手动更改为 "Inherit"。

问题 #2 仍然存在,但看起来这里有两个截然不同的问题。

好的。最终对问题 #2 进行了排序(请参阅其他答案以了解 #1 的解决方案),但这极大地削弱了我们对 Silverstripe 的信心,并在此召开了一次会议。

供未来读者使用的代码:

在您的未版本化的 DataObject 中,添加它。在本例中,我的文件 object 名为 "Image"。如果您有多个文件要在保存时发布,则必须为每个文件添加这些 IF 块之一。

public function onAfterWrite()
{
  if ($this->Image()->exists() && !$this->Image()->isPublished()) {
    $this->Image()->doPublish();
  }
  parent::onAfterWrite();
}

旁注:

这种Object/File关系确实是一个奇怪的设计选择。实际上在任何情况下您都希望将文件或图像附加到数据 object 而不是在 save/publish 和 object/page 的同时发布该文件?开发人员甚至需要使用 $owns 在 Versioned objects 上明确定义它——我很高兴打赌大多数开发人员必须添加比不添加更多的次数。这应该真正告诉我们一些错误的方式。

将图像添加到 CMS 系统应该不难。最多应该阅读基本文档。不是谷歌搜索,而是深入 API 文档潜水(回答不多)或在 StackOverlfow 上发帖(没有人真正知道答案)三天。这是一张 图像 。产品的核心功能。

我从 v2.4 开始就一直在使用 SS,看到了为达到 v4 所学到的所有艰辛教训。但这似乎是简单存在 over-engineered 的教科书案例。