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

mixerapi / mixerapi-dev / 13002105632

28 Jan 2025 01:57AM UTC coverage: 86.907% (-0.5%) from 87.451%
13002105632

Pull #156

github

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

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

6 existing lines in 2 files now uncovered.

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
    {
62
        $jsonLd = $this->recursion($serialize);
8✔
63
        $this->config = Configure::read('JsonLdView');
8✔
64
        if (isset($this->config['isHydra']) && $this->config['isHydra']) {
8✔
UNCOV
65
            $this->hydra = 'hydra:';
×
66
        }
67

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

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

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

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

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

98
        return $json;
5✔
99
    }
100

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

111
    public function setData(mixed $data): void
112
    {
NEW
113
        $this->data = $data;
×
114
    }
115

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

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

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

148
            $mixed = $this->resource($mixed, $serializableAssocation);
7✔
149

150
            foreach ($serializableAssocation->getAssociations() as $value) {
7✔
151
                $this->recursion($value);
6✔
152
            }
153
        }
154

155
        return $mixed;
8✔
156
    }
157

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

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

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

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

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

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

194
            if (!empty($query)) {
4✔
195
                $id .= '?' . $query;
1✔
196
            }
197

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

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

211
        return $return;
4✔
212
    }
213

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

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

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

233
        if (!is_array($jsonLd)) {
3✔
234
            $jsonLd = $jsonLd->toArray();
3✔
235
        }
236

237
        return array_merge($links, $jsonLd);
3✔
238
    }
239

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

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

263
        return $entity;
7✔
264
    }
265
}
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