Passed
Push — main ( e55545...3ca3cd )
by Pavel
01:30
created

src/Serializer.php (1 issue)

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