# Adding a new shipping method

**Out of the box, shipping methods that are registered in the Magento core automatically popup in the Loki Checkout as well. You don't need to do anything for this. However, it might be that things are not working perfectly yet by default or that you need more customization. This document outlines the possibilities.**

A custom module is needed:

- When you want to add a custom logo for each shipping method;
- When you want to add a custom form within the checkout;
- When delivery dates need to be picked from a listing; 
- When pickup locations need to be displayed on a map;
- ...

## Let's talk instead of hack
With Loki Checkout, we aim for an easy integration of shipment methods, where the code of one solution is easily reused for another. Keep it simple stupid. However, if this does not apply to your own customization, **do not** hack the code randomly to get things working.

Instead, get in touch so we can head for the best technical solution with the lowest technical debt.

We offer a specific integrator program to allow for the LokiCheckout core to be used for development of third party integrations (contrary to building a real-life shop). Together with our [open source integrations](https://github.com/orgs/LokiCheckout/repositories), you should be able to make this work.

## Assumptions on this page
We assume that there is already an existing Magento 2 extension available for the PSP of choice. And we assume that this extension has been installed already.

If not, a LokiCheckout module is definitely needed.

## Building a LokiCheckout module
A LokiCheckout module is a Magento module that depends on `LokiCheckout_Core` (both via the `module.xml` file and as a composer dependency). Likewise, we recommend you to have a `README.md` and `CHANGELOG.md` (based on [Keep a Changelog](https://keepachangelog.com/)).

You could create a new Magento module in the way you see fit. Alternatively, you might want to create a new module based upon the [LokiCheckout_EmptyShipment](https://gitlab.yireo.com/loki-checkout/LokiCheckout_EmptyShipment) module. To use one module as a template for another new module, you could use our [Yireo_ModuleDuplicator](https://github.com/yireo/Yireo_ModuleDuplicator) to rename sources quickly.

All specific shipment steps are outlined below anyway.

## Adding a custom logo via an icon resolver
Within the shipment method selection panel of the checkout (template `LokiCheckout_Core::checkout/shipping/shipping-methods.phtml`), methods are shown with a radiobox and a label. This can be extended with a logo which is displayed via the PHTML template `LokiCheckout_Core::checkout/shipping/shipping-methods/icon.phtml`. Within this template, a ViewModel `LokiCheckout\ViewModel\ShipmentMethodIcon` is being used to determine the output of an icon image (literally, the HTML of that icon).

> For Pete his sake, do not override this PHTML template and do not create a DI plugin on this ViewModel.

The ViewModel yet again makes use of an icon resolver logic to try to map the shipment method to a relevant icon. For this, first create a new class:

```php
<?php declare(strict_types=1);

namespace Yireo\Example\Shipment\Icon;

use Magento\Framework\Module\Manager as ModuleManager;
use LokiCheckout\Shipment\Icon\IconResolverContext;
use LokiCheckout\Shipment\Icon\IconResolverInterface;

class IconResolver implements IconResolverInterface
{
    public function resolve(IconResolverContext $iconResolverContext): false|string
    {
        return '<img src="foobar.png" />';
    }
}
```

Your new class needs to be registered with the LokiCheckout mechanism by adding a `etc/frontend/di.xml` file that adds your class to the constructor argument `iconResolvers` of the class `LokiCheckout\Shipment\Icon\IconResolverListing`:

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="LokiCheckout\Shipment\Icon\IconResolverListing">
        <arguments>
            <argument name="iconResolvers" xsi:type="array">
                <item name="example" xsi:type="object">Yireo\Example\Shipment\Icon\IconResolver</item>
            </argument>
        </arguments>
    </type>
</config>
```

## Common icon patterns
In the example above, a simple image `foobar.png` is returned. Obviously, you'll want to return the actual static content URL for this image. You can do that for instance via the method `Magento\Framework\View\Element\AbstractBlockgetViewFileUrl()` (by injecting `Magento\Framework\View\Element\Template` into your resolver class) - a bit ugly but it works nicely.

When working with image URLs, you might want to take a look at the ViewModel class `Loki\Field\ViewModel\ImageOutput`.

In the case of an SVG, it might be better to output the SVG directly (bypassing an `<img>` tag):
```php
$iconFilePath = $iconResolverContext->getIconPath(
    'Yireo_Example',
    'view/frontend/web/images/example.svg'
);
        
return $iconResolverContext->getIconOutput($iconFilePath, 'svg');
```

## Disable all logos
If you want to disable all logos for any shipment method, use the following in a `di.xml` file:

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="LokiCheckout\Core\ViewModel\ShipmentMethodIcon">
        <arguments>
            <item name="enabled" xsi:type="boolean">false</item>
        </arguments>
    </type>
</config>
```

## Adding a custom form
The shipment templates also allow for child templates to be added for the purpose of forms and additional things (like scripts). In this example, we'll use a custom shipment method with code `foobar`:

- Block name `loki.checkout.shipping.methods.foobar.form`
- Block name `loki.checkout.shipping.methods.foobar.additional`

These blocks do not exist yet, but you can easily add them yourselves. For instance, with a XML layout file `loki_checkout_block_shipping_methods.xml` a block for the form can be created at the global level as follows:

```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <body>
        <block
            name="loki.checkout.shipping.methods.foobar.form"
            template="Yireo_Example::method/foobar.phtml"/>
    </body>
</page>
```

In this case, you can see that the block is just a plain PHTML template. How you want to build a HTML form in the PHTML template and link this to the PHTML template, is up to you. This could be custom logic, for instance a complete Loki Component.

## Delivery options and pickup locations
A lot of shipping methods offer delivery options and pickup locations. Reusing logic between modules is therefore a proper thing to do. With the LokiCheckout, various PHTML templates can be reused within a shipment logic:

- `LokiCheckout_Core::checkout/shipping/shipping-method/locations.phtml` and child templates
- `LokiCheckout_Core::checkout/shipping/shipping-method/delivery-dates.phtml` and child templates

These templates rely upon arrays of value objects to be passed via a parent block:
- `Loki\Components\Location\Location[]` in the case of locations
- `\LokiCheckout\Shipment\DeliveryDate[]` in the case of delivery dates and timeframes

The location logic also supports displaying pickup locations on a map (based on `LokiMapComponents`).

> The logic of maps and the hierarchy of these templates is currently undergoing some changes and will be stabilized at the end of milestone 1.1.

## Pickup locations in a dropdown or map
Some shipping methods render their pickup locations via the template `LokiCheckout_ShippingMaps::checkout/shipping/methods/pickup-location.phtml`. This template offers some flexibility to show or hide its subelements. The select title could be toggled with a property `hide_select_title`, the select box itself with `hide_select` and the map with `hide_map`.

For example, the following hides the select box, keeping the map:

```xml
<referenceBlock name="loki-checkout.shipping.methods.lokisimpleshipping_lokisimpleshipping.form">
	<arguments>
        <argument name="hide_select" xsi:type="boolean">true</argument>
    </arguments>
</referenceBlock>
```

Note that in this case, the form of `lokisimpleshipping_lokisimpleshipping` is targetted. But this applies to any shipping method to the same template (like `instore_pickup` and `amstorepickup_amstorepickup`):

```xml
<referenceBlock name="loki-checkout.shipping.methods.amstorepickup_amstorepickup.form">
	<arguments>
        <argument name="hide_map" xsi:type="boolean">true</argument>
    </arguments>
</referenceBlock>
```

The template also calls the child aliases `before`, `middle` and `after`. This can be used to render the current value, for instance like this:

```xml
<referenceBlock name="loki-checkout.shipping.methods.lokisimpleshipping_lokisimpleshipping.form">
    <block
    	name="loki-checkout.shipping.methods.lokisimpleshipping_lokisimpleshipping.form.preview"
        as="after"
        template="LokiCheckout_SimpleShipping::sidebar/preview/shipping_details/selected-store.phtml"
    />
</referenceBlock>
```  

## Cleaning up
When your shipping method is selected, it might save further information in the quote. If another method is selected instead, you will need to clean up this information again. To do this, there is a ready-made event `loki_checkout_shipping_method_update` that allows you to hook into this:

File `etc/frontend/events.xml`:
```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"
>
    <event name="loki_checkout_shipping_method_update">
        <observer name="LokiCheckout_Example::cleanupQuote" instance="LokiCheckout\Example\Observer\CleanupQuote"/>
    </event>
</config>
```

Class `LokiCheckout\Example\Observer\CleanupQuote`:
```php
<?php declare(strict_types=1);

namespace LokiCheckout\Example\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class CleanupQuote implements ObserverInterface
{
    public function execute(Observer $observer)
    {
        $method = $observer->getMethod();
        if (str_starts_with($method, 'example_')) {
            return;
        }

        // @todo
    }
}
```
