Complex classes like RestController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RestController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 41 | class RestController |
||
| 42 | { |
||
| 43 | /** |
||
| 44 | * @var DocumentModel |
||
| 45 | */ |
||
| 46 | private $model; |
||
| 47 | |||
| 48 | /** |
||
| 49 | * @var ContainerInterface service_container |
||
| 50 | */ |
||
| 51 | private $container; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @var Response |
||
| 55 | */ |
||
| 56 | private $response; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @var RestUtilsInterface |
||
| 60 | */ |
||
| 61 | private $restUtils; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * @var SchemaUtils |
||
| 65 | */ |
||
| 66 | private $schemaUtils; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * @var Router |
||
| 70 | */ |
||
| 71 | private $router; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @var EngineInterface |
||
| 75 | */ |
||
| 76 | private $templating; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * @var JsonPatchValidator |
||
| 80 | */ |
||
| 81 | private $jsonPatchValidator; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var SecurityUtils |
||
| 85 | */ |
||
| 86 | protected $securityUtils; |
||
| 87 | |||
| 88 | /** @var CollectionCache */ |
||
| 89 | protected $collectionCache; |
||
| 90 | |||
| 91 | /** |
||
| 92 | * @param Response $response Response |
||
| 93 | * @param RestUtilsInterface $restUtils Rest utils |
||
| 94 | * @param Router $router Router |
||
| 95 | * @param EngineInterface $templating Templating |
||
| 96 | * @param ContainerInterface $container Container |
||
| 97 | * @param SchemaUtils $schemaUtils Schema utils |
||
| 98 | * @param CollectionCache $cache Cache service |
||
| 99 | */ |
||
| 100 | public function __construct( |
||
| 101 | Response $response, |
||
| 102 | RestUtilsInterface $restUtils, |
||
| 103 | Router $router, |
||
| 104 | EngineInterface $templating, |
||
| 105 | ContainerInterface $container, |
||
| 106 | SchemaUtils $schemaUtils, |
||
| 107 | CollectionCache $cache |
||
| 108 | ) { |
||
| 109 | $this->response = $response; |
||
| 110 | $this->restUtils = $restUtils; |
||
| 111 | $this->router = $router; |
||
| 112 | $this->templating = $templating; |
||
| 113 | $this->container = $container; |
||
| 114 | $this->schemaUtils = $schemaUtils; |
||
| 115 | $this->collectionCache = $cache; |
||
| 116 | } |
||
| 117 | |||
| 118 | /** |
||
| 119 | * Setter for the SecurityUtils |
||
| 120 | * |
||
| 121 | * @param SecurityUtils $securityUtils The securityUtils service |
||
| 122 | * @return void |
||
| 123 | */ |
||
| 124 | public function setSecurityUtils(SecurityUtils $securityUtils) |
||
| 125 | { |
||
| 126 | $this->securityUtils = $securityUtils; |
||
| 127 | } |
||
| 128 | |||
| 129 | /** |
||
| 130 | * @param JsonPatchValidator $jsonPatchValidator Service for validation json patch |
||
| 131 | * @return void |
||
| 132 | */ |
||
| 133 | public function setJsonPatchValidator(JsonPatchValidator $jsonPatchValidator) |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Get the container object |
||
| 140 | * |
||
| 141 | * @return \Symfony\Component\DependencyInjection\ContainerInterface |
||
| 142 | * |
||
| 143 | * @obsolete |
||
| 144 | */ |
||
| 145 | public function getContainer() |
||
| 146 | { |
||
| 147 | return $this->container; |
||
| 148 | } |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Returns a single record |
||
| 152 | * |
||
| 153 | * @param Request $request Current http request |
||
| 154 | * @param string $id ID of record |
||
| 155 | * |
||
| 156 | * @return \Symfony\Component\HttpFoundation\Response $response Response with result or error |
||
| 157 | */ |
||
| 158 | public function getAction(Request $request, $id) |
||
| 159 | { |
||
| 160 | $repository = $this->model->getRepository(); |
||
| 161 | if (!$document = $this->collectionCache->getByRepository($repository, $id)) { |
||
| 162 | // Check and wait if another update is being processed |
||
| 163 | // if there is no cache else we need to wait if there is a lock or possible 404 |
||
| 164 | $this->collectionCache->updateOperationCheck($repository, $id); |
||
| 165 | $document = $this->serialize($this->findRecord($id, $request)); |
||
|
|
|||
| 166 | $this->collectionCache->setByRepository($repository, $document, $id); |
||
| 167 | } |
||
| 168 | |||
| 169 | $response = $this->getResponse() |
||
| 170 | ->setStatusCode(Response::HTTP_OK) |
||
| 171 | ->setContent($document); |
||
| 172 | |||
| 173 | return $response; |
||
| 174 | } |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Get the response object |
||
| 178 | * |
||
| 179 | * @return \Symfony\Component\HttpFoundation\Response $response Response object |
||
| 180 | */ |
||
| 181 | public function getResponse() |
||
| 182 | { |
||
| 183 | return $this->response; |
||
| 184 | } |
||
| 185 | |||
| 186 | /** |
||
| 187 | * Get a single record from database or throw an exception if it doesn't exist |
||
| 188 | * |
||
| 189 | * @param mixed $id Record id |
||
| 190 | * @param Request $request request |
||
| 191 | * |
||
| 192 | * @return object $record Document object |
||
| 193 | */ |
||
| 194 | protected function findRecord($id, Request $request = null) |
||
| 195 | { |
||
| 196 | return $this->getModel()->find($id, $request); |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * Return the model |
||
| 201 | * |
||
| 202 | * @throws \Exception in case no model was defined. |
||
| 203 | * |
||
| 204 | * @return DocumentModel $model Model |
||
| 205 | */ |
||
| 206 | public function getModel() |
||
| 214 | |||
| 215 | /** |
||
| 216 | * Set the model class |
||
| 217 | * |
||
| 218 | * @param DocumentModel $model Model class |
||
| 219 | * |
||
| 220 | * @return self |
||
| 221 | */ |
||
| 222 | public function setModel(DocumentModel $model) |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Serialize the given record and throw an exception if something went wrong |
||
| 231 | * |
||
| 232 | * @param object|object[] $result Record(s) |
||
| 233 | * |
||
| 234 | * @throws \Graviton\ExceptionBundle\Exception\SerializationException |
||
| 235 | * |
||
| 236 | * @return string $content Json content |
||
| 237 | */ |
||
| 238 | protected function serialize($result) |
||
| 239 | { |
||
| 240 | $response = $this->getResponse(); |
||
| 241 | |||
| 242 | try { |
||
| 243 | // array is serialized as an object {"0":{...},"1":{...},...} when data contains an empty objects |
||
| 244 | // we serialize each item because we can assume this bug affects only root array element |
||
| 245 | if (is_array($result) && array_keys($result) === range(0, count($result) - 1)) { |
||
| 246 | $result = array_map( |
||
| 247 | function ($item) { |
||
| 248 | return $this->getRestUtils()->serializeContent($item); |
||
| 249 | }, |
||
| 250 | $result |
||
| 251 | ); |
||
| 252 | |||
| 253 | return '['.implode(',', array_filter($result)).']'; |
||
| 254 | } |
||
| 255 | |||
| 256 | return $this->getRestUtils()->serializeContent($result); |
||
| 257 | } catch (\Exception $e) { |
||
| 258 | $exception = new SerializationException($e); |
||
| 259 | $exception->setResponse($response); |
||
| 260 | throw $exception; |
||
| 261 | } |
||
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * Get RestUtils service |
||
| 266 | * |
||
| 267 | * @return \Graviton\RestBundle\Service\RestUtils |
||
| 268 | */ |
||
| 269 | public function getRestUtils() |
||
| 273 | |||
| 274 | /** |
||
| 275 | * Returns all records |
||
| 276 | * |
||
| 277 | * @param Request $request Current http request |
||
| 278 | * |
||
| 279 | * @return \Symfony\Component\HttpFoundation\Response $response Response with result or error |
||
| 280 | */ |
||
| 281 | public function allAction(Request $request) |
||
| 282 | { |
||
| 283 | $model = $this->getModel(); |
||
| 284 | |||
| 285 | $response = $this->getResponse() |
||
| 286 | ->setStatusCode(Response::HTTP_OK) |
||
| 287 | ->setContent($this->serialize($model->findAll($request))); |
||
| 288 | |||
| 289 | return $response; |
||
| 290 | } |
||
| 291 | |||
| 292 | /** |
||
| 293 | * Writes a new Entry to the database |
||
| 294 | * |
||
| 295 | * @param Request $request Current http request |
||
| 296 | * |
||
| 297 | * @return \Symfony\Component\HttpFoundation\Response $response Result of action with data (if successful) |
||
| 298 | */ |
||
| 299 | public function postAction(Request $request) |
||
| 325 | |||
| 326 | /** |
||
| 327 | * Validates the current request on schema violations. If there are errors, |
||
| 328 | * the exception is thrown. If not, the deserialized record is returned. |
||
| 329 | * |
||
| 330 | * @param object|string $content \stdClass of the request content |
||
| 331 | * @param DocumentModel $model the model to check the schema for |
||
| 332 | * |
||
| 333 | * @return \Graviton\JsonSchemaBundle\Exception\ValidationExceptionError[] |
||
| 334 | * @throws \Exception |
||
| 335 | */ |
||
| 336 | protected function validateRequest($content, DocumentModel $model) |
||
| 344 | |||
| 345 | /** |
||
| 346 | * Deserialize the given content throw an exception if something went wrong |
||
| 347 | * |
||
| 348 | * @param string $content Request content |
||
| 349 | * @param string $documentClass Document class |
||
| 350 | * |
||
| 351 | * @throws DeserializationException |
||
| 352 | * |
||
| 353 | * @return object $record Document |
||
| 354 | */ |
||
| 355 | protected function deserialize($content, $documentClass) |
||
| 377 | |||
| 378 | /** |
||
| 379 | * Get the router from the dic |
||
| 380 | * |
||
| 381 | * @return Router |
||
| 382 | */ |
||
| 383 | public function getRouter() |
||
| 387 | |||
| 388 | /** |
||
| 389 | * Update a record |
||
| 390 | * |
||
| 391 | * @param Number $id ID of record |
||
| 392 | * @param Request $request Current http request |
||
| 393 | * |
||
| 394 | * @throws MalformedInputException |
||
| 395 | * |
||
| 396 | * @return Response $response Result of action with data (if successful) |
||
| 397 | */ |
||
| 398 | public function putAction($id, Request $request) |
||
| 442 | |||
| 443 | /** |
||
| 444 | * Patch a record |
||
| 445 | * |
||
| 446 | * @param Number $id ID of record |
||
| 447 | * @param Request $request Current http request |
||
| 448 | * |
||
| 449 | * @throws MalformedInputException |
||
| 450 | * |
||
| 451 | * @return Response $response Result of action with data (if successful) |
||
| 452 | */ |
||
| 453 | public function patchAction($id, Request $request) |
||
| 505 | |||
| 506 | /** |
||
| 507 | * Deletes a record |
||
| 508 | * |
||
| 509 | * @param Number $id ID of record |
||
| 510 | * |
||
| 511 | * @return Response $response Result of the action |
||
| 512 | */ |
||
| 513 | public function deleteAction($id) |
||
| 532 | |||
| 533 | /** |
||
| 534 | * Return OPTIONS results. |
||
| 535 | * |
||
| 536 | * @param Request $request Current http request |
||
| 537 | * |
||
| 538 | * @throws SerializationException |
||
| 539 | * @return \Symfony\Component\HttpFoundation\Response $response Result of the action |
||
| 540 | */ |
||
| 541 | public function optionsAction(Request $request) |
||
| 562 | |||
| 563 | |||
| 564 | /** |
||
| 565 | * Return schema GET results. |
||
| 566 | * |
||
| 567 | * @param Request $request Current http request |
||
| 568 | * @param string $id ID of record |
||
| 569 | * |
||
| 570 | * @throws SerializationException |
||
| 571 | * @return \Symfony\Component\HttpFoundation\Response $response Result of the action |
||
| 572 | */ |
||
| 573 | public function schemaAction(Request $request, $id = null) |
||
| 608 | |||
| 609 | /** |
||
| 610 | * Renders a view. |
||
| 611 | * |
||
| 612 | * @param string $view The view name |
||
| 613 | * @param array $parameters An array of parameters to pass to the view |
||
| 614 | * @param Response $response A response instance |
||
| 615 | * |
||
| 616 | * @return Response A Response instance |
||
| 617 | */ |
||
| 618 | public function render($view, array $parameters = array(), Response $response = null) |
||
| 622 | |||
| 623 | /** |
||
| 624 | * @param Request $request request |
||
| 625 | * @return string |
||
| 626 | */ |
||
| 627 | private function getRouteName(Request $request) |
||
| 639 | |||
| 640 | /** |
||
| 641 | * Security needs to be enabled to get Object. |
||
| 642 | * |
||
| 643 | * @return String |
||
| 644 | * @throws UsernameNotFoundException |
||
| 645 | */ |
||
| 646 | public function getSecurityUser() |
||
| 650 | } |
||
| 651 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.