180 lines
4.9 KiB
PHP
Executable File
180 lines
4.9 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\ApiBundle\Helper;
|
|
|
|
use Mautic\CoreBundle\Helper\CsvHelper;
|
|
|
|
class BatchIdToEntityHelper
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $ids = [];
|
|
|
|
private array $originalKeys = [];
|
|
|
|
private array $errors = [];
|
|
|
|
private bool $isAssociative = false;
|
|
|
|
/**
|
|
* @param string $idKey
|
|
*/
|
|
public function __construct(
|
|
array $parameters,
|
|
private $idKey = 'id',
|
|
) {
|
|
$this->extractIds($parameters);
|
|
}
|
|
|
|
public function hasIds(): bool
|
|
{
|
|
return !empty($this->ids);
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getIds()
|
|
{
|
|
return $this->ids;
|
|
}
|
|
|
|
public function hasErrors(): bool
|
|
{
|
|
return !empty($this->errors);
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getErrors()
|
|
{
|
|
return $this->errors;
|
|
}
|
|
|
|
/**
|
|
* Reorder the entities based on the original keys
|
|
* BC allowed a request to have associative keys (don't ask why; yes it's terrible implementation but we're keeping BC here)
|
|
* The issue this solves is the response should match the format given by the request. If the request had associative keys, the response
|
|
* will return with associative keys (json object). If the request was a sequential numeric array starting with 0, the response will
|
|
* be a simple array (json array).
|
|
*/
|
|
public function orderByOriginalKey(array $entities): array
|
|
{
|
|
if (!$this->isAssociative) {
|
|
// The request was keyed by sequential numbers starting with 0
|
|
return array_values($entities);
|
|
}
|
|
|
|
// Ensure entities are keyed by ID in order to find the original keys assuming that some entities are missing if the ID was not found
|
|
$entitiesKeyedById = [];
|
|
foreach ($entities as $entity) {
|
|
$entitiesKeyedById[$entity->getId()] = $entity;
|
|
}
|
|
|
|
$orderedEntities = [];
|
|
foreach ($this->ids as $key => $id) {
|
|
if (!isset($entitiesKeyedById[$id])) {
|
|
$hasPreviousId = array_filter(
|
|
$entities,
|
|
fn ($entity) => $id == $entity->getPreviousId()
|
|
);
|
|
|
|
if ($hasPreviousId) {
|
|
$orderedEntities[$key] = array_shift($hasPreviousId);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$originalKey = $this->originalKeys[$key];
|
|
$orderedEntities[$originalKey] = $entitiesKeyedById[$id];
|
|
}
|
|
|
|
return $orderedEntities;
|
|
}
|
|
|
|
private function extractIds(array $parameters): void
|
|
{
|
|
$this->ids = [];
|
|
|
|
if (isset($parameters['ids'])) {
|
|
$this->extractIdsFromIdKey($parameters['ids']);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->extractIdsFromParams($parameters);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $ids
|
|
*/
|
|
private function extractIdsFromIdKey($ids): void
|
|
{
|
|
// ['ids' => [1,2,3]]
|
|
if (is_array($ids)) {
|
|
$this->isAssociative = $this->isAssociativeArray($ids);
|
|
$this->ids = array_values($ids);
|
|
$this->originalKeys = array_keys($ids);
|
|
|
|
return;
|
|
}
|
|
|
|
// ['ids' => '1,2,3'] OR ['ids' => '1']
|
|
if (str_contains($ids, ',') || is_numeric($ids)) {
|
|
$this->ids = CsvHelper::strGetCsv($ids);
|
|
$this->originalKeys = array_keys($this->ids);
|
|
$this->isAssociative = false;
|
|
|
|
return;
|
|
}
|
|
|
|
// Couldn't parse the 'ids' key; not throwing an exception in order to keep BC with
|
|
// the old CommonApiController code and the use of a foreach in extractIdsFromParams
|
|
$this->errors[] = 'mautic.api.call.id_missing';
|
|
}
|
|
|
|
private function extractIdsFromParams(array $parameters): void
|
|
{
|
|
$this->isAssociative = $this->isAssociativeArray($parameters);
|
|
$this->originalKeys = array_keys($parameters);
|
|
|
|
// [1,2,3]
|
|
$firstKey = array_key_first($parameters);
|
|
if (!is_array($parameters[$firstKey])) {
|
|
$this->ids = array_values($parameters);
|
|
|
|
return;
|
|
}
|
|
|
|
// [ ['id' => 1, 'foo' => 'bar'], ['id' => 2, 'bar' => 'foo'] ]
|
|
foreach ($parameters as $key => $params) {
|
|
// Missing id column key in the array; terrible but keep BC
|
|
if (!isset($params[$this->idKey])) {
|
|
$this->errors[$key] = 'mautic.api.call.id_missing';
|
|
|
|
continue;
|
|
}
|
|
|
|
$this->ids[] = $params[$this->idKey];
|
|
}
|
|
}
|
|
|
|
private function isAssociativeArray(array $array): bool
|
|
{
|
|
if (empty($array)) {
|
|
return false;
|
|
}
|
|
$firstKey = array_key_first($array);
|
|
|
|
return array_keys($array) !== range(0, count($array) - 1) && 0 !== $firstKey;
|
|
}
|
|
|
|
public function setIsAssociative(bool $isAssociative): void
|
|
{
|
|
$this->isAssociative = $isAssociative;
|
|
}
|
|
}
|