I'm trying to have a form that is responsible to register food products in my database. For each product, I'd like also to register the ingredients written on the product package.
For those two Entities I have the following two classes:
Ingredient:
class Ingredient {
// ...
/**
* @var string
*
* @Assert\NotBlank()
* @Assert\Type("string")
*
* @ORM\Column(name="name", type="string", length=255, unique=false)
*/
private $name;
/**
* @ORM\ManyToMany(targetEntity="NutritionBundle\Entity\Product", mappedBy="ingredients")
*/
private $products;
// ...
public function __toString() {
return $this->getName();
}
/**
* Set name
*
* @param string $name
*
* @return Ingredient
*/
public function setName( $name ) {
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName() {
return $this->name;
}
// ...
/**
* @return Product
*/
public function getProducts() {
return $this->products;
}
/**
* @param Product $products
*
* @return Ingredient
*/
public function setProducts( Product $products ) {
$this->products = $products;
return $this;
}
// ...
}
Product:
class Product {
// ...
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
// ...
/**
* @ORM\ManyToMany( targetEntity="NutritionBundle\Entity\Ingredient", inversedBy="products" )
* @ORM\JoinTable(name="ingredients_products")
*/
private $ingredients;
// ...
public function __construct() {
$this->ingredients = new ArrayCollection();
}
// ...
/**
* Set name
*
* @param string $name
*
* @return Product
*/
public function setName( $name ) {
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName() {
return $this->name;
}
// ...
/**
* @return mixed
*/
public function getIngredients() {
return $this->ingredients;
}
/**
* @param mixed $ingredients
*
* @return Product
*/
public function setIngredients( $ingredients ) {
$this->ingredients = $ingredients;
return $this;
}
// ...
}
Then, on the twig side, I need to create the form and utilize the Select2 in order to archive the following:
At the moment, the ProductType
class I did for the product form looks like this:
class ProductType extends AbstractType {
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder
->add( 'name' )
->add( 'company' )
->add( 'front_picture' )
->add( 'back_picture' )
->add( 'weight' )
->add( 'unit' )
->add( 'barcode' )
->add( 'product_url' )
->add( 'ingredients' );
// ->add(
// 'ingredients',
// EntityType::class,
// array(
// 'class' => 'NutritionBundle\Entity\Ingredient',
// 'choice_label' => function ( $ingredient ) {
// return sprintf(
// '%2$s [E%1$s]',
// $ingredient->getCode(),
// $ingredient->getName()
// );
// },
// 'choice_value' => 'id',
// 'multiple' => true,
// )
// );
// $builder->get( 'ingredients' )->addModelTransformer( $this->ingredients_transformer );
}
public function configureOptions( OptionsResolver $resolver ) {
$resolver->setDefaults(
array(
'data_class' => 'NutritionBundle\Entity\Product',
)
);
}
public function getBlockPrefix() {
return 'nutritionbundle_product';
}
}
As you can see in the commented out code, I have also try the EntityType
option, but propably doesn't meet my requirements.
Finally in the twig I have the following code for the Ingredients
field:
<div
class="form-group {% if product_form.ingredients.vars.errors|length %}has-error{% endif %}"
>
<label
for="{{ product_form.ingredients.vars.id }}"
class="control-label"
>
Ingredients
</label>
<select
name="{{ product_form.ingredients.vars.full_name|e('html_attr') }}"
id="{{ product_form.ingredients.vars.id }}"
multiple="multiple"
class="form-control"
data-values="{{ product_form.ingredients.vars.value|join(',') }}"
></select>
<span class="help-block">
<small>Enter the product ingredients as they appear in the ingredients list.</small>
</span>
{% if product_form.ingredients.vars.errors|length %}
<span class="help-block">
{{ form_errors( product_form.ingredients ) }}
</span>
{% endif %}
</div>
The reason I have to render the Ingredients
field in this way, is to achieve the customization I need for loading only the ingredients already assigned to given product using only AJAX.
Finally, the final issue is that, because of this customized field rendering, the Symfony still rendering the Ingredients
field using the default control:
As you can see, above the buttons is my customized rendered field and below the buttons is the field forced by Symfony.
In my current form state, if I try to create new ingredients that doesn't exists yet in the DB, I get error, because the ingredients are not in the DB and ofcourse they can't be assigned to the product.
To not render the Ingredients
field again, you can prevent the rendering with
{% do product_form.ingredients.setRendered %}
But I suggest you use https://github.com/tetranz/select2entity-bundle
Then you won't need to prevent the rendering. Using that Bundle you'll have to define a remote route in a controller which will return the list of options.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.