Issues (26)

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 (null !== ($links = $this->linksGenerator->resourceLinks($resource))) {
75
            $data['links'] = $links;
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 (null !== ($links = $this->linksGenerator->relationshipLinks($resource, $relation))) {
134
                $relationships[$relation]['links'] = $links;
135
            }
136
        }
137
138
        return $relationships;
139
    }
140
141
    /**
142
     * @param object $resource
143
     * @param Includeset $includeset
144
     * @return array<string, string>
145
     */
146
    protected function compoundedDocument(object $resource, Includeset $includeset): array
147
    {
148
        $meta = $this->metadata->getResourceMeta($resource);
149
        $type = $meta->getType();
150
        $id = $meta->getId($resource);
151
152
        if (($this->options[static::OPTIONS_NO_COMPOUND_DOCUMENTS] ?? false) === false) {
153
            $objKey = sprintf('%s-%s', $type, $id);
154
            if (!isset($this->compoundData[$objKey])) {
155
                $serializer = clone $this;
156
                $serializer->includeset = $includeset;
157
                $serializer->metadata = $this->metadata;
158
159
                $this->compoundData[$objKey] = $serializer->serialize($resource);
160
            }
161
        }
162
163
        return [
164
            'type' => $type,
165
            'id' => $id,
166
        ];
167
    }
168
}
169