Completed
Pull Request — master (#54)
by Jasper
14:39
created

DocumentParser::getDocument()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5.005

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 6
nop 1
dl 0
loc 28
ccs 16
cts 17
cp 0.9412
crap 5.005
rs 9.3888
c 0
b 0
f 0
1
<?php
2
3
namespace Swis\JsonApi\Client\Parsers;
4
5
use Art4\JsonApiClient\DocumentInterface as Art4JsonApiDocumentInterface;
6
use Art4\JsonApiClient\ResourceCollectionInterface;
7
use Art4\JsonApiClient\ResourceItemInterface;
8
use Art4\JsonApiClient\Utils\Manager as Art4JsonApiClientManager;
9
use Swis\JsonApi\Client\Collection;
10
use Swis\JsonApi\Client\CollectionDocument;
11
use Swis\JsonApi\Client\Document;
12
use Swis\JsonApi\Client\ErrorCollection;
13
use Swis\JsonApi\Client\Interfaces\DocumentInterface;
14
use Swis\JsonApi\Client\Interfaces\DocumentParserInterface;
15
use Swis\JsonApi\Client\Interfaces\ItemInterface;
16
use Swis\JsonApi\Client\Interfaces\ManyRelationInterface;
17
use Swis\JsonApi\Client\Interfaces\OneRelationInterface;
18
use Swis\JsonApi\Client\ItemDocument;
19
use Swis\JsonApi\Client\Jsonapi;
20
use Swis\JsonApi\Client\Links;
21
use Swis\JsonApi\Client\Meta;
22
23
class DocumentParser implements DocumentParserInterface
24
{
25
    /**
26
     * @var \Art4\JsonApiClient\Utils\Manager
27
     */
28
    private $manager;
29
30
    /**
31
     * @var \Swis\JsonApi\Client\Parsers\ItemParser
32
     */
33
    private $itemParser;
34
35
    /**
36
     * @var \Swis\JsonApi\Client\Parsers\CollectionParser
37
     */
38
    private $collectionParser;
39
40
    /**
41
     * @var \Swis\JsonApi\Client\Parsers\ErrorsParser
42
     */
43
    private $errorsParser;
44
45
    /**
46
     * @var \Swis\JsonApi\Client\Parsers\LinksParser
47
     */
48
    private $linksParser;
49
50
    /**
51
     * @var \Swis\JsonApi\Client\Parsers\JsonapiParser
52
     */
53
    private $jsonapiParser;
54
55
    /**
56
     * @var \Swis\JsonApi\Client\Parsers\MetaParser
57
     */
58
    private $metaParser;
59
60
    /**
61
     * @param \Art4\JsonApiClient\Utils\Manager             $manager
62
     * @param \Swis\JsonApi\Client\Parsers\ItemParser       $itemParser
63
     * @param \Swis\JsonApi\Client\Parsers\CollectionParser $collectionParser
64
     * @param \Swis\JsonApi\Client\Parsers\ErrorsParser     $errorsParser
65
     * @param \Swis\JsonApi\Client\Parsers\LinksParser      $linksParser
66
     * @param \Swis\JsonApi\Client\Parsers\JsonapiParser    $jsonapiParser
67
     * @param \Swis\JsonApi\Client\Parsers\MetaParser       $metaParser
68
     */
69 80
    public function __construct(
70
        Art4JsonApiClientManager $manager,
71
        ItemParser $itemParser,
72
        CollectionParser $collectionParser,
73
        ErrorsParser $errorsParser,
74
        LinksParser $linksParser,
75
        JsonapiParser $jsonapiParser,
76
        MetaParser $metaParser
77
    ) {
78 80
        $this->manager = $manager;
79 80
        $this->itemParser = $itemParser;
80 80
        $this->collectionParser = $collectionParser;
81 80
        $this->errorsParser = $errorsParser;
82 80
        $this->linksParser = $linksParser;
83 80
        $this->jsonapiParser = $jsonapiParser;
84 80
        $this->metaParser = $metaParser;
85 80
    }
86
87
    /**
88
     * @param string $json
89
     *
90
     * @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
91
     */
92 80
    public function parse(string $json): DocumentInterface
93
    {
94
        /** @var \Art4\JsonApiClient\DocumentInterface $jsonApiDocument */
95 80
        $jsonApiDocument = $this->manager->parse($json);
96
97 55
        return $this->getDocument($jsonApiDocument)
98 55
            ->setLinks($this->parseLinks($jsonApiDocument))
99 55
            ->setErrors($this->parseErrors($jsonApiDocument))
100 55
            ->setMeta($this->parseMeta($jsonApiDocument))
101 55
            ->setJsonapi($this->parseJsonapi($jsonApiDocument));
102
    }
103
104
    /**
105
     * @param \Art4\JsonApiClient\DocumentInterface $jsonApiDocument
106
     *
107
     * @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
108
     */
109 55
    private function getDocument(Art4JsonApiDocumentInterface $jsonApiDocument): DocumentInterface
110
    {
111 55
        if (!$jsonApiDocument->has('data')) {
112 10
            return new Document();
113
        }
114
115 45
        $data = $jsonApiDocument->get('data');
116
117 45
        if ($data instanceof ResourceItemInterface) {
118 15
            $document = (new ItemDocument())
119 15
                ->setData($this->itemParser->parse($data));
120 30
        } elseif ($data instanceof ResourceCollectionInterface) {
121 30
            $document = (new CollectionDocument())
122 30
                ->setData($this->collectionParser->parse($data));
123
        } else {
124
            throw new \DomainException('Document data is not a Collection or an Item');
125
        }
126
127 45
        if ($jsonApiDocument->has('included')) {
128 15
            $document->setIncluded($this->collectionParser->parse($jsonApiDocument->get('included')));
129
        }
130
131 45
        $allItems = Collection::wrap($document->getData())
132 45
            ->concat($document->getIncluded());
133
134 45
        $this->linkRelationships($allItems);
135
136 45
        return $document;
137
    }
138
139
    /**
140
     * @param \Swis\JsonApi\Client\Collection $items
141
     */
142 45
    private function linkRelationships(Collection $items): void
143
    {
144
        // N.B. We reverse the items to make sure the first item in the collection takes precedence
145 45
        $keyedItems = $items->reverse()->keyBy(
146 18
            function (ItemInterface $item) {
147 25
                return $this->getItemKey($item);
148 45
            }
149
        );
150
151 45
        $items->each(
152 18
            function (ItemInterface $item) use ($keyedItems) {
153 25
                foreach ($item->getRelations() as $name => $relation) {
154 10
                    if ($relation instanceof OneRelationInterface) {
155
                        /** @var \Swis\JsonApi\Client\Interfaces\ItemInterface $relatedItem */
156 5
                        $relatedItem = $relation->getIncluded();
157
158 5
                        $includedItem = $this->getItem($keyedItems, $relatedItem);
159 5
                        if ($includedItem !== null) {
160 5
                            $relation->associate($includedItem);
161
                        }
162 5
                    } elseif ($relation instanceof ManyRelationInterface) {
163
                        /** @var \Swis\JsonApi\Client\Collection $relatedCollection */
164 5
                        $relatedCollection = $relation->getIncluded();
165
166
                        /** @var \Swis\JsonApi\Client\Interfaces\ItemInterface $relatedItem */
167 5
                        foreach ($relatedCollection as $key => $relatedItem) {
168 5
                            $includedItem = $this->getItem($keyedItems, $relatedItem);
169 5
                            if ($includedItem !== null) {
170 6
                                $relatedCollection->put($key, $includedItem);
171
                            }
172
                        }
173
                    }
174
                }
175 45
            }
176
        );
177 45
    }
178
179
    /**
180
     * @param \Swis\JsonApi\Client\Collection               $included
181
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
182
     *
183
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface|null
184
     */
185 10
    private function getItem(Collection $included, ItemInterface $item): ?ItemInterface
186
    {
187 10
        return $included->get($this->getItemKey($item));
188
    }
189
190
    /**
191
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
192
     *
193
     * @return string
194
     */
195 25
    private function getItemKey(ItemInterface $item): string
196
    {
197 25
        return sprintf('%s:%s', $item->getType(), $item->getId());
198
    }
199
200
    /**
201
     * @param \Art4\JsonApiClient\DocumentInterface $document
202
     *
203
     * @return \Swis\JsonApi\Client\Links|null
204
     */
205 55
    private function parseLinks(Art4JsonApiDocumentInterface $document): ?Links
206
    {
207 55
        if (!$document->has('links')) {
208 50
            return null;
209
        }
210
211 5
        return $this->linksParser->parse($document->get('links')->asArray());
212
    }
213
214
    /**
215
     * @param \Art4\JsonApiClient\DocumentInterface $document
216
     *
217
     * @return \Swis\JsonApi\Client\ErrorCollection
218
     */
219 55
    private function parseErrors(Art4JsonApiDocumentInterface $document): ErrorCollection
220
    {
221 55
        if (!$document->has('errors')) {
222 50
            return new ErrorCollection();
223
        }
224
225 5
        return $this->errorsParser->parse($document->get('errors'));
226
    }
227
228
    /**
229
     * @param \Art4\JsonApiClient\DocumentInterface $document
230
     *
231
     * @return \Swis\JsonApi\Client\Meta|null
232
     */
233 55
    private function parseMeta(Art4JsonApiDocumentInterface $document): ?Meta
234
    {
235 55
        if (!$document->has('meta')) {
236 45
            return null;
237
        }
238
239 10
        return $this->metaParser->parse($document->get('meta'));
240
    }
241
242
    /**
243
     * @param \Art4\JsonApiClient\DocumentInterface $document
244
     *
245
     * @return \Swis\JsonApi\Client\Jsonapi|null
246
     */
247 55
    private function parseJsonapi(Art4JsonApiDocumentInterface $document): ?Jsonapi
248
    {
249 55
        if (!$document->has('jsonapi')) {
250 50
            return null;
251
        }
252
253 5
        return $this->jsonapiParser->parse($document->get('jsonapi'));
254
    }
255
}
256