Completed
Pull Request — develop (#401)
by Bastian
23:54 queued 17:48
created

RestUtils::getSerializerContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * service for RESTy stuff
4
 */
5
6
namespace Graviton\RestBundle\Service;
7
8
use Psr\Log\LoggerInterface;
9
use Symfony\Component\DependencyInjection\ContainerInterface;
10
use Symfony\Component\Routing\Route;
11
use Symfony\Component\Routing\Router;
12
use JMS\Serializer\Serializer;
13
use JMS\Serializer\SerializationContext;
14
use Graviton\RestBundle\Controller\RestController;
15
16
/**
17
 * A service (meaning symfony service) providing some convenience stuff when dealing with our RestController
18
 * based services (meaning rest services).
19
 *
20
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
21
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
22
 * @link     http://swisscom.ch
23
 */
24
final class RestUtils implements RestUtilsInterface
25
{
26
    /**
27
     * @var ContainerInterface
28
     */
29
    private $container;
30
31
    /**
32
     * @var Serializer
33
     */
34
    private $serializer;
35
36
    /**
37
     * @var null|SerializationContext
38
     */
39
    private $serializerContext;
40
41
    /**
42
     * @var Router
43
     */
44
    private $router;
45
46
    /** @var LoggerInterface  */
47
    private $logger;
48
49
    /**
50
     * @param ContainerInterface   $container         container
51
     * @param Router               $router            router
52
     * @param Serializer           $serializer        serializer
53
     * @param LoggerInterface      $logger            PSR logger (e.g. Monolog)
54
     * @param SerializationContext $serializerContext context for serializer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $serializerContext not be null|SerializationContext?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
55
     */
56 4
    public function __construct(
57
        ContainerInterface $container,
58
        Router $router,
59
        Serializer $serializer,
60
        LoggerInterface $logger,
61
        SerializationContext $serializerContext = null
62
    ) {
63 4
        $this->container = $container;
64 4
        $this->serializer = $serializer;
65 4
        $this->serializerContext = $serializerContext;
66 4
        $this->router = $router;
67 4
        $this->logger = $logger;
68 4
    }
69
70
    /**
71
     * Builds a map of baseroutes (controllers) to its relevant route to the actions.
72
     * ignores schema stuff.
73
     *
74
     * @return array grouped array of basenames and actions..
75
     */
76
    public function getServiceRoutingMap()
77
    {
78
        $ret = array();
79
        $optionRoutes = $this->getOptionRoutes();
80
81
        foreach ($optionRoutes as $routeName => $optionRoute) {
82
            // get base name from options action
83
            $routeParts = explode('.', $routeName);
84
            array_pop($routeParts); // get rid of last part
85
            $baseName = implode('.', $routeParts);
86
87
            // get routes from same controller
88
            foreach ($this->getRoutesByBasename($baseName) as $routeName => $route) {
89
                // don't put schema stuff
90
                if (strpos('schema', strtolower($routeName)) === false) {
91
                    $ret[$baseName][$routeName] = $route;
92
                }
93
            }
94
        }
95
96
        return $ret;
97
    }
98
99
    /**
100
     * Public function to serialize stuff according to the serializer rules.
101
     *
102
     * @param object $content Any content to serialize
103
     * @param string $format  Which format to serialize into
104
     *
105
     * @throws \Exception
106
     *
107
     * @return string $content Json content
108
     */
109 4
    public function serializeContent($content, $format = 'json')
110
    {
111 4
        $result = '';
112
        try {
113 4
            $result = $this->getSerializer()->serialize(
114 2
                $content,
115 2
                $format,
116 4
                $this->getSerializerContext()
117 2
            );
118 2
        } catch (\Exception $e) {
119
            $this->logger->alert(
120
                sprintf(
121
                    'Cannot serialize content (%s); Content: class: %s, id: %s)',
122
                    $e->getMessage(),
123
                    get_class($content),
124
                    $content->getId()
125
                )
126
            );
127
        }
128
129 4
        return $result;
130
    }
131
132
    /**
133
     * Deserialize the given content throw an exception if something went wrong
134
     *
135
     * @param string $content       Request content
136
     * @param string $documentClass Document class
137
     * @param string $format        Which format to deserialize from
138
     *
139
     * @throws \Exception
140
     *
141
     * @return object|array|integer|double|string|boolean
142
     */
143
    public function deserializeContent($content, $documentClass, $format = 'json')
144
    {
145
        $record = $this->getSerializer()->deserialize(
146
            $content,
147
            $documentClass,
148
            $format
149
        );
150
151
        return $record;
152
    }
153
154
    /**
155
     * Get the serializer
156
     *
157
     * @return Serializer
158
     */
159 4
    public function getSerializer()
160
    {
161 4
        return $this->serializer;
162
    }
163
164
    /**
165
     * Get the serializer context
166
     *
167
     * @return SerializationContext
168
     */
169 4
    public function getSerializerContext()
170
    {
171 4
        return clone $this->serializerContext;
172
    }
173
174
    /**
175
     * It has been deemed that we search for OPTION routes in order to detect our
176
     * service routes and then derive the rest from them.
177
     *
178
     * @return array An array with option routes
179
     */
180
    public function getOptionRoutes()
181
    {
182
        $router = $this->router;
183
        $ret = array_filter(
184
            $router->getRouteCollection()
185
                   ->all(),
186
            function ($route) {
187
                if (!in_array('OPTIONS', $route->getMethods())) {
188
                    return false;
189
                }
190
                // ignore all schema routes
191
                if (strpos($route->getPath(), '/schema') === 0) {
192
                    return false;
193
                }
194
                if ($route->getPath() == '/' || $route->getPath() == '/core/version') {
195
                    return false;
196
                }
197
198
                return is_null($route->getRequirement('id'));
199
            }
200
        );
201
202
        return $ret;
203
    }
204
205
    /**
206
     * Based on $baseName, this function returns all routes that match this basename..
207
     * So if you pass graviton.cont.action; it will return all route names that start with the same.
208
     * In our routing naming schema, this means all the routes from the same controller.
209
     *
210
     * @param string $baseName basename
211
     *
212
     * @return array array with matching routes
213
     */
214
    public function getRoutesByBasename($baseName)
215
    {
216
        $ret = array();
217
        foreach ($this->router
218
                      ->getRouteCollection()
219
                      ->all() as $routeName => $route) {
220
            if (preg_match('/^' . $baseName . '/', $routeName)) {
221
                $ret[$routeName] = $route;
222
            }
223
        }
224
225
        return $ret;
226
    }
227
228
    /**
229
     * Gets the Model assigned to the RestController
230
     *
231
     * @param Route $route Route
232
     *
233
     * @return bool|object The model or false
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use \Graviton\RestBundle\Model\DocumentModel|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
234
     * @throws \Exception
235
     */
236
    public function getModelFromRoute(Route $route)
237
    {
238
        $ret = false;
239
        $controller = $this->getControllerFromRoute($route);
240
241
        if ($controller instanceof RestController) {
242
            $ret = $controller->getModel();
243
        }
244
245
        return $ret;
246
    }
247
248
    /**
249
     * Gets the controller from a Route
250
     *
251
     * @param Route $route Route
252
     *
253
     * @return bool|object The controller or false
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use object|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
254
     */
255
    public function getControllerFromRoute(Route $route)
256
    {
257
        $ret = false;
258
        $actionParts = explode(':', $route->getDefault('_controller'));
259
260
        if (count($actionParts) == 2) {
261
            $ret = $this->container->get($actionParts[0]);
262
        }
263
264
        return $ret;
265
    }
266
}
267