Removing Products from the Cart | Building a Shopping Cart with Symfony
Tags: php, symfony, tutorial, form
Published: 2020-12-21 12:56:14

- [Creating the Event Subscriber](#event-subscriber)
- [Registering the Event Subscriber](#register-event-subscriber)

-----

So far, the *Remove* button has no effect. As we have multiple submit buttons and the default button is *Save*, we need to check if a *Remove* button has been clicked and reorder the cart by removing the product the user wants to remove from the cart.

To modify the form data (the `Order` entity in our case) based on the submitted data, the best practice is to use [Form Events](https://symfony.com/doc/current/form/events.html). To do that, we need to create an Event Subscriber class, subscribe to the  `FormEvents::POST_SUBMIT` event, and reorder the cart.

Creating the Event Subscriber <a name="event-subscriber"></a>
---------------------------
Create a `RemoveCartItemListener` subscriber and subscribe to the `FormEvents::POST_SUBMIT` event: 

```php
<?php

namespace App\Form\EventListener;

use App\Entity\Order;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

/**
 * Class RemoveCartItemListener
 * @package App\Form\EventListener
 */
class RemoveCartItemListener implements EventSubscriberInterface
{
    /**
     * @inheritDoc
     */
    public static function getSubscribedEvents(): array
    {
        return [FormEvents::POST_SUBMIT => 'postSubmit'];
    }

    /**
     * Removes items from the cart based on the data sent from the user.
     *
     * @param FormEvent $event
     */
    public function postSubmit(FormEvent $event): void
    {
        $form = $event->getForm();
        $cart = $form->getData();

        if (!$cart instanceof Order) {
            return;
        }

        // Removes items from the cart
        foreach ($form->get('items')->all() as $child) {
            if ($child->get('remove')->isClicked()) {
                $cart->removeItem($child->getData());
                break;
            }
        }
    }
}

```

The `postSubmit()` method will be invoked after the form is submitted. This will allow us to manipulate the form data mapped with the submitted data.

The current cart is available from the main form by using the `FormEvent::getData()` method. It should be an instance of an `Order` entity. 

For each item field, we check if a *Remove* button has been clicked by the user with the button's `isClicked()` method. If so, we get the `OrderItem` entity by using the form field's `getData()` method and remove it from the current cart.

Now, we need to register this event subscriber to the `CartType` form.

Registering the Event Subscriber <a name="register-event-subscriber"></a>
-------------------------------

To register the `RemoveCartItemListener` subscriber to the `CartType` form, use the `addEventListener` method of the builder in the form type class:

```php
<?php

namespace App\Form;

use App\Entity\Order;
use App\Form\EventListener\RemoveCartItemListener;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CartType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('items', CollectionType::class, [
                'entry_type' => CartItemType::class
            ])
            ->add('save', SubmitType::class)
            ->add('clear', SubmitType::class);

        $builder->addEventSubscriber(new RemoveCartItemListener());
    }

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

That's all. Nothing to do in the `CartController` because we added the logic in the form. Now you are familiar with the form events, let's do the same to clear the cart.