Spaces:
No application file
No application file
namespace Mautic\ApiBundle\Helper; | |
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])) { | |
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 = str_getcsv($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; | |
} | |
} | |