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

mixerapi / mixerapi-dev / 12976740567

26 Jan 2025 05:15PM UTC coverage: 87.726% (+0.3%) from 87.451%
12976740567

Pull #156

github

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

45 of 45 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 Cake\View\View;
16
use MixerApi\Core\View\SerializableAssociation;
17
use ReflectionClass;
18
use ReflectionException;
19
use RuntimeException;
20

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

33
    private ?PaginatorHelper $paginator;
34

35
    private mixed $data;
36

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

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

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

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

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

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

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

81
        return $json;
5✔
82
    }
83

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

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

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

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

117
        return $mixed;
7✔
118
    }
119

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

139
        $links = [];
3✔
140

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

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

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

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

165
        return $return;
3✔
166
    }
167

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

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

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

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

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

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

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

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

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