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

mixerapi / mixerapi-dev / 12990519882

27 Jan 2025 01:52PM UTC coverage: 87.726% (+0.3%) from 87.451%
12990519882

Pull #156

github

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

44 of 44 new or added lines in 3 files covered. (100.0%)

7 existing lines in 2 files now uncovered.

922 of 1051 relevant lines covered (87.73%)

3.29 hits per line

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

94.2
/plugins/hal-view/src/JsonSerializer.php
1
<?php
2
declare(strict_types=1);
3

4
namespace MixerApi\HalView;
5

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

20
/**
21
 * Creates a HAL+JSON resource
22
 *
23
 * @link https://tools.ietf.org/html/draft-kelly-json-hal-06
24
 * @uses \Cake\Utility\Inflector
25
 * @uses \MixerApi\Core\View\SerializableAssociation
26
 * @uses ReflectionClass
27
 */
28
class JsonSerializer
29
{
30
    private ?ServerRequest $request;
31

32
    private ?PaginatorHelper $paginator;
33

34
    private mixed $data;
35

36
    /**
37
     * If constructed without parameters collection meta data will not be added to HAL $data
38
     *
39
     * @param mixed $serialize the data to be converted into a HAL array
40
     * @param \Cake\Http\ServerRequest|null $request optional ServerRequest
41
     * @param \Cake\View\Helper\PaginatorHelper|null $paginator optional PaginatorHelper
42
     */
43
    public function __construct(mixed $serialize, ?ServerRequest $request = null, ?PaginatorHelper $paginator = null)
44
    {
45
        $this->request = $request;
7✔
46
        $this->paginator = $paginator;
7✔
47

48
        $hal = $this->recursion($serialize);
7✔
49

50
        if ($hal instanceof ResultSetInterface || $hal instanceof PaginatedResultSet) {
7✔
51
            $this->data = $this->collection($hal);
3✔
52
        } elseif (is_subclass_of($hal, Entity::class)) {
4✔
53
            $this->data = $this->item($hal);
3✔
54
        } else {
55
            $this->data = $serialize;
1✔
56
        }
57
    }
58

59
    /**
60
     * Serializes HAL data as hal+json
61
     *
62
     * @param int $jsonOptions JSON options see https://www.php.net/manual/en/function.json-encode.php
63
     * @return string
64
     * @throws \RuntimeException
65
     */
66
    public function asJson(int $jsonOptions = 0): string
67
    {
68
        EventManager::instance()->dispatch(new Event('MixerApi.HalView.beforeSerialize', $this));
6✔
69

70
        $json = json_encode($this->data, $jsonOptions);
6✔
71

72
        if ($json === false) {
6✔
73
            throw new RuntimeException(json_last_error_msg(), json_last_error());
1✔
74
        }
75

76
        EventManager::instance()->dispatch(new Event('MixerApi.HalView.afterSerialize', $this, [
5✔
77
            'data' => $json,
5✔
78
        ]));
5✔
79

80
        return $json;
5✔
81
    }
82

83
    /**
84
     * Get HAL data as an array
85
     *
86
     * @return array|null
87
     */
88
    public function getData(): ?array
89
    {
90
        return $this->data;
1✔
91
    }
92

93
    /**
94
     * Recursive method for converting mixed data into HAL. This method converts instances of Cake\ORM\Entity into
95
     * HAL resources, but does not serialize the data.
96
     *
97
     * @param mixed $mixed data to be serialized
98
     * @return array|\Cake\Datasource\EntityInterface|\Cake\Datasource\ResultSetInterface|\Cake\Datasource\Paging\PaginatedResultSet
99
     */
100
    private function recursion(mixed &$mixed): mixed
101
    {
102
        if ($mixed instanceof ResultSetInterface || $mixed instanceof PaginatedResultSet || is_array($mixed)) {
7✔
103
            foreach ($mixed as $item) {
6✔
104
                $this->recursion($item);
6✔
105
            }
106
        } elseif ($mixed instanceof EntityInterface) {
7✔
107
            $serializableAssociation = new SerializableAssociation($mixed);
6✔
108

109
            $mixed = $this->resource($mixed, $serializableAssociation);
6✔
110

111
            foreach ($serializableAssociation->getAssociations() as $value) {
6✔
112
                $this->recursion($value);
5✔
113
            }
114
        }
115

116
        return $mixed;
7✔
117
    }
118

119
    /**
120
     * HAL array for collection requests
121
     *
122
     * @param \Cake\Datasource\Paging\PaginatedResultSet|\Cake\Datasource\ResultSetInterface $collection the data to be converted into a HAL array
123
     * @return array
124
     */
125
    private function collection(mixed $collection): array
126
    {
127
        try {
128
            if ($collection instanceof PaginatedResultSet) {
3✔
129
                $entity = $collection->toArray()[0];
3✔
130
            } else {
UNCOV
131
                $entity = $collection->first();
×
132
            }
133
            $tableName = Inflector::tableize((new ReflectionClass($entity))->getShortName());
3✔
UNCOV
134
        } catch (ReflectionException $e) {
×
UNCOV
135
            $tableName = 'data';
×
136
        }
137

138
        $links = [];
3✔
139

140
        $return = [
3✔
141
            'count' => $collection->count(),
3✔
142
            'total' => null,
3✔
143
        ];
3✔
144

145
        if ($this->request instanceof ServerRequest) {
3✔
146
            $links = [
3✔
147
                'self' => ['href' => $this->request->getPath()],
3✔
148
            ];
3✔
149
        }
150

151
        if ($this->paginator instanceof PaginatorHelper) {
3✔
152
            $links = array_merge_recursive($links, [
3✔
153
                'next' => ['href' => $this->paginator->next()],
3✔
154
                'prev' => ['href' => $this->paginator->prev()],
3✔
155
                'first' => ['href' => $this->paginator->first()],
3✔
156
                'last' => ['href' => $this->paginator->last()],
3✔
157
            ]);
3✔
158
            $return['total'] = intval($this->paginator->counter());
3✔
159
        }
160

161
        $return['_links'] = $links;
3✔
162
        $return['_embedded'] = [$tableName => $collection];
3✔
163

164
        return $return;
3✔
165
    }
166

167
    /**
168
     * HAL array for item requests
169
     *
170
     * @param mixed $hal the data to be converted into a HAL array
171
     * @return array
172
     */
173
    private function item(mixed $hal): array
174
    {
175
        $links = [];
3✔
176

177
        if ($this->request instanceof ServerRequest) {
3✔
178
            $links = ['_links' => ['self' => $this->request->getPath()]];
3✔
179
        }
180

181
        if (!is_array($hal)) {
3✔
182
            $hal = $hal->toArray();
3✔
183
        }
184

185
        return array_merge($links, $hal);
3✔
186
    }
187

188
    /**
189
     * Creates a HAL+JSON Resource
190
     *
191
     * Requires an instance of Cake\ORM\Entity or EntityInterface and an EmbeddableHalResource instance
192
     *
193
     * @param \Cake\Datasource\EntityInterface $entity Cake\ORM\Entity or EntityInterface
194
     * @param \MixerApi\Core\View\SerializableAssociation $association SerializableAssociation
195
     * @return \Cake\Datasource\EntityInterface
196
     */
197
    private function resource(EntityInterface $entity, SerializableAssociation $association): EntityInterface
198
    {
199
        $embedded = [];
6✔
200

201
        foreach ($association->getAssociations() as $property => $value) {
6✔
202
            if (!is_array($value) && !$value instanceof EntityInterface) {
5✔
UNCOV
203
                continue;
×
204
            }
205
            $embedded[$property] = $value;
5✔
206
            $entity->unset($property);
5✔
207
        }
208

209
        if (!empty($embedded)) {
6✔
210
            $entity->set('_embedded', $embedded);
5✔
211
        }
212

213
        if ($entity instanceof HalResourceInterface) {
6✔
214
            $entity->set('_links', $entity->getHalLinks($entity));
6✔
215
        }
216

217
        return $entity;
6✔
218
    }
219
}
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