Reputation: 762
in a legacy system selling digital goods...
products are organized in product categories (4). products may include other sub-products. customer can buy products and become owner of a product instances. each product instance has its' own configuration.
customers should be able to cancel their product instances. therefor i want to build a central cancellation form like the following:
the form contains all product instances of a customer. the products are organized in categories. each product instance should be described by a short textual block describing its' configuration (attributes + some subentity attributes). beside the description there should be a checkbox. if the box is checked the product instance should be canceled.
the entity field type (expanded, multiple) seems to be a good choice? but how can i display the textual information beside the checkbox?
how can i realize such a form using symfony2 form builder?
it would be enough if i could access the product object inside a custom form-type view object to build output like:
<!-- how a product instance should be displayed -->
<!-- description on the left -->
<div class="left">
{{ product.name }} {{ product.calcExpireDate() }}<br />
{% for s in product.getSubentities() %} {{ s.name }}, {% endfor %}
</div>
<!-- checkbox on the right -->
<div class="right">
<input type="checkbox ..[] > <label>Cancel<label>
</div>
Upvotes: 0
Views: 329
Reputation: 897
Here's solution for you:
entities and form types are minimized (without getters/setters)
Suppose you have two entities Category and Product that will look similar to this:
/**
* @ORM\Table()
* @ORM\Entity()
*/
class Category
{
/**
* @var integer
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @ORM\Column(name="name", type="string")
*/
private $name;
/**
* @var \Doctrine\Common\Collections\ArrayCollection
* @ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __toString()
{
return $this->name;
}
}
/**
* @ORM\Table()
* @ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\ProductRepository")
*/
class Product
{
/**
* @var integer
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @ORM\Column(name="name", type="string")
*/
private $name;
/**
* @var string
* @ORM\Column(name="description", type="text")
*/
private $description;
/**
* @var Category
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
*/
private $category;
/**
* @var boolean
*/
private $cancel;
/**
* @var boolean
*/
private $asap;
}
Now, create meta-model for product cancelation:
class Cancel
{
/**
* @var string
*/
private $name;
/**
* @var \Doctrine\Common\Collections\ArrayCollection
*/
private $products;
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
}
NOTE: this is not Entity (Table) but just plain 'ol PHP object.
Generate CRUD for these Product and Category and create CancelType for Cancel model with following buildForm method:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('products', 'collection', array('type' => new ProductType()));
}
Create action that will render our new CancelType
public function cancelAction()
{
$em = $this->getDoctrine()->getManager();
$productsToCancel = $em->getRepository('AcmeDemoBundle:Product')->findAll();
$cancel = new Cancel();
foreach ($productsToCancel as $product) {
$cancel->addProduct($product);
}
$form = $this->createForm(new CancelType(), $cancel);
return $this->render('AcmeDemoBundle:Product:cancel.html.twig', array(
'form' => $form->createView(),
));
}
and create cancel.html.twig with following content:
<div>
{{ form_widget(form.name) }}
</div>
{% for product in form.products %}
<fieldset>
<legend>{{ product.vars.value.category.name }}</legend>
<table>
<tr>
<td>{{ product.vars.value.name }}</td>
<td>
{{ form_widget(product.cancel) }} cancel
{{ form_widget(product.asap) }} asap
</td>
</tr>
<tr>
<td>
{{ product.vars.value.description }}
</td>
</tr>
</table>
</fieldset>
{% endfor %}
With all that you will have what you want plus advantages by having standard Symfony2 form validation.
Last thing is to add action in the controller to handle form submission :)
Upvotes: 1
Reputation: 897
You should use form collection in this kind of situation. Example in symfony2 docs shows how to do that.
Upvotes: 0