xero-8.x-2.x-dev/src/XeroItemManager.php
src/XeroItemManager.php
<?php
namespace Drupal\xero;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\xero\Plugin\DataType\XeroItemList;
use Drupal\xero\TypedData\XeroComplexItemInterface;
/**
* Provides a service for managing Xero items.
*
* This is a simplifying wrapper around Xero queries.
*/
class XeroItemManager implements XeroItemManagerInterface {
use StringTranslationTrait;
/**
* The typed data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
*/
protected $typedDataManager;
/**
* A Xero query factory.
*
* @var \Drupal\xero\XeroQueryFactory
*/
protected $xeroQueryFactory;
/**
* A logger channel for recording errors.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* Whether or not to throw errors generated when the query is executed.
*
* @var bool
*/
protected $throwErrors = FALSE;
/**
* Constructs a new XeroItemManager object.
*
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
* The typed data manager.
* @param \Drupal\xero\XeroQueryFactory $xero_query_factory
* The Xero query factory.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The Drupal logger factory.
*/
public function __construct(TypedDataManagerInterface $typed_data_manager, XeroQueryFactory $xero_query_factory, LoggerChannelFactoryInterface $logger_factory) {
$this->typedDataManager = $typed_data_manager;
$this->xeroQueryFactory = $xero_query_factory;
$this->logger = $logger_factory->get('xero');
}
/**
* {@inheritDoc}
*/
public function createItem(XeroComplexItemInterface $item): bool|XeroComplexItemInterface {
return $this->sendItem($item, 'put');
}
/**
* {@inheritDoc}
*/
public function updateItem(XeroComplexItemInterface $item): bool|XeroComplexItemInterface {
return $this->sendItem($item, 'post');
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
* @throws \Throwable
*/
public function loadItem($type, $guid): bool|XeroComplexItemInterface {
$xeroQuery = $this->xeroQueryFactory->get();
$xeroQuery->setType($type)
->setId($guid)
->throwErrors($this->throwErrors);
$item_list = $xeroQuery->execute();
if ($item_list instanceof XeroItemList && count($item_list) === 1) {
$result = $item_list->get(0);
}
else {
$result = FALSE;
$this->logger->error('Could not load @type @guid from Xero', [
'@type' => $type,
'@guid' => $guid,
]);
}
return $result;
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
* @throws \Throwable
*/
public function reloadItem(XeroComplexItemInterface $item): bool|XeroComplexItemInterface {
$type = $item->getDataDefinition()->getDataType();
$guidName = $item->getXeroProperty('guid_name');
if ($guidName) {
$guid = $item->get($guidName)->getString();
}
else {
throw new \InvalidArgumentException($this->t('Xero data type @type does not have an ID property', [
'@type' => $item->getName(),
]));
}
return $this->loadItem($type, $guid);
}
/**
* Sends a single item to Xero.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* The Xero item to update.
* @param string $method
* The HTTP method to use.
*
* @return \Drupal\xero\TypedData\XeroComplexItemInterface|bool
* A typed data representation of the Xero item, or FALSE if unsuccessful.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
* @throws \Throwable
*/
protected function sendItem(XeroComplexItemInterface $item, string $method): bool|XeroComplexItemInterface {
$type = $item->getDataDefinition()->getDataType();
/** @var \Drupal\Core\TypedData\ListDataDefinition $list_definition */
$list_definition = $this->typedDataManager->createListDataDefinition($type);
$items = $this->typedDataManager->create($list_definition, []);
$items->offsetSet(0, $item);
// Do the remote creation.
$xeroQuery = $this->xeroQueryFactory->get();
$xeroQuery->setType($type)
->setData($items)
->setMethod($method)
->throwErrors($this->throwErrors);
// If the method is post, then the id must be set or XeroQuery
// will change the method to put.
$guidName = $item->getXeroProperty('guid_name');
$guid = $item->get($guidName)->getString();
if (!empty($guid)) {
$xeroQuery->setId($guid);
}
/** @var \Drupal\xero\Plugin\DataType\XeroItemList $result */
$result = $xeroQuery->execute();
if ($result instanceof XeroItemList && $result->count() > 0) {
return $result->get(0);
}
$this->logger->error(
'Sending @type data by @method to Xero failed for this data:\n @item_data', [
'@type' => $type,
'@method' => $method,
'@item_data' => print_r($this->preparePropertiesAsStringArray($item), TRUE),
]
);
return FALSE;
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function findItem($type, array $conditions, $unique = FALSE): bool|XeroComplexItemInterface|null {
$xeroQuery = $this->xeroQueryFactory->get();
$xeroQuery->setType($type)
->throwErrors($this->throwErrors);
foreach ($conditions as $condition) {
// The third argument is optional.
if (isset($condition[2])) {
$xeroQuery->addCondition($condition[0], $condition[1], $condition[2]);
}
else {
$xeroQuery->addCondition($condition[0], $condition[1]);
}
}
$item_list = $xeroQuery->execute();
// If there is a single matching item, return it.
// If there are multiple matching items, return the first one only if
// the match is not required to be unique.
if ($item_list && $item_list instanceof XeroItemList) {
$result = NULL;
$count = count($item_list);
if ($count !== 0 && ($unique === FALSE || $count === 1)) {
$result = $item_list->get(0);
}
}
else {
$result = FALSE;
$this->logger->error(
'Unexpected response when trying to find @type data on Xero with these conditions:\n @item_data', [
'@type' => $type,
'@conditions' => print_r($conditions, TRUE),
]
);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function throwErrors($shouldThrow = TRUE): XeroItemManagerInterface {
$this->throwErrors = $shouldThrow;
return $this;
}
/**
* Convert an item into a pretty-printable array of string properties.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* The item to prepare a string array from.
*
* @return array
* An array of properties specified on the item, keyed by property name.
*
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
*/
protected function preparePropertiesAsStringArray(XeroComplexItemInterface $item) {
$properties = $item->getSpecifiedProperties();
$result = [];
foreach ($properties as $name => $property) {
$result[$name] = $item->get($name)->getString();
}
return $result;
}
}
