Php 仅使用表单组件的复杂数据结构

Php 仅使用表单组件的复杂数据结构,php,symfony,symfony-2.1,symfony-forms,symfony-2.2,Php,Symfony,Symfony 2.1,Symfony Forms,Symfony 2.2,我正在开发一个带有可定制产品的电子商务系统。每个产品都可能有一些选项,消费者可以选择这些选项中的一个或多个值。我不能使用变体方法,因为我的用户定制程度很高(有些产品可能有超过100万个变体),所以我需要坚持客户选择的选项组合 一旦每个产品可能有不同的选项,表单选项就会动态组合。此表单应将用户选择转换为关系数据库的可存储结构 基本上,这是我的场景(我的尝试): 产品 选择权 期权价值 产品选项 命令 订单项 OrderItemOption 固定装置: 选项:1#沙拉 值:1个番茄、2个生菜

我正在开发一个带有可定制产品的电子商务系统。每个产品都可能有一些选项,消费者可以选择这些选项中的一个或多个值。我不能使用变体方法,因为我的用户定制程度很高(有些产品可能有超过100万个变体),所以我需要坚持客户选择的选项组合

一旦每个产品可能有不同的选项,表单选项就会动态组合。此表单应将用户选择转换为关系数据库的可存储结构

基本上,这是我的场景(我的尝试):

  • 产品
  • 选择权
  • 期权价值
  • 产品选项
  • 命令
  • 订单项
  • OrderItemOption
固定装置:

  • 选项:1#沙拉
    • 值:1个番茄、2个生菜、3个泡菜、3个胡萝卜
  • 产品:汉堡包
  • 产品选项:1#沙拉
    • 值:1#番茄、2#莴苣、苦瓜
我的目标是:

class OrderItemType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $field = $builder->create('options', new OptionPickerType(), ['options' => $options['product']->getOptions()]);
        $field->addModelTransformation(new FixOptionIndexTransformer());
        $builder->add($field);
    }
}

class OptionPickerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        foreach ($options['options'] as $productOption) {
            $name = $productOption->getId();
            $builder->add($name, 'choice', array(
                'choice_list'   => new ObjectChoiceList($productOption->getValues(), 'label', array(), null, 'id'),
                'multiple' => true,
                'cascade_validation' => true,
                'property_path' => '['.$name.']'
            ));
        }
    }
}

$form = $factory->create(new OrderItemType(), ['product' => $product]);

if ($request->isMethod('POST')) {
    $form->bind($request);

    if ($form->isValid()) {
        $item = $form->getItem(); // A collection of ItemOption filled with the OptionValue picked out from Choice field
    }
}
此配置将按预期返回OptionValue数组的集合。事实上,这对我来说是不够的。我真正需要的是一个包含所有选定值的扁平集合更多额外数据:

class ItemOption
{
    protected $item;
    protected $productOption;
    protected $option; // $productOption->getName()
    protected $optionValue;
    protected $value; // / $optionValue->getLabel()
}
如您所见,选择值字段实际上位于ItemOption内

经过几天的尝试,我不知道如何做到这一点,甚至想不出其他方法


你能帮我吗?

首先,当我发现很难将表单映射到模型时,我后来发现模型过于复杂。在这方面,简化模型以在必要时具有清晰的关系和中间对象(在不必要时没有中间对象)通常会有所帮助

也就是说,在我看来,您的选项选择器上的模型转换器应该完成以下工作:

foreach ($options['options'] as $productOption) {
    // ...
}

$builder->addModelTransformer(new CallbackTransformer(
    // model to normalized
    // needed when setting default values, not sure if required in your case
    function ($modelData) {
    },
    // normalized to model
    // converts the array of arrays of OptionValues to an array of ItemOptions
    function ($normalizedData) {
        $itemOptions = array();

        foreach ($normalizedData as $optionValues) {
            foreach ($optionValues as $optionValue) {
                $itemOption = new ItemOption();
                $itemOption->setProductOption($optionValue->getProductOption());
                $itemOption->setOptionValue($optionValue);
                $itemOptions[] = $itemOption;
            }
        }

        return $itemOptions;
    }
));

首先,当我发现很难将表单映射到模型时,我后来发现模型过于复杂。在这方面,简化模型以在必要时具有清晰的关系和中间对象(在不必要时没有中间对象)通常会有所帮助

也就是说,在我看来,您的选项选择器上的模型转换器应该完成以下工作:

foreach ($options['options'] as $productOption) {
    // ...
}

$builder->addModelTransformer(new CallbackTransformer(
    // model to normalized
    // needed when setting default values, not sure if required in your case
    function ($modelData) {
    },
    // normalized to model
    // converts the array of arrays of OptionValues to an array of ItemOptions
    function ($normalizedData) {
        $itemOptions = array();

        foreach ($normalizedData as $optionValues) {
            foreach ($optionValues as $optionValue) {
                $itemOption = new ItemOption();
                $itemOption->setProductOption($optionValue->getProductOption());
                $itemOption->setOptionValue($optionValue);
                $itemOptions[] = $itemOption;
            }
        }

        return $itemOptions;
    }
));

在这里,使用框架可能是一个真正的痛苦。您的问题迫切需要一个NoSQL解决方案,而Symfony并不是专门设计来解决的。@MikeBrant Symfony是一个可以处理许多不同后端的框架。它没有特定的数据持久性后端。它通常与Doctrine2一起使用,但也可以与spreep一起使用,或者配置为使用您想要的任何其他后端。此外,Doctrine2提供了一个ODM,对于从未使用过像Mongo这样的nosqldb的人来说,它是微不足道的。简而言之,Symfony不是问题所在。他询问的表单组件是可用于表单数据操作的最复杂的类之一,可以与纯PHP类一起使用。@MikeBrant,感谢您的考虑。是的,我同意这是NoSQL的一个例子,但我们不能使用这个。Symfony2表单组件是一个功能强大的工具,可能有更复杂的解决方案(比如从头创建一个新的字段类型),但我不知道。您已经用symfony-2.1和symfony-2.2标记了这个问题。您使用的是哪个版本?我提出这个问题的原因是,在2.2中,一个新的PropertyAccess组件是从2.1中表单组件中存在的类创建的,解决方案可能在于利用这些类。在这里,使用框架可能是一个真正的难题。您的问题迫切需要一个NoSQL解决方案,而Symfony并不是专门设计来解决的。@MikeBrant Symfony是一个可以处理许多不同后端的框架。它没有特定的数据持久性后端。它通常与Doctrine2一起使用,但也可以与spreep一起使用,或者配置为使用您想要的任何其他后端。此外,Doctrine2提供了一个ODM,对于从未使用过像Mongo这样的nosqldb的人来说,它是微不足道的。简而言之,Symfony不是问题所在。他询问的表单组件是可用于表单数据操作的最复杂的类之一,可以与纯PHP类一起使用。@MikeBrant,感谢您的考虑。是的,我同意这是NoSQL的一个例子,但我们不能使用这个。Symfony2表单组件是一个功能强大的工具,可能有更复杂的解决方案(比如从头创建一个新的字段类型),但我不知道。您已经用symfony-2.1和symfony-2.2标记了这个问题。您使用的是哪个版本?我问这个问题的原因是,在2.2中,一个新的PropertyAccess组件是从2.1中表单组件中存在的类创建的,解决方案可能在于利用这些类。谢谢,Bernhard,我会试试你的建议。谢谢,Bernhard,我会试试你的建议。