BitBag Shipping Export Plugin - simple way to control shipments in your online store

Mikołaj Król, 17 lip 2017

The problem

Managing shipments in any eCommerce app is something that may be tricky. There are many shipping providers and every of them has probably its own API format which allows to provide shipment data and book a courier. To make this process more straight forward and generic, we decided to create an abstract layer for Sylius platform based applications which allows you to write just simple API call and configuration form for specific shipping provider. The workflow is quite simple - configure proper data that's needed to export a shipment, like access key or pickup hour, book a courier for an order with one click and get shipping label file if any was received from the API. The implementation limits to write a shipping provider gateway configuration form, one event listener and webservice access layer.

Installation

Install package via composer:

$ composer require bitbag/shipping-export-plugin

Enable plugin in your AppKernel.php:

final class AppKernel extends Kernel
{
    /**
     * {@inheritdoc}
     */
    public function registerBundles()
    {
        return array_merge(parent::registerBundles(), [
            ...

            new \BitBag\ShippingExportPlugin\ShippingExportPlugin(),
        ]);
    }
}

Import config in app/config/config.yml:

imports:
    ...

    - { resource: "@ShippingExportPlugin/Resources/config/config.yml" }

Import routing in you app/config/routing.yml:

bitbag_shipping_export_plugin:
    resource: "@ShippingExportPlugin/Resources/config/routing.yml"
    prefix: /admin

Now when you go to the admin dashboard page, two new menu items should appear under "Sales" and "Configuration" named "Export shipping data" and "Shipping gateways". Shipments to export will appear once someone will place an order with shipping method which has a configured shipping gateway.

Update your database either with migrations or $ bin/console doctrine:schema:update --force  command.

Shipping gateway configuration form

Example form type definition:

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

final class FrankMartinShippingGatewayType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('iban', TextType::class, [
                'label' => 'IBAN',
                'constraints' => [
                    new NotBlank([
                        'message' => 'IBAN number cannot be blank.',
                        'groups' => 'bitbag',
                    ])
                ],
            ])
            ->add('address', TextType::class, [
                'label' => 'Address',
                'constraints' => [
                    new NotBlank([
                        'message' => 'Address cannot be blank.',
                        'groups' => 'bitbag',
                    ])
                ],
            ])
        ;
    }
}

Service definition:

services:
    app.form.type.frank_martin_shipping_gateway:
        class: AppBundle\Form\Type\FrankMartinShippingGatewayType
        tags:
            - { name: bitbag.shipping_gateway_configuration_type, type: "frank_martin_shipping_gateway", label: "Transporter Gateway" }

Shipping export event listener

Basic event listener definition:

namespace AppBundle\EventListener;

use BitBag\ShippingExportPlugin\Event\ExportShipmentEvent;

final class FrankMartinShippingExportEventListener
{
    /**
     * @param ExportShipmentEvent $event
     */
    public function exportShipment(ExportShipmentEvent $event)
    {
        $shippingExport = $event->getShippingExport();
        $shippingGateway = $shippingExport->getShippingGateway();

        if ($shippingGateway->getCode() !== 'frank_martin_shipping_gateway') {
            return;
        }

        if (false) {
            $event->addErrorFlash(); // Add an error notification

            return;
        }

        $event->addSuccessFlash(); // Add success notification
        $event->saveShippingLabel("Some label content received from external API", 'pdf'); // Save label
        $event->exportShipment(); // Mark shipment as "Exported"
    }
}

Service definition:

services:
    ...
    app.event_listener.frank_martin_shipping_export:
    class: AppBundle\EventListener\FrankMartinShippingExportEventListener
    tags:
        - { name: kernel.event_listener, event: 'bitbag.export_shipment', method: exportShipment }

Plugin parameters:

parameters:
    bitbag.shipping_gateway.validation_groups: ['bitbag']
    bitbag.shipping_labels_path: '%kernel.root_dir%/../shipping_labels'

Final thoughts

You can read more use-cases of this plugin in feature files which can be found in this plugin directory. Contributors are always welcomed! You can check out our GitHub profile where more Sylius plugins you may love can be found.

Sylius BDD, ResourceBundle, 27 cze 2017 My adventure with Sylius, part 1, 22 wrz 2017