基架列表框在模型管理员筛选器中为带有枚举的数据对象选择多个选项


Scaffold ListBox multiple select in ModelAdmin Filter for DataObject with Enum

目前,具有枚举的搜索字段的自动脚手架会生成一个下拉列表,仅允许进行一次选择。 我有兴趣使用现有过滤器来更改此设置以允许多项选择。

给定以下数据对象...

class MyDataObject extends DataObject {
    static $db = array(
        'Name'      => "Varchar(255)",
        'MyEnum'    => "Enum('Option1,Option2,Option3','Option1')"
    );  
}

。和以下模型管理员...

class MyModelAdmin extends ModelAdmin {
    static $mangaged_models = array(
        'MyDataObject',
    );  
    static $url_segment = 'mymodeladmin';
    static $menu_title = 'MyModelAdmin';
    static $menu_priority = 9;
}

。我正在寻找某种模块或简单的过滤器,以将枚举基架到多选列表框中

多选列表框定义为...

  • 允许多选
  • 键入一些字符后,提供建议

我要求一个通用的解决方案 - 我可以为每个模型管理员构建一个搜索上下文,但这非常令人沮丧。使用现有过滤器(ExactMatchMultiFilter 看起来很完美,但似乎实际上不起作用)或模块中有一个过滤器,或者有人可以建议如何为此修改现有过滤器

,如下所示。
class MyDataObject extends DataObject {
    static $db = array(
        'Name'      => "Varchar(255)",
        'MyEnum'    => "Enum('Option1,Option2,Option3','Option1')"
    );
    public static $searchable_fields = array (
        'MyEnum'    => array('filter' => 'ExactMatchMultiFilter')
    );
}

任何帮助都非常感谢。

从您的问题来看,您似乎打算传递到可搜索字段的filter会更改脚手架。我做了一些挖掘,但情况似乎并非如此。但是,如果您改用field选项,则可能会实现所需的目标。

您确实特别提到了ListboxField,虽然它确实支持多个,但它不会由字段上的默认构造函数启用,这是它的实例化方式。

你想要的东西可以通过开箱即用地完成更多 CheckboxSetField .(我承认,在模型管理中使用时,UI有点平均)

生成的代码可能如下所示:

class MyDataObject extends DataObject {
    static $db = array(
        'Name'      => "Varchar(255)",
        'MyEnum'    => "Enum('Option1,Option2,Option3','Option1')"
    );
    public static $searchable_fields = array (
        'MyEnum'    => array('field' => 'CheckboxSetField')
    );
}

不幸的是,这并不容易,您会注意到,只要这样做,它就会说"没有可用的选项"而不是复选框列表。这是由于SilverStripe在提供我之前提到的field选项时的行为不同。

这种解决方法不是很好,但可以说仍然是通用的。我做了一个ModelAdmin的扩展类,它在搜索表单中查找CheckboxSetField并为其设置枚举值。

class MyModelAdminExtension extends Extension {
    public function updateSearchForm($form) {
        $modelClass = $form->getController()->modelClass;
        foreach ($form->Fields() as $field) {
            if ($field->class == 'CheckboxSetField') {
                //We need to remove the "q[]" around the field name set by ModelAdmin
                $fieldName = substr($field->getName(), 2, -1);
                $dbObj = singleton($modelClass)->dbObject($fieldName);
                if ($dbObj->class == 'Enum') {
                    $enumValues = $dbObj->enumValues();
                    $field->setSource($enumValues);
                }
            }
        }
    }
}

这是一个相对安全的ModelAdmin扩展,因为它专门查找映射到CheckboxSetField的 Enum 的组合,这只有在您手动指定它时才会发生。

走了这么远,我们实际上可以回顾ListboxField,克服被禁用的多个选项并用值填充它(因为它会遇到上面提到的相同问题)。这个解决方案将不那么通用,因为我们将强制从枚举映射的所有ListboxField是倍数,但如果我们想要一个更好的解决方案,这就是我们获得它的方式。

class MyModelAdminExtension extends Extension {
    public function updateSearchForm($form) {
        $modelClass = $form->getController()->modelClass;
        foreach ($form->Fields() as $field) {
            if ($field->class == 'ListboxField') {
                //We need to remove the "q[]" around the field name set by ModelAdmin
                $fieldName = substr($field->getName(), 2, -1);
                $dbObj = singleton($modelClass)->dbObject($fieldName);
                if ($dbObj->class == 'Enum') {
                    $field->setMultiple(true);
                    $enumValues = $dbObj->enumValues();
                    $field->setSource($enumValues);
                }
            }
        }
    }
}

对于我们的模型...

class MyDataObject extends DataObject {
    private static $db = array(
        'Name' => "Varchar(255)",
        'MyEnum' => "Enum('Option1,Option2,Option3','Option1')"
    );
    public static $searchable_fields = array (
        'MyEnum' => array('field' => 'ListboxField')
    );
}

您现在拥有了所需的内容 - 具有枚举值的多选ListBoxField

你现在可能会问,我为什么要报道CheckboxSetField?好吧,我认为重要的是要查看所有可能的解决方案。我通过尝试CheckboxSetField找到了我提供的解决方案,这真的只是最后一刻的事情,我意识到通过一些小的修改,我可以让它为ListboxField工作。


正如您提出的,上述代码在HasOne关系中处理枚举存在问题。这是由于ModelAdmin扩展采用字段名称并将其专门视为表单模型类上的数据库字段(通过dbObject)。相反,我们可以从字段名称上的双下划线中检测关系,将其替换为点语法并将其视为关系(通过 relObject )。

我们更新的updateSearchForm函数如下所示:

public function updateSearchForm($form) {
    $modelClass = $form->getController()->modelClass;
    foreach ($form->Fields() as $field) {
        if ($field->class == 'ListboxField') {
            //We need to remove the "q[]" around the field name set by Model Admin
            $fieldName = substr($field->getName(), 2, -1);
            $dbObj = null;
            //Check if the field name represents a value across a relationship
            if (strpos($fieldName, '__') !== false) {
                //To use "relObject", we need dot-syntax
                $fieldName = str_replace('__', '.', $fieldName);
                $dbObj = singleton($modelClass)->relObject($fieldName);
            }
            else {
                $dbObj = singleton($modelClass)->dbObject($fieldName);
            }
            if ($dbObj != null && $dbObj->class == 'Enum') {
                $field->setMultiple(true);
                $enumValues = $dbObj->enumValues();
                $field->setSource($enumValues);
            }
        }
    }
}