Completed
Pull Request — master (#119)
by Toby
02:45
created

Document::mergeResources()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0106

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
ccs 14
cts 15
cp 0.9333
rs 8.6737
cc 6
eloc 11
nc 6
nop 3
crap 6.0106
1
<?php
2
3
/*
4
 * This file is part of JSON-API.
5
 *
6
 * (c) Toby Zerner <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tobscure\JsonApi;
13
14
use JsonSerializable;
15
16
class Document implements JsonSerializable
17
{
18
    use LinksTrait, SelfLinkTrait, PaginationLinksTrait, MetaTrait;
19
20
    const MEDIA_TYPE = 'application/vnd.api+json';
21
22
    private $data;
23
    private $errors;
24
    private $jsonapi;
25
26
    private $include = [];
27
    private $fields = [];
28
29 24
    private function __construct()
30
    {
31 24
    }
32
33
    /**
34
     * @param ResourceInterface|ResourceInterface[] $data
35
     *
36
     * @return self
37
     */
38 15
    public static function fromData($data)
39
    {
40 15
        $document = new self;
41 15
        $document->setData($data);
42
43 15
        return $document;
44
    }
45
46
    /**
47
     * @param array $meta
48
     *
49
     * @return self
50
     */
51 6
    public static function fromMeta(array $meta)
52
    {
53 6
        $document = new self;
54 6
        $document->setMeta($meta);
55
56 6
        return $document;
57
    }
58
59
    /**
60
     * @param Error[] $errors
61
     *
62
     * @return self
63
     */
64 3
    public static function fromErrors(array $errors)
65
    {
66 3
        $document = new self;
67 3
        $document->setErrors($errors);
68
69 3
        return $document;
70
    }
71
72
    /**
73
     * Set the primary data.
74
     *
75
     * @param ResourceInterface|ResourceInterface[]|null $data
76
     */
77 15
    public function setData($data)
78
    {
79 15
        $this->data = $data;
80 15
    }
81
82
    /**
83
     * Set the errors array.
84
     *
85
     * @param Error[]|null $errors
86
     */
87 3
    public function setErrors(array $errors = null)
88
    {
89 3
        $this->errors = $errors;
90 3
    }
91
92
    /**
93
     * Set the jsonapi version.
94
     *
95
     * @param string $version
96
     */
97
    public function setApiVersion($version)
98
    {
99
        $this->jsonapi['version'] = $version;
100
    }
101
102
    /**
103
     * Set the jsonapi meta information.
104
     *
105
     * @param array $meta
106
     */
107
    public function setApiMeta(array $meta)
108
    {
109
        $this->jsonapi['meta'] = $meta;
110
    }
111
112
    /**
113
     * Set the relationship paths to include.
114
     *
115
     * @param string[] $include
116
     */
117 3
    public function setInclude($include)
118
    {
119 3
        $this->include = $include;
120 3
    }
121
122
    /**
123
     * Set the sparse fieldsets.
124
     *
125
     * @param array $fields
126
     */
127 3
    public function setFields($fields)
128
    {
129 3
        $this->fields = $fields;
130 3
    }
131
132
    /**
133
     * Serialize for JSON usage.
134
     *
135
     * @return array
136
     */
137 24
    public function jsonSerialize()
138
    {
139
        $document = [
140 24
            'links' => $this->links,
141 24
            'meta' => $this->meta,
142 24
            'errors' => $this->errors,
143 24
            'jsonapi' => $this->jsonapi
144 24
        ];
145
146 24
        if ($this->data) {
147 15
            $isCollection = is_array($this->data);
148 15
            $resources = $isCollection ? $this->data : [$this->data];
149
150 15
            $map = $this->buildResourceMap($resources);
151
152 15
            $primary = $this->extractResourcesFromMap($map, $resources);
153
154 15
            $document['data'] = $isCollection ? $primary : $primary[0];
155
156 15
            if ($map) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $map of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157 15
                $document['included'] = call_user_func_array('array_merge', $map);
158 15
            }
159 15
        }
160
161 24
        return (object) array_filter($document);
162
    }
163
164 15
    private function buildResourceMap(array $resources)
165
    {
166 15
        $map = [];
167
168 15
        $include = $this->buildRelationshipTree($this->include);
169
170 15
        $this->mergeResources($map, $resources, $include);
171
172 15
        return $map;
173
    }
174
175 15
    private function mergeResources(array &$map, array $resources, array $include)
176
    {
177 15
        foreach ($resources as $resource) {
178 15
            $relationships = [];
179
180 15
            foreach ($include as $name => $nested) {
181 3
                if (! ($relationship = $resource->getRelationship($name))) {
182
                    continue;
183
                }
184
185 3
                $relationships[$name] = $relationship;
186
187 3
                if ($data = $relationship->getData()) {
188 3
                    $children = is_array($data) ? $data : [$data];
189
190 3
                    $this->mergeResources($map, $children, $nested);
191 3
                }
192 15
            }
193
194 15
            $this->mergeResource($map, $resource, $relationships);
195 15
        }
196 15
    }
197
198 15
    private function mergeResource(array &$map, ResourceInterface $resource, array $relationships)
199
    {
200 15
        $type = $resource->getType();
201 15
        $id = $resource->getId();
202 15
        $links = $resource->getLinks();
203 15
        $meta = $resource->getMeta();
204
205 15
        $fields = isset($this->fields[$type]) ? $this->fields[$type] : null;
206
207 15
        $attributes = $resource->getAttributes($fields);
208
209 15
        if ($fields) {
210 3
            $keys = array_flip($fields);
211
212 3
            $attributes = array_intersect_key($attributes, $keys);
213 3
            $relationships = array_intersect_key($relationships, $keys);
214 3
        }
215
216 15
        if (empty($map[$type][$id])) {
217 15
            $map[$type][$id] = new ResourceObject($type, $id);
218 15
        }
219
220 15
        array_map([$map[$type][$id], 'setAttribute'], array_keys($attributes), $attributes);
221 15
        array_map([$map[$type][$id], 'setRelationship'], array_keys($relationships), $relationships);
222 15
        array_map([$map[$type][$id], 'setLink'], array_keys($links), $links);
223 15
        array_map([$map[$type][$id], 'setMetaItem'], array_keys($meta), $meta);
224 15
    }
225
226 15
    private function extractResourcesFromMap(array &$map, array $resources)
227
    {
228 15
        return array_filter(
229 15
            array_map(function ($resource) use (&$map) {
230 15
                $type = $resource->getType();
231 15
                $id = $resource->getId();
232
233 15
                if (isset($map[$type][$id])) {
234 15
                    $resource = $map[$type][$id];
235 15
                    unset($map[$type][$id]);
236
237 15
                    return $resource;
238
                }
239 15
            }, $resources)
240 15
        );
241
    }
242
243 15
    private function buildRelationshipTree(array $paths)
244
    {
245 15
        $tree = [];
246
247 15
        foreach ($paths as $path) {
248 3
            $keys = explode('.', $path);
249 3
            $array = &$tree;
250
251 3
            foreach ($keys as $key) {
252 3
                if (! isset($array[$key])) {
253 3
                    $array[$key] = [];
254 3
                }
255
256 3
                $array = &$array[$key];
257 3
            }
258 15
        }
259
260 15
        return $tree;
261
    }
262
}
263