PDK backend (PHP)

Latest version of myparcelnl/pdk on Packagist
myparcelnl/pdk issues on GitHub
myparcelnl/pdk pull requests on GitHub

The PDK backend is a PHP library that contains all the logic for building a MyParcel plugin. It contains everything from the API communication to the logic for rendering the frontend components. It is built to be framework-agnostic and extremely extensible, so you can implement it in any PHP application. The PDK backend is built on top of PHP-DI, so it uses dependency injection to make it easy to extend and configure.

The PDK backend is currently only available in PHP, but we intend on creating a Node.js version as well.

If this is something you would like to use, please tell us! This will help us prioritize building it.

Prerequisites

For the backend to work, you need to have the following installed:

If you intend on using the frontend, you will also need to have the following installed:

  • Node 16 or higher (Lower versions may work, but this is not tested)

Installation

Install myparcelnl/pdk using [Composer]:

composer require myparcelnl/pdk

Core concepts

Terminology

See the terminology section for a list of generic terms used throughout the PDK and this documentation.

Dependency injection

Facades

Actions

Frontend

Implementation

To use the PDK backend, you first need to instantiate it. This needs to happen before any of the Facades can be used.

In the following examples, we're building a plugin for WordPress with WooCommerce, so we're prefixing all classes with Wc. You should replace this with your own prefix. This makes it easier to distinguish between your own classes and the PDK classes.

📚 For more in-depth examples and to see the whole thing in action, check out our actual WooCommerce plugin, which is built with the PDK.

Configuration

First, you should create a config file. We recommend you copy the template configuration and place it in <your-project>/config/pdk.php. This template contains all classes you should extend to implement the PDK in your platform. Make sure to check each interface to see what it does and how to implement it.

For example, to define your WcOrderRepository, create a class that extends AbstractPdkOrderRepository and implement all abstract methods:

use MyParcelNL\Pdk\Plugin\Repository\AbstractPdkOrderRepository;
use MyParcelNL\Pdk\Plugin\Model\PdkOrder;

class WcOrderRepository extends AbstractPdkOrderRepository
{
    public function get($input): PdkOrder
    {
        return $this->retrieve((string) $order->id, function () use ($order) {
            return new PdkOrder([
                // map your order data to PDK order data
            ]);
        });
    }
}

And then reference it in your PDK configuration:

return [
    // ...
    PdkOrderRepositoryInterface::class => autowire(MyPlatformPdkOrderRepository::class),
];

Repeat this for all required classes and any other classes you want to implement yourself. Keep in mind you must only extend interfaces, never classes.

Creating the bootstrapper

The PdkBootstrapper is responsible for instantiating the PDK. You can extend it to add your own configuration. For example, to add your own configuration to the PDK, you can extend the getAdditionalConfig method:

use MyParcelNL\Pdk\Base\Pdk;
use MyParcelNL\Pdk\Base\PdkBootstrapper;

namespace MyCompany\MyApp\Pdk\Base;

class WcPdkBootstrapper extends PdkBootstrapper
{
    protected function getAdditionalConfig(
        string $name,
        string $title,
        string $version,
        string $path,
        string $url
    ): array {
        return [
            'mode'      => value(WP_DEBUG ? Pdk::MODE_DEVELOPMENT : Pdk::MODE_PRODUCTION),
            'userAgent' => value([
                // User agents should be sorted from most specific to least specific.
                'MyParcelNL-WooCommerce' => $version,
                'WooCommerce'            => defined('WOOCOMMERCE_VERSION')
                    ? constant('WOOCOMMERCE_VERSION')
                    : '?',
                'WordPress'              => get_bloginfo('version'),
            ]),

            // Here you can add any additional config you want to pass to the DI container.
        ];
    }
}

This allows for easy access to the plugin name, title, version, path and URL. You can use this file along with the config file to instantiate the PDK. We recommend you put all interface overrides in the config file and all other configuration in the bootstrapper.

Instantiating the PDK

Now you can call the bootstrapper to instantiate the PDK:

// <plugin>/myparcelnl-woocommerce.php

use MyParcelNL\WooCommerce\Pdk\WcPdkBootstrapper;

class MyParcelNL {
    public function __construct() {
        WcPdkBootstrapper::boot(
            'myparcelnl',              // Name of the plugin
            'MyParcel',                // Title; will be shown in the admin panel
            $this->getVersion(),       // Plugin version from composer.json
            plugin_dir_path(__FILE__), // Plugin path
            plugin_dir_url(__FILE__)   // Plugin URL
        );
    }
    // ...
}

new MyParcelNL();

This bootstraps the PDK and is required before you can use any functionality. This will allow you to use all facades and the service container in your own code as well. For example, to use our new WcOrderRepository class:

use MyParcelNL\Pdk\App\Order\Contract\PdkOrderRepositoryInterface;

/** @var PdkOrderRepositoryInterface $orderRepository */
$orderRepository = Pdk::get(PdkOrderRepositoryInterface::class);

$order = $orderRepository->get(1); // PdkOrder

Or via constructor injection:

use MyParcelNL\Pdk\App\Order\Contract\PdkOrderRepositoryInterface;

class MyClass {
    private PdkOrderRepositoryInterface $orderRepository;

    public function __construct(PdkOrderRepositoryInterface $orderRepository) {
        $this->orderRepository = $orderRepository;
    }
    // ...
}

Setting up the PDK API

If you do not intend to use the frontend, you can skip this step.

The EndpointActionsInterface interface has the following methods:

public function all(): Collection;
public function getBaseUrl(): string;
public function getEndpoints(): Collection;

When using PdkEndpointActions, all and getEndpoints are already implemented. That means you will only need to implement the getBaseUrl method. This method should return a full URL to your endpoint, without a trailing slash.

For example:

class MyCustomEndpointActions extends \MyParcelNL\Pdk\Plugin\Action\PdkEndpointActions
{
    public function getBaseUrl(): string
    {
        return 'https://' . MY_DOMAIN . '/my-myparcel-pdk-endpoint';
    }
}

Now you need to register this class in your PDK [configuration]:

// config/pdk.php
return [
    // ...
    EndpointActionsInterface::class => autowire(MyCustomEndpointActions::class),
];

This allows the PDK to use your custom endpoint for all PDK API calls. Do not forget to implement the endpoint itself. This depends on your platform and is out of scope for this documentation.

Note that you should never use the exact class names when injecting dependencies. Always use the interface.

Next steps

Create all required classes and register them in your PDK configuration. You can now use the PDK in your own code. To continue with the frontend part of the application, see the PDK frontend documentation.

Edit this page
Last updated: 
Contributors Freek van Rijt