src/Dreamscape/ResellerApiSdk/DataObject/Collection.php line 246

  1. <?php
  2. namespace App\Dreamscape\ResellerApiSdk\DataObject;
  3. use App\Dreamscape\ResellerApiSdk\DataObject\Structure\DataType;
  4. use InvalidArgumentException;
  5. use Iterator;
  6. use ArrayAccess;
  7. use Countable;
  8. /**
  9.  * Class for storing the collection of data-objects and primitives.
  10.  *
  11.  * @property-read string $type
  12.  * @property-read int $totalItems
  13.  * @property-read int $totalPages
  14.  * @property-read int $currentPage
  15.  *
  16.  * @copyright Dreamscape Networks International Pte Ltd https://www.dreamscapenetworks.com
  17.  */
  18. final class Collection implements IteratorArrayAccessCountable
  19. {
  20.     private array $items = [];
  21.     private string $type;
  22.     private bool $is_type_primitive false;
  23.     private int $total_items 0;
  24.     private int $total_pages 1;
  25.     private int $current_page 1;
  26.     /**
  27.      * Constructor.
  28.      *
  29.      * @param string $type
  30.      *
  31.      * @throws InvalidArgumentException
  32.      */
  33.     private function __construct(string $type)
  34.     {
  35.         if (empty($type)) {
  36.             throw new InvalidArgumentException('The argument $type must be not empty string');
  37.         }
  38.         if (DataType::isValid($type)) {
  39.             $this->is_type_primitive true;
  40.         } elseif (!in_array(AbstractDataObject::class, class_parents($type))) {
  41.             throw new InvalidArgumentException('The class must be heir of \'' AbstractDataObject::class . '\'');
  42.         }
  43.         $this->type $type;
  44.     }
  45.     /**
  46.      * Created the instance of collection of the specified type.
  47.      *
  48.      * @param string $type
  49.      * @param int $total_items
  50.      * @param int $total_pages
  51.      * @param int $current_page
  52.      *
  53.      * @return self
  54.      *
  55.      * @throws InvalidArgumentException
  56.      */
  57.     public static function build(
  58.         string $type,
  59.         int $total_items 0,
  60.         int $total_pages 1,
  61.         int $current_page 1
  62.     ): self {
  63.         $object = new self($type);
  64.         $object->total_items $total_items;
  65.         $object->total_pages $total_pages;
  66.         $object->current_page $current_page;
  67.         return $object;
  68.     }
  69.     /**
  70.      * @param string $name
  71.      *
  72.      * @return string
  73.      *
  74.      * @throws InvalidArgumentException
  75.      */
  76.     public function __get(string $name)
  77.     {
  78.         if (empty($name)) {
  79.             throw new InvalidArgumentException('The argument $name must be not empty string');
  80.         }
  81.         switch ($name) {
  82.             case 'type':
  83.                 return $this->type;
  84.             case 'totalItems':
  85.                 return $this->total_items;
  86.             case 'totalPages':
  87.                 return $this->total_pages;
  88.             case 'currentPage':
  89.                 return $this->current_page;
  90.         }
  91.         throw new InvalidArgumentException('The property \'' $name '\' does not exist');
  92.     }
  93.     /**
  94.      * @param array $items
  95.      *
  96.      * @return self
  97.      *
  98.      * @throws InvalidArgumentException
  99.      */
  100.     public function import(array $items): self
  101.     {
  102.         foreach ($items as $item) {
  103.             $this->add($item);
  104.         }
  105.         return $this;
  106.     }
  107.     /**
  108.      * @param mixed $item
  109.      *
  110.      * @return self
  111.      *
  112.      * @throws InvalidArgumentException
  113.      */
  114.     public function add($item): self
  115.     {
  116.         $this->checkItemApplicable($item);
  117.         $this->items[] = $this->is_type_primitive DataType::convert($this->type$item) : $item;
  118.         return $this;
  119.     }
  120.     /**
  121.      * Checks is the provided item applicable for this collection
  122.      * according to its type.
  123.      *
  124.      * @param mixed $item
  125.      *
  126.      * @throws InvalidArgumentException
  127.      */
  128.     private function checkItemApplicable($item)
  129.     {
  130.         if ($this->is_type_primitive) {
  131.             if (!DataType::isValueApplicable($this->type$item)) {
  132.                 throw new InvalidArgumentException(
  133.                     'Only items of type \'' $this->type '\' can be ' .
  134.                     'added, \'' DataType::getType($item) . '\' provided'
  135.                 );
  136.             }
  137.         } elseif (!$item instanceof $this->type) {
  138.             throw new InvalidArgumentException(
  139.                 'Only instances of \'' $this->type '\' class ' .
  140.                 'can be added, \'' DataType::getType($item) . '\' provided'
  141.             );
  142.         }
  143.     }
  144.     /**
  145.      * @return array
  146.      */
  147.     public function toArray(): array
  148.     {
  149.         return array_map(function ($item) {
  150.             /** @var AbstractDataObject $item */
  151.             return $this->is_type_primitive $item $item->toArray();
  152.         }, $this->items);
  153.     }
  154.     /**
  155.      * @return array
  156.      */
  157.     public function toRequestArray(): array
  158.     {
  159.         return array_map(function ($item) {
  160.             return $this->is_type_primitive $item $item->toRequestArray();
  161.         }, $this->items);
  162.     }
  163.     #region Interface Iterator
  164.     /**
  165.      * @inheritDoc
  166.      */
  167.     public function current()
  168.     {
  169.         return current($this->items);
  170.     }
  171.     /**
  172.      * @inheritDoc
  173.      */
  174.     public function next()
  175.     {
  176.         next($this->items);
  177.     }
  178.     /**
  179.      * @inheritDoc
  180.      */
  181.     public function key(): int
  182.     {
  183.         return key($this->items);
  184.     }
  185.     /**
  186.      * @inheritDoc
  187.      */
  188.     public function valid(): bool
  189.     {
  190.         return current($this->items) !== false;
  191.     }
  192.     /**
  193.      * @inheritDoc
  194.      */
  195.     public function rewind()
  196.     {
  197.         reset($this->items);
  198.     }
  199.     #endregion
  200.     #region Interface ArrayAccess
  201.     /**
  202.      * @inheritDoc
  203.      */
  204.     public function offsetExists($offset): bool
  205.     {
  206.         return isset($this->items[$offset]);
  207.     }
  208.     /**
  209.      * @inheritDoc
  210.      *
  211.      * @throws InvalidArgumentException
  212.      */
  213.     public function offsetGet($offset)
  214.     {
  215.         if (!is_integer($offset)) {
  216.             throw new InvalidArgumentException(
  217.                 'Offset must be a integer type, \'' DataType::getType($offset) . '\' given'
  218.             );
  219.         }
  220.         return $this->items[$offset] ?? null;
  221.     }
  222.     /**
  223.      * @inheritDoc
  224.      *
  225.      * @throws InvalidArgumentException
  226.      */
  227.     public function offsetSet($offset$value)
  228.     {
  229.         $this->checkItemApplicable($value);
  230.         if (is_null($offset)) {
  231.             $this->items[] = $value;
  232.             return;
  233.         }
  234.         if (!is_integer($offset)) {
  235.             throw new InvalidArgumentException(
  236.                 'Offset must be a integer type, \'' DataType::getType($offset) . '\' given'
  237.             );
  238.         }
  239.         if ($offset >= count($this->items)) {
  240.             throw new InvalidArgumentException('Array offset out of bounds');
  241.         }
  242.         $this->items[$offset] = $value;
  243.     }
  244.     /**
  245.      * @inheritDoc
  246.      *
  247.      * @throws InvalidArgumentException
  248.      */
  249.     public function offsetUnset($offset)
  250.     {
  251.         if (!is_integer($offset)) {
  252.             throw new InvalidArgumentException(
  253.                 'Offset must be a integer type, \'' DataType::getType($offset) . '\' given'
  254.             );
  255.         }
  256.         unset($this->items[$offset]);
  257.         // to order elements of array
  258.         $this->items array_values($this->items);
  259.     }
  260.     #endregion
  261.     #region Interface Countable
  262.     /**
  263.      * @inheritDoc
  264.      */
  265.     public function count(): int
  266.     {
  267.         return count($this->items);
  268.     }
  269.     #endregion
  270. }