swisnl /
json-api-client
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Swis\JsonApi\Client\Parsers; |
||
| 6 | |||
| 7 | use Swis\JsonApi\Client\Collection; |
||
| 8 | use Swis\JsonApi\Client\Exceptions\ValidationException; |
||
| 9 | use Swis\JsonApi\Client\Interfaces\DataInterface; |
||
| 10 | use Swis\JsonApi\Client\Interfaces\ItemInterface; |
||
| 11 | use Swis\JsonApi\Client\Interfaces\TypeMapperInterface; |
||
| 12 | use Swis\JsonApi\Client\Item; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * @internal |
||
| 16 | */ |
||
| 17 | class ItemParser |
||
| 18 | { |
||
| 19 | private TypeMapperInterface $typeMapper; |
||
| 20 | |||
| 21 | private LinksParser $linksParser; |
||
| 22 | |||
| 23 | private MetaParser $metaParser; |
||
| 24 | |||
| 25 | 444 | public function __construct(TypeMapperInterface $typeMapper, LinksParser $linksParser, MetaParser $metaParser) |
|
| 26 | { |
||
| 27 | 444 | $this->typeMapper = $typeMapper; |
|
| 28 | 444 | $this->linksParser = $linksParser; |
|
| 29 | 444 | $this->metaParser = $metaParser; |
|
| 30 | 222 | } |
|
| 31 | |||
| 32 | /** |
||
| 33 | * @param mixed $data |
||
| 34 | */ |
||
| 35 | 328 | public function parse($data): ItemInterface |
|
| 36 | { |
||
| 37 | 328 | if (! is_object($data)) { |
|
| 38 | 24 | throw new ValidationException(sprintf('Resource MUST be an object, "%s" given.', gettype($data))); |
|
| 39 | } |
||
| 40 | 304 | if (! property_exists($data, 'type')) { |
|
| 41 | 4 | throw new ValidationException('Resource object MUST contain a type.'); |
|
| 42 | } |
||
| 43 | 300 | if (! property_exists($data, 'id')) { |
|
| 44 | 4 | throw new ValidationException('Resource object MUST contain an id.'); |
|
| 45 | } |
||
| 46 | 296 | if (! is_string($data->type)) { |
|
| 47 | 24 | throw new ValidationException(sprintf('Resource property "type" MUST be a string, "%s" given.', gettype($data->type))); |
|
| 48 | } |
||
| 49 | 272 | if (! is_string($data->id) && ! is_numeric($data->id)) { |
|
| 50 | 16 | throw new ValidationException(sprintf('Resource property "id" MUST be a string, "%s" given.', gettype($data->id))); |
|
| 51 | } |
||
| 52 | 256 | if (property_exists($data, 'attributes')) { |
|
| 53 | 132 | if (! is_object($data->attributes)) { |
|
| 54 | 24 | throw new ValidationException(sprintf('Resource property "attributes" MUST be an object, "%s" given.', gettype($data->attributes))); |
|
| 55 | } |
||
| 56 | 108 | if (property_exists($data->attributes, 'type') || property_exists($data->attributes, 'id') || property_exists($data->attributes, 'relationships') || property_exists($data->attributes, 'links')) { |
|
| 57 | 16 | throw new ValidationException('These properties are not allowed in attributes: `type`, `id`, `relationships`, `links`.'); |
|
| 58 | } |
||
| 59 | } |
||
| 60 | |||
| 61 | 216 | $item = $this->getItemInstance($data->type); |
|
| 62 | 216 | $item->setId((string) $data->id); |
|
| 63 | |||
| 64 | 216 | if (property_exists($data, 'attributes')) { |
|
| 65 | 92 | $item->fill((array) $data->attributes); |
|
| 66 | } |
||
| 67 | |||
| 68 | 216 | if (property_exists($data, 'relationships')) { |
|
| 69 | 200 | $this->setRelations($item, $data->relationships); |
|
| 70 | } |
||
| 71 | |||
| 72 | 88 | if (property_exists($data, 'links')) { |
|
| 73 | 52 | $item->setLinks($this->linksParser->parse($data->links, LinksParser::SOURCE_ITEM)); |
|
| 74 | } |
||
| 75 | |||
| 76 | 88 | if (property_exists($data, 'meta')) { |
|
| 77 | 56 | $item->setMeta($this->metaParser->parse($data->meta)); |
|
| 78 | } |
||
| 79 | |||
| 80 | 88 | return $item; |
|
| 81 | } |
||
| 82 | |||
| 83 | 216 | private function getItemInstance(string $type): ItemInterface |
|
| 84 | { |
||
| 85 | 216 | if ($this->typeMapper->hasMapping($type)) { |
|
| 86 | 192 | return $this->typeMapper->getMapping($type); |
|
| 87 | } |
||
| 88 | |||
| 89 | 24 | return (new Item)->setType($type); |
|
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @param mixed $data |
||
| 94 | */ |
||
| 95 | 200 | private function setRelations(ItemInterface $item, $data): void |
|
| 96 | { |
||
| 97 | 200 | if (! is_object($data)) { |
|
| 98 | 24 | throw new ValidationException(sprintf('Resource property "relationships" MUST be an object, "%s" given.', gettype($data))); |
|
| 99 | } |
||
| 100 | 176 | if (property_exists($data, 'type') || property_exists($data, 'id')) { |
|
| 101 | 8 | throw new ValidationException('These properties are not allowed in relationships: `type`, `id`.'); |
|
| 102 | } |
||
| 103 | |||
| 104 | 168 | foreach ($data as $name => $relationship) { |
|
| 105 | 168 | if ($item->hasAttribute($name)) { |
|
| 106 | 4 | throw new ValidationException(sprintf('Relationship "%s" cannot be set because it already exists in Resource object.', $name)); |
|
| 107 | } |
||
| 108 | 164 | if (! is_object($relationship)) { |
|
| 109 | 24 | throw new ValidationException(sprintf('Relationship MUST be an object, "%s" given.', gettype($relationship))); |
|
| 110 | } |
||
| 111 | 140 | if (! property_exists($relationship, 'links') && ! property_exists($relationship, 'data') && ! property_exists($relationship, 'meta')) { |
|
| 112 | 4 | throw new ValidationException('Relationship object MUST contain at least one of the following properties: `links`, `data`, `meta`.'); |
|
| 113 | } |
||
| 114 | |||
| 115 | 136 | $value = false; |
|
| 116 | 136 | if (property_exists($relationship, 'data')) { |
|
| 117 | 136 | $value = null; |
|
| 118 | 136 | if ($relationship->data !== null) { |
|
| 119 | 132 | $value = $this->parseRelationshipData($relationship->data); |
|
| 120 | } |
||
| 121 | } |
||
| 122 | |||
| 123 | 72 | $links = null; |
|
| 124 | 72 | if (property_exists($relationship, 'links')) { |
|
| 125 | 52 | $links = $this->linksParser->parse($relationship->links, LinksParser::SOURCE_RELATIONSHIP); |
|
| 126 | } |
||
| 127 | |||
| 128 | 72 | $meta = null; |
|
| 129 | 72 | if (property_exists($relationship, 'meta')) { |
|
| 130 | 52 | $meta = $this->metaParser->parse($relationship->meta); |
|
| 131 | } |
||
| 132 | |||
| 133 | 72 | $item->setRelation($name, $value, $links, $meta); |
|
| 134 | } |
||
| 135 | 36 | } |
|
| 136 | |||
| 137 | /** |
||
| 138 | * @param mixed $data |
||
| 139 | * |
||
| 140 | * @throws \InvalidArgumentException |
||
| 141 | */ |
||
| 142 | 132 | private function parseRelationshipData($data): DataInterface |
|
| 143 | { |
||
| 144 | 132 | if (is_array($data)) { |
|
| 145 | 60 | return Collection::make($data) |
|
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 146 | 60 | ->map(fn ($identifier) => $this->parseRelationshipData($identifier)); |
|
| 147 | } |
||
| 148 | |||
| 149 | 128 | if (! is_object($data)) { |
|
| 150 | 16 | throw new ValidationException(sprintf('ResourceIdentifier MUST be an object, "%s" given.', gettype($data))); |
|
| 151 | } |
||
| 152 | 112 | if (! property_exists($data, 'type')) { |
|
| 153 | 4 | throw new ValidationException('ResourceIdentifier object MUST contain a type.'); |
|
| 154 | } |
||
| 155 | 108 | if (! property_exists($data, 'id')) { |
|
| 156 | 4 | throw new ValidationException('ResourceIdentifier object MUST contain an id.'); |
|
| 157 | } |
||
| 158 | 104 | if (! is_string($data->type)) { |
|
| 159 | 24 | throw new ValidationException(sprintf('ResourceIdentifier property "type" MUST be a string, "%s" given.', gettype($data->type))); |
|
| 160 | } |
||
| 161 | 80 | if (! is_string($data->id) && ! is_numeric($data->id)) { |
|
| 162 | 16 | throw new ValidationException(sprintf('ResourceIdentifier property "id" MUST be a string, "%s" given.', gettype($data->id))); |
|
| 163 | } |
||
| 164 | |||
| 165 | 64 | $item = $this->getItemInstance($data->type)->setId((string) $data->id); |
|
| 166 | 64 | if (property_exists($data, 'meta')) { |
|
| 167 | 56 | $item->setMeta($this->metaParser->parse($data->meta)); |
|
| 168 | } |
||
| 169 | |||
| 170 | 64 | return $item; |
|
| 171 | } |
||
| 172 | } |
||
| 173 |