1

I'm developing a e-commerce system with customizable products. Each product may have some options that, for its part, may have one or many values selected by consumer. I can't use the variant approach due the high level of customization of my users (some products could have more than 1M of variants), so I need persist the option combination chosen by costumer.

The form options are assembled dynamically once each product may have distinct options. This form should transform the user choice into a storable structure for a relational database.

Basically, it's my scenario (my attempt):

  • Product
  • Option
  • OptionValue
  • ProductOption
  • Order
  • OrderItem
  • OrderItemOption

Fixture:

  • Option: 1#Salad
    • Values: 1#Tomato, 2#Lettuce, 3#Pickles, 3#Carrot
  • Product: Hamburger
  • ProductOption: 1#Salad
    • Values: 1#Tomato, 2#Lettuce, Picles

My target is something like:

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
    }
}

This configuration will return a collection of arrays of OptionValue as expected. In fact it isn't enough for my purposes. What I really need is a flatten collection with all chosen values more some extra data:

class ItemOption
{
    protected $item;
    protected $productOption;
    protected $option; // $productOption->getName()
    protected $optionValue;
    protected $value; // / $optionValue->getLabel()
}

As you can see, the value of Choice field in fact is inside the ItemOption.

After some days trying, I could not figure out how to do this or even think in other approach.

Can you help me?

4
  • Here is where using frameworks can be a real pain. Your problem screams out for a NoSQL solution, which isn't really what Symfony was designed to do. Commented Jan 29, 2013 at 22:46
  • 1
    @MikeBrant Symfony is a framework that can work with many different backends. It doesn't come with a specific data persistence backend. It's typically used with Doctrine2, but can also be used with Propel, or configured to use whatever other backend you want. Furthermore, Doctrine2 offers an ODM that makes it trivial for people who have never done anything with a nosql db like Mongo. In short, Symfony is not the problem. The form component that he's asking about is one of the most sophisticated classes available for doing form data manipulation, and can be used with pure PHP classes. Commented Jan 29, 2013 at 22:51
  • @MikeBrant, thanks for the considerations. Yes, I agree that it's a case of NoSQL, but we can't use this one. The Symfony2 form component is a powerfull tool and probably there is some solution even that more complicated (like creating a new field type from scratch), but I've no ideas. Commented Jan 29, 2013 at 23:50
  • You've tagged the question with both symfony-2.1 and symfony-2.2. Which version are you using? Reason I ask is that in 2.2, a new PropertyAccess component was created from classes that existed in the Form component in 2.1 and a solution might lie with utilising these classes. Commented Mar 10, 2013 at 1:28

1 Answer 1

1

First of all, often when I find it hard to map a form to my model, I later discover that the model is overly complicated. Simplifying the model to have clear relationships and intermediate objects where necessary (and none where not necessary) often helps here.

That being said, it seems to me that a model transformer on your option picker should do the job:

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;
    }
));
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, Bernhard, I'll try your suggestion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.