Passed
Push — main ( 3ca3cd...5fb0dc )
by Pavel
01:34
created

src/Serializer.php (1 issue)

Labels
Severity
1
<?php namespace JSONAPI\Resource;
2
3
use JSONAPI\Resource\LinkGenerator\BasicLinksGenerator;
4
use JSONAPI\Resource\LinkGenerator\LinkGeneratorInterface;
5
use JSONAPI\Resource\Metadata\Repository;
6
7
class Serializer
8
{
9
    /** @var array[] */
10
    protected array $compoundData = [];
11
12
    // Skip creating compound data.
13
    const OPTIONS_NO_COMPOUND_DOCUMENTS = 'noCompoundDocuments';
14
15
    // Skip attributes serialization.
16
    const OPTIONS_NO_ATTRIBUTES = 'noAttributes';
17
18
    /**
19
     * @param array<string, mixed> $options
20
     */
21
    public function __construct(
22
        protected ?Repository $metadata = null,
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_PROTECTED, expecting T_VARIABLE on line 22 at column 8
Loading history...
23
        protected ?Fieldset $fieldset = null,
24
        protected ?Includeset $includeset = null,
25
        protected ?LinkGeneratorInterface $linksGenerator = null,
26
        protected array $options = [],
27
    ) {
28
        $this->metadata ??= new Repository();
29
        $this->linksGenerator ??= new BasicLinksGenerator($this->metadata);
30
    }
31
32
    /**
33
     * @param object|object[] $resource
34
     * @return array<string, array>|array<array<string, array>>
35
     */
36
    public function serialize(object | array $resource): array
37
    {
38
        return is_array($resource)
39
            ? array_map(fn($resource) => $this->serializeResource($resource), $resource)
40
            : $this->serializeResource($resource);
41
    }
42
43
    /**
44
     * @return array[]
45
     */
46
    public function compoundData(): array
47
    {
48
        return array_values((array) $this->compoundData);
49
    }
50
51
    /**
52
     * @param object $resource
53
     * @return array<string, string|array>
54
     */
55
    protected function serializeResource(object $resource): array
56
    {
57
        $meta = $this->metadata->getResourceMeta($resource);
58
        $type = $meta->getType();
59
        $id = $meta->getId($resource);
60
61
        $data = [
62
            'type' => $type,
63
            'id' => $id,
64
        ];
65
66
        if (null !== ($attributes = $this->serializeAttributes($resource, $this->fieldset[$type] ?? null))) {
67
            $data['attributes'] = $attributes;
68
        }
69
70
        if (null !== ($relationships = $this->serializeRelationships($resource))) {
71
            $data['relationships'] = $relationships;
72
        }
73
74
        if ($this->linksGenerator !== null) {
75
            if (null !== ($links = $this->linksGenerator->resourceLinks($resource))) {
76
                $data['links'] = $links;
77
            }
78
        }
79
80
        return $data;
81
    }
82
83
    /**
84
     * @param object $resource
85
     * @param string[]|null $fields
86
     * @return array<string, mixed>|null
87
     */
88
    protected function serializeAttributes(object $resource, array | null $fields): array | null
89
    {
90
        if (($this->options[static::OPTIONS_NO_ATTRIBUTES] ?? false) === true) {
91
            return null;
92
        }
93
94
        $result = [];
95
96
        $attributes = array_filter(
97
            $this->metadata->getResourceAttributes($resource),
98
            fn($key) => is_null($fields) || in_array($key, $fields)
99
        );
100
101
        foreach ($attributes as $key => $attribute) {
102
            $result[$key] = $attribute->getValue($resource);
103
        }
104
105
        return $result;
106
    }
107
108
    /**
109
     * @param object $resource
110
     * @return array<string, array>|null
111
     */
112
    protected function serializeRelationships(object $resource): array | null
113
    {
114
        if ($this->includeset === null || count($this->includeset) === 0) {
115
            return null;
116
        }
117
118
        $relationships = [];
119
        $relationshipsMap = $this->metadata->getResourceRelationships($resource);
120
121
        foreach ($this->includeset as $relation => $childIncludeset) {
122
            if (!isset($relationshipsMap[$relation])) {
123
                // TODO: Maybe we should throw an exception here
124
                continue;
125
            }
126
127
            $value = $relationshipsMap[$relation]->getValue($resource);
128
129
            $relationships[$relation] = [
130
                'data' => is_array($value)
131
                    ? array_map(fn($val) => $this->compoundedDocument($val, $childIncludeset), $value)
132
                    : $this->compoundedDocument($value, $childIncludeset),
133
            ];
134
135
            if ($this->linksGenerator !== null) {
136
                if (null !== ($links = $this->linksGenerator->relationshipLinks($resource, $relation))) {
137
                    $relationships[$relation]['links'] = $links;
138
                }
139
            }
140
        }
141
142
        return $relationships;
143
    }
144
145
    /**
146
     * @param object $resource
147
     * @param Includeset $includeset
148
     * @return array<string, string>
149
     */
150
    protected function compoundedDocument(object $resource, Includeset $includeset): array
151
    {
152
        $meta = $this->metadata->getResourceMeta($resource);
153
        $type = $meta->getType();
154
        $id = $meta->getId($resource);
155
156
        if (($this->options[static::OPTIONS_NO_COMPOUND_DOCUMENTS] ?? false) === false) {
157
            $objKey = sprintf('%s-%s', $type, $id);
158
            if (!isset($this->compoundData[$objKey])) {
159
                $serializer = clone $this;
160
                $serializer->includeset = $includeset;
161
                $serializer->metadata = $this->metadata;
162
163
                $this->compoundData[$objKey] = $serializer->serialize($resource);
164
            }
165
        }
166
167
        return [
168
            'type' => $type,
169
            'id' => $id,
170
        ];
171
    }
172
}
173