Adding Products to the Cart | Building a Shopping Cart with Symfony
Tags: php, symfony, tutorial, form
Published: 2020-12-21 12:55:22

- [Building the `AddToCartType` Form](#build-form)
- [Displaying the Form](#display-form)
- [Validating the Model](#validate-model)
- [Handling the Form](#handle-form)

-------

In this step, we will handle the form on the product detail page:

![Capture d’écran 2020-12-21 à 10.39.06](https://dev-to-uploads.s3.amazonaws.com/i/9eczrkasuis25kbvsw3u.png) 

Building a `AddToCartType` form <a name="build-form"></a>
-------------------------------

Use the Maker bundle to generate a form class:

```
$ symfony console make:form AddToCartType OrderItem
```

The `AddToCartType` class defines a form for the `OrderItem` entity. Customize it to have the following fields:
- **quantity**: the number of quantity of products the user wants to purchase, 
- **add**: the submit button to add the product to the cart.

```php
<?php

namespace App\Form;

use App\Entity\OrderItem;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AddToCartType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('quantity');
        $builder->add('add', SubmitType::class, [
            'label' => 'Add to cart'
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => OrderItem::class,
        ]);
    }
}

```

The form fields are bound to the model. The submitted data will be automatically mapped to the model class properties.

Displaying the Form <a name="display-form"></a>
-------------------
To display the form to the user, create the form in the `ProductController` controller using the `FormFactory` factory and pass it to the template:

```php
<?php
namespace App\Controller;
use App\Entity\Product;
use App\Form\AddToCartType;
use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class ProductController
 * @package App\Controller
 */
class ProductController extends AbstractController
{
    /**
     * @Route("/product/{id}", name="product.detail")
     */
    public function detail(Product $product)
    {
        $form = $this->createForm(AddToCartType::class);
        
        return $this->render('product/detail.html.twig', [
            'product' => $product,
            'form' => $form->createView()
        ]);
    }
}
```

In the template `product/detail.html.twig`, display the form by using the `form` Twig function:

```twig
<div class="col-md-8">
    <h1 class="mt-4 mt-md-0">{{ product.name }}</h1>
    <h2>{{ product.price }} €</h2>
    <hr>
    <b>Description: </b>{{ product.description }}
    {{ form_start(form, {'attr': {'class': 'mt-4 p-4 bg-light'}}) }}
    <div class="form-group">
        {{ form_label(form.quantity) }}
        {{ form_widget(form.quantity, {
            'attr': {
                'class': 'form-control ' ~ (form.quantity.vars.valid ? '' : 'is-invalid')
            }
        }) }}
        <div class="invalid-feedback">
            {{ form_errors(form.quantity) }}
        </div>
    </div>
    {{ form_widget(form.add, {'attr': {'class': 'btn btn-warning w-25'}}) }}
    {{ form_end(form) }}
</div>
``` 

Validating the Model <a name="validate-the-model"></a>
--------------------

We need to add some validation constraints on the `OrderItem` model to ensure that the submitted quantity is:
- not empty and,
- greater than or equal to 1.

```php
<?php

namespace App\Entity;

use App\Repository\OrderItemRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass=OrderItemRepository::class)
 */
class OrderItem
{
    // ...
    
    /**
     * @ORM\Column(type="integer")
     * @Assert\NotBlank()
     * @Assert\GreaterThanOrEqual(1)
     */
    private $quantity;
    
    // ...
}
```

Handling the Form <a name="handle-form"></a>
-----------------
Now, we can handle the form submission and add the product to the cart by using the `CartManager` manager.

```php
<?php

namespace App\Controller;

use App\Entity\Product;
use App\Form\AddToCartType;
use App\Manager\CartManager;
use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class ProductController
 * @package App\Controller
 */
class ProductController extends AbstractController
{
    // ...

    /**
     * @Route("/product/{id}", name="product.detail")
     */
    public function detail(Product $product, Request $request, CartManager $cartManager)
    {
        $form = $this->createForm(AddToCartType::class);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $item = $form->getData();
            $item->setProduct($product);

            $cart = $cartManager->getCurrentCart();
            $cart
                ->addItem($item)
                ->setUpdatedAt(new \DateTime());

            $cartManager->save($cart);

            return $this->redirectToRoute('product.detail', ['id' => $product->getId()]);
        }

        return $this->render('product/detail.html.twig', [
            'product' => $product,
            'form' => $form->createView()
        ]);
    }
}
```

When the form is submitted, the `OrderItem` object is updated according to the submitted data. We retrieve it by using the `Form::getData()` method and we don't forget to link the product to the `OrderItem` object. Then, we add the item to the current cart and persist it in the session and database by using the `CartManager::save()` method. Finally, we redirect the user to the product page.

If the form is not valid, we display the page with the form containing the submitted values and error messages.

For the moment, the user can add products to the cart but cannot review their cart because the page does not yet exist. Let's create the cart page in the next step.