为什么我的函数输出“方法 'forTemplate' 在 'SilverStripe\View\ArrayData 上不存在”

Why does my function output " the method 'forTemplate' does not exist on 'SilverStripe\View\ArrayData"

这将是我在这里的第一个问题。我希望我能给你所有你需要的信息。

我是运行一个使用silverstripe v4.8的个人项目 在我的模板中,我有一个 optionsetfield,它基本上只是一个无线电场。 选项中的前 3 个项目,我已经硬编码了。 我为其余部分编写了另一个函数,基本上可以获取所有可以创建事件的人的名字,遍历它们并将它们添加为选项。

当我转储我的结果时,它似乎以我想要的方式出现:

array (size=1)
  'Rick' => string 'Rick' (length=4)

但是当我试图在我的模板中看到它时,它给了我:

Object->__call(): the method 'forTemplate' does not exist on 'SilverStripe\View\ArrayData'

现在,当我不将该函数添加到我的选项集时,前 3 个硬编码项工作正常。

我将 post 我的 OptionsField 和下面的其他函数。 提前谢谢你

  public function createEventsFilterForm()
{
    $form = Form::create();

    $form = FieldList::create([
        OptionsetField::create('Eventfilters')
            ->setTitle('')
            ->setSource([
                'past' => 'Verlopen',
                'today' => 'Vandaag',
                'future' => 'Toekomst',
                $this->getFirstNameOfEvents()
            ])
    ]);

    return $form;
}

public function getFirstNameOfEvents()
{

    $allEvents = UpcomingEvents::get();
    foreach ($allEvents as $event) {

        $firstName = 'NVT';

        $memberProfileID = $event->MemberProfileID;

        if ($memberProfileID) {
            $firstName = [MemberProfile::get()->byID($memberProfileID)->FirstName];
        }

        $output = ArrayLib::valuekey($firstName);

        return $output;
    }
}

tl;博士:

SilverStripe 模板无法处理数组。我猜数组自动转换为 ArrayData 对象。

如果你想更明确一点,你可以这样写:

return new ArrayData([
  'Name' => "FOOBAR"
]);

然后在模板中:

$FirstNameOfEvent <!-- this will also cause this error, because SSViwer does not know how to render ArrayData -->

<!-- but you can access object properties, like the Name that we defined: -->
$FirstNameOfEvent.Name <!-- will output FOOBAR -->


详细解释:

forTemplate是SSViewer在渲染对象时调用的。
基本上,它是等同于 __toString() 的 SilverStripe,每当您尝试在 SilverStripe 模板中将对象输出到浏览器时,SSViewer(渲染器)将对该对象调用 forTemplate

举个例子:

class Foo extends VieableData {
  public $message = 'Hello World';
  public function forTemplate() {
    return "This is a foo object with the message '{$this->message}'";
  }
}
class PageController extends ContentController {
  public function MyFooObject() {
    return new Foo();
  }
}

因此,如果在您的 Page.ss 模板中调用 $MyFooObject,它将调用同名函数并获取一个对象。因为它是一个对象,SSViewer 不知道如何渲染并将调用 Foo->forTemplate()。然后将导致输出 This is a foo object with the message 'Hello World'

ArrayData 没有 forTemplate 方法,因此会出现错误。有两种方法可以解决这个问题[1]:

  • 子类 ArrayData 并实现一个 forTemplate 方法,将您的数据转换为可以输出到浏览器的字符串(或 DBField 对象)
  • 不要尝试在您的模板中呈现 ArrayData,而是直接访问数据(就像上面的 tl;dr,所以 $MyArrayData.MyField)[2]

[1]:所有对象同理
[2]:直接访问对象属性始终是可能的,即使您有 forTemplate 方法。 forTemplate 只是默认设置,如果您不指定 属性。


编辑:

抱歉,我部分误解了您的 question/problem。
我上面说的所有内容仍然是正确的,理解起来很重要,但它没有回答你的问题。

我以为您是在模板中调用 $getFirstNameOfEvents,但实际上,您是在 DropDownField 中使用它(错过了那部分)。
关于 SilverStripe CMS 的事情是,它也使用与前端相同的模板系统来处理它自己的事情。所以DropDownField也会使用SSViewer来渲染。所以我的解释仍然是正确的,它只是发生在内置模板文件 DropDownField.ss 中。它做这样的事情:

<select>
<% loop $Source %>
  <option value="$Key">$Value</option>
<% end_loop %>
</select>

$Source 这是您的数组 ['past' => 'Verlopen', 'today' => 'Vandaag', 'future' => 'Toekomst', $this->getFirstNameOfEvents()],它会自动转换为 ArrayData 对象。

现在,问题是,它并不像您认为的那样工作:

// the result you want:
['past' => 'Verlopen', 'today' => 'Vandaag', 'future' => 'Toekomst', 'Rick' => 'Rick']

// the result your code produces:
['past' => 'Verlopen', 'today' => 'Vandaag', 'future' => 'Toekomst', 0 => ['Rick' => 'Rick']]

注意你如何在一个数组中包含一个数组。因为getFirstNameOfEventsreturns一个数组。

那么你实际应该做什么:

    $source = ['past' => 'Verlopen', 'today' => 'Vandaag', 'future' => 'Toekomst'];
    $source = array_merge($source, $this->getFirstNameOfEvents());

    $form = FieldList::create([
        OptionsetField::create('Eventfilters')
            ->setTitle('')
            ->setSource($source)
    ]);