Reputation: 308
I am trying to achieve the following scenario:
Auction and Category entities (many-to-one). The Category entity has a one-to-many relationship to CategoryAttribute entity which allows an unlimited number of attributes of various types to be added to the category. Let's say a Cars category will have Make and Year attributes. The CategoryAttribute entity has widgetType property which defines how to render the attribute (input, select etc.), attributeValues which is the data for the attribute (populating the select etc.) and isRequired property to tell whether a property is required or not. So far so good. Managing the attributes was piece of cake BUT:
On the Auction side of things I want when the user selects a given category from the list to render all the attributes for that category to be filled in. This is translated to an related AuctionAttribute entity (many-to-one to the auction in attributes property in the class). The AuctionAttribute has reference to the CategoryAttribute, a attributeValue to hold the input or selected value.
Well, the whole AJAX request and fill in of the attributes for a selected category was not a problem. The problem(s) arise when I submit the form. Basically there are two issues.
How do we bind the attributes part of the form to the actual form for validation. Let's say we have Car category selected and Make attribute is required, how do we validate that this attribute?
How do we bind the attributes input to AuctionAttribute entity in that form?
I know that for embedded forms I need to hook up to the FormEvents::PRE_SUBMIT event but I am not sure how to transform the attribute to an Entity in there.
In terms of code, I have the following:
When getting the attributes for a category, I create a AuctionAttributeFormType and render it into a twig form helper and return the HTML back in the AJAX request:
$form = $this->createForm(new Type\AuctionAttributeFormType(), null, array('csrf_protection' => false));
foreach ($categoryAttributes as $attribute) {
$form->add('attribute_'.$attribute->getId(), $attribute->getWidgetType(), array('label' => $attribute->getName(), 'required' => $attribute->isRequired());
}
When the Auction form is submitted, I hook to the PRE_SUBMIT event and when whether there is a attribute submitted and it belongs to the set of attributes of the category but this is as far as I went before I got stuck:
$builder->addEventListener(
Form\FormEvents::PRE_SUBMIT, function (Form\FormEvent $event) {
$auction = $event->getData();
if (null !== $auction['category']) {
$categoryAttributes = $this
->repository
->findAttributesForCategory($auction['category'])
->getResult();
if (count($categoryAttributes) > 0) {
$attribute_values = array();
foreach ($categoryAttributes as $attribute) {
if (isset($auction['attribute_' . $attribute->getId()])) {
$attribute_values[$attribute->getId()] = $auction['attribute_' . $attribute->getId()];
}
}
}
}
}
);
I need to get the values from attribute_values array into AuctionAttribute entities bound to the Auction entity. Any idea how this could be achieved. I think it should be done through some kind of data transformer but I am not sure to what to transform that data - should it be a form->add field, or directly touch the Auction entity which is filled in with data.
Any suggestions?
EDIT:
I made it work with the use of Model transformer but now there is another problem, when editing the record, if there is more than one attribute, only the first one is populated with data. Here is a sample gist of the code:
https://gist.github.com/SvetlinStaev/86e066a865478e40718c
Upvotes: 1
Views: 607
Reputation: 856
My suggestion is NOT to convert the submitted data via Event Listeners, but to use a Data Transformer, which you attach to a form field like so:
$formBuilder->add(
$formBuilder
->create('FIELD_NAME', 'FIELD_TYPE', [
... FIELD_OPTIONS ...
])
->addModelTransformer(new SomeModelTransformer())
)
And the "SomeModelTransformer" class should look like this:
class SeatingToNumberTransformer implements DataTransformerInterface
{
/**
* Transforms the object from the norm data to model data
* The norm data is the field value. Say you have an integer field, $normDataObject would be an int.
* In your case: you need to instantiate several new AuctionAttribute objects and persist them maybe
*/
public function transform($normDataObject)
{
$transformedObject = $this->someTransformAction($normDataObject);
return $transformedObject;
}
/**
* Reverts the transform
* in your case: from AuctionAttribute to int
*/
public function reverseTransform($modelDataObject)
{
$transformedObject = $this->someOtherTransformAction($modelDataObject);
return $transformedObject;
}
}
More info can be found here
If you need more help, just let me know.
Upvotes: 1