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

mixerapi / mixerapi-dev / 12976479143

26 Jan 2025 04:37PM UTC coverage: 87.451% (-0.1%) from 87.598%
12976479143

push

github

web-flow
CakePHP 5.1 deprecations (#155)

* Handle cakephp 5.1.0: Calling `Cake\ORM\ResultSet` methods, such as `first()`, on PaginatedResultSet is deprecated. You must call `items()` first (for example, `items()->first()`)

* CakePHP 5.1

* resolve phpstan errors

5 of 6 new or added lines in 2 files covered. (83.33%)

1 existing line in 1 file now uncovered.

899 of 1028 relevant lines covered (87.45%)

3.29 hits per line

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

93.85
/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\Http\ServerRequest;
10
use Cake\ORM\Entity;
11
use Cake\Utility\Inflector;
12
use Cake\View\Helper\PaginatorHelper;
13
use MixerApi\Core\View\SerializableAssociation;
14
use ReflectionClass;
15
use ReflectionException;
16
use RuntimeException;
17

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

30
    private ?PaginatorHelper $paginator;
31

32
    private mixed $data;
33

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

46
        $hal = $this->recursion($serialize);
7✔
47

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

57
    /**
58
     * Serializes HAL data as hal+json
59
     *
60
     * @param int $jsonOptions JSON options see https://www.php.net/manual/en/function.json-encode.php
61
     * @return string
62
     * @throws \RuntimeException
63
     */
64
    public function asJson(int $jsonOptions = 0): string
65
    {
66
        $json = json_encode($this->data, $jsonOptions);
6✔
67

68
        if ($json === false) {
6✔
69
            throw new RuntimeException(json_last_error_msg(), json_last_error());
1✔
70
        }
71

72
        return $json;
5✔
73
    }
74

75
    /**
76
     * Get HAL data as an array
77
     *
78
     * @return array|null
79
     */
80
    public function getData(): ?array
81
    {
82
        return $this->data;
1✔
83
    }
84

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

101
            $mixed = $this->resource($mixed, $serializableAssociation);
6✔
102

103
            foreach ($serializableAssociation->getAssociations() as $value) {
6✔
104
                $this->recursion($value);
5✔
105
            }
106
        }
107

108
        return $mixed;
7✔
109
    }
110

111
    /**
112
     * HAL array for collection requests
113
     *
114
     * @param \Cake\Datasource\Paging\PaginatedResultSet|\Cake\Datasource\ResultSetInterface $collection the data to be converted into a HAL array
115
     * @return array
116
     */
117
    private function collection(mixed $collection): array
118
    {
119
        try {
120
            if ($collection instanceof PaginatedResultSet) {
3✔
121
                $entity = $collection->toArray()[0];
3✔
122
            } else {
NEW
123
                $entity = $collection->first();
×
124
            }
125
            $tableName = Inflector::tableize((new ReflectionClass($entity))->getShortName());
3✔
126
        } catch (ReflectionException $e) {
×
127
            $tableName = 'data';
×
128
        }
129

130
        $links = [];
3✔
131

132
        $return = [
3✔
133
            'count' => $collection->count(),
3✔
134
            'total' => null,
3✔
135
        ];
3✔
136

137
        if ($this->request instanceof ServerRequest) {
3✔
138
            $links = [
3✔
139
                'self' => ['href' => $this->request->getPath()],
3✔
140
            ];
3✔
141
        }
142

143
        if ($this->paginator instanceof PaginatorHelper) {
3✔
144
            $links = array_merge_recursive($links, [
3✔
145
                'next' => ['href' => $this->paginator->next()],
3✔
146
                'prev' => ['href' => $this->paginator->prev()],
3✔
147
                'first' => ['href' => $this->paginator->first()],
3✔
148
                'last' => ['href' => $this->paginator->last()],
3✔
149
            ]);
3✔
150
            $return['total'] = intval($this->paginator->counter());
3✔
151
        }
152

153
        $return['_links'] = $links;
3✔
154
        $return['_embedded'] = [$tableName => $collection];
3✔
155

156
        return $return;
3✔
157
    }
158

159
    /**
160
     * HAL array for item requests
161
     *
162
     * @param mixed $hal the data to be converted into a HAL array
163
     * @return array
164
     */
165
    private function item(mixed $hal): array
166
    {
167
        $links = [];
3✔
168

169
        if ($this->request instanceof ServerRequest) {
3✔
170
            $links = ['_links' => ['self' => $this->request->getPath()]];
3✔
171
        }
172

173
        if (!is_array($hal)) {
3✔
174
            $hal = $hal->toArray();
3✔
175
        }
176

177
        return array_merge($links, $hal);
3✔
178
    }
179

180
    /**
181
     * Creates a HAL+JSON Resource
182
     *
183
     * Requires an instance of Cake\ORM\Entity or EntityInterface and an EmbeddableHalResource instance
184
     *
185
     * @param \Cake\Datasource\EntityInterface $entity Cake\ORM\Entity or EntityInterface
186
     * @param \MixerApi\Core\View\SerializableAssociation $association SerializableAssociation
187
     * @return \Cake\Datasource\EntityInterface
188
     */
189
    private function resource(EntityInterface $entity, SerializableAssociation $association): EntityInterface
190
    {
191
        $embedded = [];
6✔
192

193
        foreach ($association->getAssociations() as $property => $value) {
6✔
194
            if (!is_array($value) && !$value instanceof EntityInterface) {
5✔
195
                continue;
×
196
            }
197
            $embedded[$property] = $value;
5✔
198
            $entity->unset($property);
5✔
199
        }
200

201
        if (!empty($embedded)) {
6✔
202
            $entity->set('_embedded', $embedded);
5✔
203
        }
204

205
        if ($entity instanceof HalResourceInterface) {
6✔
206
            $entity->set('_links', $entity->getHalLinks($entity));
6✔
207
        }
208

209
        return $entity;
6✔
210
    }
211
}
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