• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mixerapi / mixerapi-dev / 13002204893

28 Jan 2025 02:07AM UTC coverage: 86.907%. First build
13002204893

Pull #156

github

web-flow
Merge 01d450ee5 into 9d91f041b
Pull Request #156: Adds beforeSerialize and afterSerialize events

24 of 33 new or added lines in 3 files covered. (72.73%)

916 of 1054 relevant lines covered (86.91%)

3.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.11
/plugins/json-ld-view/src/JsonSerializer.php
1
<?php
2
declare(strict_types=1);
3

4
namespace MixerApi\JsonLdView;
5

6
use Cake\Core\Configure;
7
use Cake\Datasource\EntityInterface;
8
use Cake\Datasource\Paging\PaginatedResultSet;
9
use Cake\Datasource\ResultSetInterface;
10
use Cake\Event\Event;
11
use Cake\Event\EventManager;
12
use Cake\Http\ServerRequest;
13
use Cake\ORM\Entity;
14
use Cake\View\Helper\PaginatorHelper;
15
use MixerApi\Core\View\SerializableAssociation;
16
use ReflectionException;
17
use RuntimeException;
18

19
/**
20
 * Creates a JSON-LD resource
21
 *
22
 * @link https://json-ld.org/
23
 * @link https://lists.w3.org/Archives/Public/public-hydra/2015Oct/0163.html
24
 */
25
class JsonSerializer
26
{
27
    public const BEFORE_SERIALIZE_EVENT = 'MixerApi.JsonLdView.beforeSerialize';
28
    public const AFTER_SERIALIZE_EVENT = 'MixerApi.JsonLdView.afterSerialize';
29

30
    /**
31
     * JSON-LD data array
32
     *
33
     * @var mixed
34
     */
35
    private mixed $data;
36

37
    /**
38
     * JsonLd config
39
     *
40
     * @var array|null
41
     */
42
    private ?array $config;
43

44
    /**
45
     * @var string
46
     */
47
    private string $hydra = '';
48

49
    /**
50
     * If constructed without parameters collection meta data will not be added to JSON-LD $data
51
     *
52
     * @param mixed $serialize the data to be converted into a JSON-LD array
53
     * @param \Cake\Http\ServerRequest|null $request optional ServerRequest
54
     * @param \Cake\View\Helper\PaginatorHelper|null $paginator optional PaginatorHelper
55
     */
56
    public function __construct(
57
        mixed $serialize,
58
        private ?ServerRequest $request = null,
59
        private ?PaginatorHelper $paginator = null
60
    ) {
61
        $jsonLd = $this->recursion($serialize);
8✔
62
        $this->config = Configure::read('JsonLdView');
8✔
63
        if (isset($this->config['isHydra']) && $this->config['isHydra']) {
8✔
64
            $this->hydra = 'hydra:';
×
65
        }
66

67
        if ($jsonLd instanceof ResultSetInterface || $jsonLd instanceof PaginatedResultSet) {
8✔
68
            $this->data = $this->collection($jsonLd);
4✔
69
        } elseif (is_subclass_of($jsonLd, Entity::class)) {
4✔
70
            $this->data = $this->item($jsonLd);
3✔
71
        } else {
72
            $this->data = $serialize;
1✔
73
        }
74
    }
75

76
    /**
77
     * Serializes data as json-ld
78
     *
79
     * @param int $jsonOptions JSON options see https://www.php.net/manual/en/function.json-encode.php
80
     * @return string
81
     * @throws \RuntimeException
82
     */
83
    public function asJson(int $jsonOptions = 0): string
84
    {
85
        EventManager::instance()->dispatch(new Event(self::BEFORE_SERIALIZE_EVENT, $this));
6✔
86

87
        $json = json_encode($this->data, $jsonOptions);
6✔
88

89
        if ($json === false) {
6✔
90
            throw new RuntimeException(json_last_error_msg(), json_last_error());
1✔
91
        }
92

93
        EventManager::instance()->dispatch(new Event(self::AFTER_SERIALIZE_EVENT, $this, [
5✔
94
            'data' => $json,
5✔
95
        ]));
5✔
96

97
        return $json;
5✔
98
    }
99

100
    /**
101
     * Get JSON-LD data as an array
102
     *
103
     * @return array|null
104
     */
105
    public function getData(): ?array
106
    {
107
        return $this->data;
2✔
108
    }
109

110
    /**
111
     * @param mixed $data The data to be serialized
112
     * @return void
113
     */
114
    public function setData(mixed $data): void
115
    {
NEW
116
        $this->data = $data;
×
117
    }
118

119
    /**
120
     * @return \Cake\Http\ServerRequest|null
121
     */
122
    public function getRequest(): ?ServerRequest
123
    {
NEW
124
        return $this->request;
×
125
    }
126

127
    /**
128
     * @return \Cake\View\Helper\PaginatorHelper|null
129
     */
130
    public function getPaginatorHelper(): ?PaginatorHelper
131
    {
NEW
132
        return $this->paginator;
×
133
    }
134

135
    /**
136
     * Recursive method for converting mixed data into JSON-LD. This method converts instances of Cake\ORM\Entity into
137
     * JSON-LD resources, but does not serialize the data.
138
     *
139
     * @param mixed $mixed data to be serialized
140
     * @return array|\Cake\Datasource\EntityInterface|\Cake\Datasource\ResultSetInterface
141
     */
142
    private function recursion(mixed &$mixed): mixed
143
    {
144
        if ($mixed instanceof ResultSetInterface || $mixed instanceof PaginatedResultSet || is_array($mixed)) {
8✔
145
            foreach ($mixed as $item) {
6✔
146
                $this->recursion($item);
6✔
147
            }
148
        } elseif ($mixed instanceof EntityInterface) {
8✔
149
            $serializableAssocation = new SerializableAssociation($mixed);
7✔
150

151
            $mixed = $this->resource($mixed, $serializableAssocation);
7✔
152

153
            foreach ($serializableAssocation->getAssociations() as $value) {
7✔
154
                $this->recursion($value);
6✔
155
            }
156
        }
157

158
        return $mixed;
8✔
159
    }
160

161
    /**
162
     * JSON-LD array for collection requests
163
     *
164
     * @param mixed $jsonLd the data to be converted into a JSON-LD array
165
     * @return array
166
     */
167
    private function collection(mixed $jsonLd): array
168
    {
169
        $return = [];
4✔
170

171
        try {
172
            if ($jsonLd instanceof PaginatedResultSet) {
4✔
173
                $entity = $jsonLd->toArray()[0];
1✔
174
            } else {
175
                $entity = $jsonLd->first();
3✔
176
            }
177

178
            if ($entity instanceof JsonLdDataInterface) {
4✔
179
                $return['@context'] = $entity->getJsonLdContext();
4✔
180
            }
181
        } catch (ReflectionException $e) {
×
182
        }
183

184
        if ($this->request instanceof ServerRequest) {
4✔
185
            $return['@id'] = $this->request->getPath();
4✔
186
        }
187

188
        $return['@type'] = $this->hydra . 'Collection';
4✔
189
        $return[$this->hydra . 'pageItems'] = intval($jsonLd->count());
4✔
190
        $return[$this->hydra . 'totalItems'] = null;
4✔
191

192
        if ($this->paginator instanceof PaginatorHelper) {
4✔
193
            $url = (string)$this->request->getUri();
4✔
194
            $id = parse_url($url, PHP_URL_PATH);
4✔
195
            $query = parse_url($url, PHP_URL_QUERY);
4✔
196

197
            if (!empty($query)) {
4✔
198
                $id .= '?' . $query;
1✔
199
            }
200

201
            $return['view'] = [
4✔
202
                '@id' => $id,
4✔
203
                '@type' => 'PartialCollectionView',
4✔
204
                $this->hydra . 'next' => $this->paginator->next(),
4✔
205
                $this->hydra . 'prev' => $this->paginator->prev(),
4✔
206
                $this->hydra . 'first' => $this->paginator->first(),
4✔
207
                $this->hydra . 'last' => $this->paginator->last(),
4✔
208
            ];
4✔
209
            $return[$this->hydra . 'totalItems'] = intval($this->paginator->counter());
4✔
210
        }
211

212
        $return[$this->hydra . 'member'] = $jsonLd;
4✔
213

214
        return $return;
4✔
215
    }
216

217
    /**
218
     * JSON-LD array for item requests
219
     *
220
     * @param mixed $jsonLd the data to be converted into a JSON-LD array
221
     * @return array
222
     */
223
    private function item(mixed $jsonLd): array
224
    {
225
        $links = [];
3✔
226

227
        if ($this->request instanceof ServerRequest) {
3✔
228
            $links['@id'] = $this->request->getPath();
3✔
229
        }
230

231
        if ($jsonLd instanceof JsonLdDataInterface) {
3✔
232
            $links['@type'] = $jsonLd->getJsonLdType();
3✔
233
            $links['@context'] = $jsonLd->getJsonLdContext();
3✔
234
        }
235

236
        if (!is_array($jsonLd)) {
3✔
237
            $jsonLd = $jsonLd->toArray();
3✔
238
        }
239

240
        return array_merge($links, $jsonLd);
3✔
241
    }
242

243
    /**
244
     * Creates a JSON-LD Resource
245
     *
246
     * Requires an instance of Cake\ORM\Entity or EntityInterface and a SerializableAssociation instance
247
     *
248
     * @param \Cake\Datasource\EntityInterface $entity Cake\ORM\Entity or EntityInterface
249
     * @param \MixerApi\Core\View\SerializableAssociation $serializable SerializableAssociation
250
     * @return \Cake\Datasource\EntityInterface
251
     */
252
    private function resource(EntityInterface $entity, SerializableAssociation $serializable): EntityInterface
253
    {
254
        foreach ($serializable->getAssociations() as $property => $value) {
7✔
255
            if (!is_array($value) && !$value instanceof EntityInterface) {
6✔
256
                $entity->unset($property);
×
257
            }
258
        }
259

260
        if ($entity instanceof JsonLdDataInterface) {
7✔
261
            $entity->set('@id', $entity->getJsonLdIdentifier($entity));
7✔
262
            $entity->set('@type', $entity->getJsonLdType());
7✔
263
            $entity->set('@context', $entity->getJsonLdContext());
7✔
264
        }
265

266
        return $entity;
7✔
267
    }
268
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc