Skip to content
Snippets Groups Projects
Commit abe84c4d authored by Bohdan Artemchuk's avatar Bohdan Artemchuk
Browse files

Issue #3339263 by bohart: Created a new filed type/widget/formatter and form...

Issue #3339263 by bohart: Created a new filed type/widget/formatter and form element to store the parcel terminal for the shipment.
parent 40c0a243
No related branches found
No related tags found
1 merge request!15Issue #3339263 by bohart: Created a new filed type to store the parcel terminal for the shipment.
<?php
namespace Drupal\commerce_loko\Element;
use Drupal\commerce_loko\LokoClientInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;
/**
* Provides a Loko parcel select form element.
*
* Usage example:
* @code
* $form['address_loko'] = [
* '#type' => 'address_loko',
* '#default_value' => [
* 'parcel_terminal_name' => 'Test parcel locker',
* 'parcel_terminal_id' => 'a9522a9d-eaf5-11e7-ba66-005056b2fc3d',
* ],
* ];
* @endcode
*
* @FormElement("address_loko")
*/
class LokoElement extends FormElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
return [
'#input' => TRUE,
'#multiple' => FALSE,
'#default_value' => NULL,
'#process' => [
[get_class($this), 'processElement'],
],
'#theme_wrappers' => ['container'],
];
}
/**
* Ensures all keys are set on the provided value.
*
* @param array $value
* The value.
*
* @return array
* The modified value.
*/
public static function applyDefaults(array $value) {
$properties = [
'parcel_terminal_name',
'parcel_terminal_id',
];
foreach ($properties as $property) {
if (!isset($value[$property])) {
$value[$property] = NULL;
}
}
return $value;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Ensure both the default value and the input have all keys set.
if (is_array($input)) {
$input = static::applyDefaults($input);
}
$element['#default_value'] = (array) $element['#default_value'];
$element['#default_value'] = static::applyDefaults($element['#default_value']);
return is_array($input) ? $input : $element['#default_value'];
}
/**
* Processes LokoElement form element.
*
* @param array $element
* The form element to process.
* @param FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*
* @throws \Exception
*/
public static function processElement(array &$element, FormStateInterface $form_state, array &$complete_form) {
/** @var LokoClientInterface $loko_client */
$loko_client = \Drupal::service('commerce_loko.client');
$loko_client->setShipmentByEntityId($element["#shipment_method"]);
$lockers = $loko_client->getAllParcelLockers();
$options = [];
foreach ($lockers as $locker) {
$options[$locker['id']] = $locker['name'];
}
$element['parcel_terminal_id'] = [
'#type' => 'select',
'#title' => t('Select your parcel terminal'),
'#options' => $options,
'#default_value' => $element['#value']['parcel_terminal_name'] ?? NULL,
'#required' => TRUE,
];
// @todo: save parcel terminal name on saving.
return $element;
}
}
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
namespace Drupal\commerce_loko; namespace Drupal\commerce_loko;
use Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface; use Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface as PluginShippingMethodInterface;
use Drupal\commerce_shipping\Entity\ShippingMethodInterface as EntityShippingMethodInterface;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
...@@ -31,12 +32,33 @@ class LokoClient implements LokoClientInterface { ...@@ -31,12 +32,33 @@ class LokoClient implements LokoClientInterface {
*/ */
protected EntityStorageInterface $shippingMethodStorage; protected EntityStorageInterface $shippingMethodStorage;
/**
* The shipping method entity.
*
* @var \Drupal\commerce_shipping\Entity\ShippingMethodInterface
*/
private EntityShippingMethodInterface $shipmentMethodEntity;
/** /**
* The shipping method plugin. * The shipping method plugin.
* *
* @var \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface * @var \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface
*/ */
private ShippingMethodInterface $shipmentMethod; private PluginShippingMethodInterface $shipmentMethodPlugin;
/**
* The configured API domain from the shipment method.
*
* @var string
*/
private string $api_domain;
/**
* The configured API token from the shipment method.
*
* @var string
*/
private string $api_token;
/** /**
* Constructor for the LokoClient Service. * Constructor for the LokoClient Service.
...@@ -56,22 +78,26 @@ class LokoClient implements LokoClientInterface { ...@@ -56,22 +78,26 @@ class LokoClient implements LokoClientInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setShipment(ShippingMethodInterface $shipping_method): void { public function setShipmentByPlugin(PluginShippingMethodInterface $shipping_method): void {
if ($shipping_method->getPluginId() !== 'loko_shipment') { if ($shipping_method->getPluginId() !== 'loko_shipment') {
throw new \InvalidArgumentException('You need to set a LokoShipment shipping method.'); throw new \InvalidArgumentException('You need to set a LokoShipment shipping method.');
} }
$this->shipmentMethod = $shipping_method; $this->shipmentMethodPlugin = $shipping_method;
$this->api_token = $shipping_method->getConfiguration()['loko_token'];
$this->api_domain = $shipping_method->getConfiguration()['domain'];
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setShipmentById(string $id): void { public function setShipmentByEntityId(string $id): void {
$shipping_method = $this->shippingMethodStorage->load($id); $shipping_method = $this->shippingMethodStorage->load($id);
if ($shipping_method instanceof ShippingMethodInterface) { if ($shipping_method instanceof EntityShippingMethodInterface) {
$this->setShipment($shipping_method); $this->shipmentMethodEntity = $shipping_method;
$this->api_token = $shipping_method->getPlugin()->getConfiguration()['loko_token'];
$this->api_domain = $shipping_method->getPlugin()->getConfiguration()['domain'];
} }
else { else {
throw new \InvalidArgumentException('The object must be an instance of \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface, but null occurred.'); throw new \InvalidArgumentException('The object must be an instance of \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface, but null occurred.');
...@@ -94,24 +120,19 @@ class LokoClient implements LokoClientInterface { ...@@ -94,24 +120,19 @@ class LokoClient implements LokoClientInterface {
* @throws \GuzzleHttp\Exception\GuzzleException * @throws \GuzzleHttp\Exception\GuzzleException
*/ */
protected function apiBaseRequest(string $method, string $apiPath, array $options = []): ResponseInterface { protected function apiBaseRequest(string $method, string $apiPath, array $options = []): ResponseInterface {
if (empty($this->shipmentMethod)) { if (empty($this->api_domain) || empty($this->api_token)) {
throw new \InvalidArgumentException('You need to set a LokoShipment shipping method.'); throw new \InvalidArgumentException('You need to configure a LokoShipment shipping method.');
} }
$shipment_configuration = $this->shipmentMethod->getConfiguration();
$base_url = $shipment_configuration['domain'];
$token_key = $shipment_configuration['loko_token'];
$options = array_merge_recursive($options, [ $options = array_merge_recursive($options, [
'headers' => [ 'headers' => [
'Authorization' => 'Bearer ' . $token_key, 'Authorization' => 'Bearer ' . $this->api_token,
'accept' => 'text/plain', 'accept' => 'text/plain',
], ],
'http_errors' => FALSE, 'http_errors' => FALSE,
]); ]);
return $this->httpClient->request($method, $base_url . $apiPath, $options); return $this->httpClient->request($method, $this->api_domain . $apiPath, $options);
} }
/** /**
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Drupal\commerce_loko; namespace Drupal\commerce_loko;
use Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface; use Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface as PluginEntityShippingMethodInterface;
/** /**
* Provides an interface for Loko Client Service. * Provides an interface for Loko Client Service.
...@@ -15,7 +15,7 @@ interface LokoClientInterface { ...@@ -15,7 +15,7 @@ interface LokoClientInterface {
* @param \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface $shipping_method * @param \Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodInterface $shipping_method
* The shipping method plugin. * The shipping method plugin.
*/ */
public function setShipment(ShippingMethodInterface $shipping_method): void; public function setShipmentByPlugin(PluginEntityShippingMethodInterface $shipping_method): void;
/** /**
* Set the shipping method plugin by id. * Set the shipping method plugin by id.
...@@ -23,7 +23,7 @@ interface LokoClientInterface { ...@@ -23,7 +23,7 @@ interface LokoClientInterface {
* @param string $id * @param string $id
* The ID of the shipping method plugin. * The ID of the shipping method plugin.
*/ */
public function setShipmentById(string $id): void; public function setShipmentByEntityId(string $id): void;
/** /**
* Validate the current API key. * Validate the current API key.
...@@ -36,7 +36,7 @@ interface LokoClientInterface { ...@@ -36,7 +36,7 @@ interface LokoClientInterface {
public function validateToken(): bool; public function validateToken(): bool;
/** /**
* Method for gets the all parcel terminals. * Method for gets the all parcel terminals objects.
* *
* @return array|false * @return array|false
* Gets the all parcel terminals. * Gets the all parcel terminals.
......
...@@ -12,6 +12,7 @@ use Drupal\commerce_shipping\ShippingService; ...@@ -12,6 +12,7 @@ use Drupal\commerce_shipping\ShippingService;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\state_machine\WorkflowManagerInterface; use Drupal\state_machine\WorkflowManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\TranslatableMarkup;
use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
...@@ -166,6 +167,8 @@ class LokoShipment extends ShippingMethodBase { ...@@ -166,6 +167,8 @@ class LokoShipment extends ShippingMethodBase {
/** /**
* Validate Loko API token. * Validate Loko API token.
*
* @throws GuzzleException
*/ */
public function validationToken(&$element, FormStateInterface $form_state, &$complete_form) { public function validationToken(&$element, FormStateInterface $form_state, &$complete_form) {
$this->setConfiguration([ $this->setConfiguration([
...@@ -176,9 +179,9 @@ class LokoShipment extends ShippingMethodBase { ...@@ -176,9 +179,9 @@ class LokoShipment extends ShippingMethodBase {
), ),
]); ]);
$this->setConfiguration(['loko_token' => $element['#value']]); $this->setConfiguration(['loko_token' => $element['#value']]);
$this->lokoClient->setShipment($this); $this->lokoClient->setShipmentByPlugin($this);
if (!$this->lokoClient->validation()) { if (!$this->lokoClient->validateToken()) {
$form_state->setError($element, new TranslatableMarkup('Token is invalid.')); $form_state->setError($element, new TranslatableMarkup('Token is invalid.'));
} }
} }
......
<?php
namespace Drupal\commerce_loko\Plugin\Field\FieldFormatter;
use Drupal\commerce_loko\LokoClientInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the Loko formatter.
*
* @FieldFormatter(
* id = "commerce_loko_formatter",
* label = @Translation("LokoElement"),
* field_types = {
* "commerce_loko_item"
* }
* )
*/
class LokoFormatter extends FormatterBase {
/**
* Loko client definition.
*
* @var \Drupal\commerce_loko\LokoClientInterface
*/
protected LokoClientInterface $lokoClient;
/**
* Constructs a LokoFormatter object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\commerce_loko\LokoClientInterface $loko_client
* The Loko API client.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, LokoClientInterface $loko_client) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->lokoClient = $loko_client;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('commerce_loko.client')
);
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
foreach ($items as $delta => $item) {
$elements[$delta]['parcel_terminal_id'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#value' => $item->parcel_terminal_id,
'#attributes' => [
'class' => ['parcel-terminal-id'],
],
];
}
// @todo Load about details about terminal from API.
// @todo Show terminal name instead of id.
return $elements;
}
}
<?php
namespace Drupal\commerce_loko\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the Loko field type.
*
* @FieldType(
* id = "commerce_loko_item",
* label = @Translation("Loko Shipment"),
* category = @Translation("Address"),
* description = @Translation("Field containing Loko postomate"),
* default_widget = "commerce_loko_widget",
* default_formatter = "commerce_loko_formatter"
* )
*/
class LokoItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return [
'shipping_method' => 'no_shipment_method_configured',
] + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
return NULL;
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['parcel_terminal_name'] = DataDefinition::create('string')
->setLabel(t('Parcel terminal'));
$properties['parcel_terminal_id'] = DataDefinition::create('string')
->setLabel(t('Parcel terminal ID'));
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = [
'columns' => [
'parcel_terminal_name' => [
'description' => 'Parcel terminal name',
'type' => 'varchar',
'length' => 128,
],
'parcel_terminal_id' => [
'description' => 'Parcel terminal ID',
'type' => 'varchar',
'length' => 36,
],
],
];
return $schema;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
$terminal_id = $this->get('parcel_terminal_id')->getValue();
$terminal_name = $this->get('parcel_terminal_name')->getValue();
return $terminal_id === NULL || $terminal_name === '';
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
// @todo Implement ContainerInjectionInterface.
$shipment_type_storage = \Drupal::entityTypeManager()->getStorage('commerce_shipping_method');
$shipments = $shipment_type_storage->loadByProperties(["plugin" => "loko_shipment"]);
$options = [];
foreach ($shipments as $shipment) {
$options[$shipment->id()] = $shipment->getName();
}
if (!$options) {
$options['no_shipment_method_configured'] = $this->t('Please create Loko shipment method for this field usage.');
}
$element['shipping_method'] = [
'#type' => 'select',
'#required' => TRUE,
'#options' => $options,
'#title' => $this->t('Select Loko shipment method'),
'#description' => $this->t('The shipment method to be used to get a configured API token.'),
'#default_value' => $this->getSetting('shipping_method'),
'#element_validate' => [[static::class, 'validateShipmentMethod']],
];
return $element;
}
/**
* Element validate callback for shipment method selection.
*
* @param array $element
* An associative array containing the properties and children of the
* generic form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form for the form this element belongs to.
*
* @see \Drupal\Core\Render\Element\FormElement::processPattern()
*/
public static function validateShipmentMethod(array $element, FormStateInterface $form_state) {
if ($form_state->getUserInput()['settings']['shipping_method'] === "no_shipment_method_configured") {
$form_state->setError($element, new TranslatableMarkup('Please create Loko shipment method for this field usage.'));
}
}
}
<?php
namespace Drupal\commerce_loko\Plugin\Field\FieldWidget;
use Drupal\commerce_loko\LokoClientInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the Loko widget.
*
* @FieldWidget(
* id = "commerce_loko_widget",
* module = "commerce_loko",
* label = @Translation("Loko Shipment"),
* field_types = {
* "commerce_loko_item"
* }
* )
*/
class LokoWidget extends WidgetBase {
/**
* Loko client definition.
*
* @var \Drupal\commerce_loko\LokoClientInterface
*/
protected LokoClientInterface $lokoClient;
/**
* Constructs a LokoWidget object.
*
* @param string $plugin_id
* The plugin_id for the widget.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the widget is associated.
* @param array $settings
* The widget settings.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\commerce_loko\LokoClientInterface $loko_client
* The Loko client.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, LokoClientInterface $loko_client) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->lokoClient = $loko_client;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['third_party_settings'],
$container->get('commerce_loko.client')
);
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
if ($items[$delta]->isEmpty()) {
$items[$delta]->parcel_terminal_name = NULL;
$items[$delta]->parcel_terminal_id = NULL;
}
return [
'#type' => 'address_loko',
'#shipment_method' => $this->getFieldSettings()['shipping_method'],
'#default_value' => $items[$delta]->getValue(),
] + $element;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment