Service   B
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 85.15%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 36
lcom 1
cbo 11
dl 0
loc 300
ccs 86
cts 101
cp 0.8515
rs 8.8
c 3
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A setUpAndRunRequest() 0 6 2
A __construct() 0 5 1
C setupRequest() 0 26 7
A getActionInstance() 0 20 4
A runCallMethod() 0 14 2
B getDefaultAction() 0 25 6
A getServiceActionRegistry() 0 4 1
A setMatchedRoute() 0 4 1
A getMatchedRoute() 0 4 1
A setRepresentation() 0 4 1
A getRepresentation() 0 4 1
A setErrorHandler() 0 4 1
A getEntityManager() 0 4 1
A getEntityManagerRegistry() 0 4 1
A getDrestManager() 0 4 1
A getResponse() 0 4 1
A getRequest() 0 4 1
A handleError() 0 12 2
A renderDeterminedRepresentation() 0 5 1
1
<?php
2
/**
3
 * This file is part of the Drest package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author Lee Davis
9
 * @copyright Copyright (c) Lee Davis <@leedavis81>
10
 * @link https://github.com/leedavis81/drest/blob/master/LICENSE
11
 * @license http://opensource.org/licenses/MIT The MIT X License (MIT)
12
 */
13
namespace Drest;
14
15
use Doctrine\ORM\EntityManager;
16
use Drest\Mapping\RouteMetaData;
17
use Drest\Service\Action\AbstractAction;
18
use DrestCommon\Error\Handler\AbstractHandler;
19
use DrestCommon\Error\Response\ResponseInterface;
20
use DrestCommon\Representation;
21
use DrestCommon\Request\Request;
22
use DrestCommon\Response\Response;
23
use DrestCommon\ResultSet;
24
25
/**
26
 * Class Service handles all attributes of a single service call.
27
 * Is also injected into custom service actions
28
 * @package Drest
29
 */
30
class Service
31
{
32
    /**
33
     * Drest Manager
34
     * @var Manager $dm
35
     */
36
    protected $dm;
37
38
    /**
39
     * Service Action Registry
40
     * @var Service\Action\Registry
41
     */
42
    protected $service_action_registry;
43
44
    /**
45
     * When a route object is matched, it's injected into the service class
46
     * @var RouteMetaData $route
47
     */
48
    protected $matched_route;
49
50
    /**
51
     * Service action instance determined
52
     * @var AbstractAction $service_action
53
     */
54
    private $service_action;
55
56
    /**
57
     * A representation instance determined either from configuration or client media accept type - can be null if none matched
58
     * Note this will be present on both a fetch (GET) request and on a push (POST / PUT) request if using the drest client
59
     * @var Representation\AbstractRepresentation $representation
60
     */
61
    protected $representation;
62
63
    /**
64
     * The error handler instance
65
     * @var AbstractHandler $error_handler
66
     */
67
    protected $error_handler;
68
69
    /**
70
     * Initialise a new instance of a Drest service
71
     * @param Manager                   $dm - The Drest Manager object
72
     * @param Service\Action\Registry   $service_action_registry - registry use to look up service actions
73
     */
74 31
    public function __construct(Manager $dm, Service\Action\Registry $service_action_registry)
75
    {
76 31
        $this->dm = $dm;
77 31
        $this->service_action_registry = $service_action_registry;
78 31
    }
79
80
81
    /**
82
     * Set up and run the required call method
83
     */
84 25
    public function setUpAndRunRequest()
85
    {
86 25
        if ($this->setupRequest()) {
87 25
            $this->runCallMethod();
88 25
        }
89 25
    }
90
91
    /**
92
     * Called on successful routing of a service call
93
     * Prepares the service to a request to be rendered
94
     *
95
     * @return boolean $result - if false then fail fast no call to runCallMethod() should be made.
96
     */
97 25
    protected function setupRequest()
98
    {
99
        // Make sure we have a route matched (this should caught and an exception thrown on Manager::determineRoute())
100 25
        if (!$this->matched_route instanceof RouteMetaData) {
101
            return false;
102
        }
103
104
        // Proceed to run the service action
105 25
        if ($this->matched_route->isExposeDisabled())
106 25
        {
107
            return true;
108
        }
109
110
        // If its a GET request and no expose fields are present, fail early
111 25
        if ($this->getRequest()->getHttpMethod() == Request::METHOD_GET)
112 25
        {
113 18
            $expose = $this->matched_route->getExpose();
114 18
            if (count($expose) === 0 || (count($expose) == 1 && empty($expose[0])))
115 18
            {
116
                $this->renderDeterminedRepresentation($this->getActionInstance()->createResultSet(array()));
117
                return false;
118
            }
119 18
        }
120
121 25
        return true;
122
    }
123
124
    /**
125
     * Get an instance of the action class to be used
126
     * @throws DrestException
127
     * @return AbstractAction $action
128
     */
129 25
    protected function getActionInstance()
130
    {
131 25
        if (!isset($this->service_action))
132 25
        {
133 25
            if ($this->service_action_registry->hasServiceAction($this->matched_route))
134 25
            {
135
                $this->service_action = $this->service_action_registry->getServiceAction($this->matched_route);
136
                $this->service_action->setService($this);
137
            } else
138
            {
139 25
                $this->service_action = $this->getDefaultAction();
140
            }
141
142 25
            if (!$this->service_action instanceof AbstractAction) {
143
                throw DrestException::actionClassNotAnInstanceOfActionAbstract(get_class($this->service_action));
144
            }
145 25
        }
146
147 25
        return $this->service_action;
148
    }
149
150
    /**
151
     * Run the call method required on this service object
152
     */
153 25
    final public function runCallMethod()
154
    {
155
        // dispatch preServiceAction event
156 25
        $this->dm->triggerPreServiceActionEvent($this);
157
158 25
        $return = $this->getActionInstance()->execute();
159
160
        // dispatch postServiceAction event
161 25
        $this->dm->triggerPostServiceActionEvent($this);
162
163 25
        if ($return instanceof ResultSet) {
164 20
            $this->renderDeterminedRepresentation($return);
165 20
        }
166 25
    }
167
168
    /**
169
     * Gets an instance of the "default" action based of request information
170
     * @throws DrestException
171
     * @return AbstractAction $action
172
     */
173 25
    protected function getDefaultAction()
174
    {
175 25
        $httpMethod = ($this->dm->calledWithANamedRoute())
176 25
            ? array_slice($this->matched_route->getVerbs(), 0, 1)[0]
177 25
            : $this->getRequest()->getHttpMethod();
178
179 25
        $className = '\\Drest\\Service\\Action\\' . ucfirst(strtolower($httpMethod));
180
        switch ($httpMethod) {
181 25
            case Request::METHOD_GET:
182 25
            case Request::METHOD_DELETE:
183 22
                $className .= ($this->matched_route->isCollection()) ? 'Collection' : 'Element';
184 22
                break;
185 3
            default:
186 3
                $className .= 'Element';
187 3
                break;
188 3
        }
189 25
        if (!class_exists($className)) {
190
            throw DrestException::unknownActionClass($className);
191
        }
192
193
        /** @var AbstractAction $object */
194 25
        $object = new $className();
195 25
        $object->setService($this);
196 25
        return $object;
197
    }
198
199
    /**
200
     * Get the service action registry
201
     * @return Service\Action\Registry $service_action_registry
202
     */
203
    public function getServiceActionRegistry()
204
    {
205
        return $this->service_action_registry;
206
    }
207
208
    /**
209
     * Set the matched route object
210
     * @param RouteMetaData $matched_route
211
     */
212 25
    public function setMatchedRoute(RouteMetaData $matched_route)
213
    {
214 25
        $this->matched_route = $matched_route;
215 25
    }
216
217
    /**
218
     * Get the route object that was matched
219
     * @return RouteMetaData $matched_route
220
     */
221 25
    public function getMatchedRoute()
222
    {
223 25
        return $this->matched_route;
224
    }
225
226
    /**
227
     * Set any predetermined representation instance
228
     * @param Representation\AbstractRepresentation $representation
229
     */
230 25
    public function setRepresentation(Representation\AbstractRepresentation $representation)
231
    {
232 25
        $this->representation = $representation;
233 25
    }
234
235
    /**
236
     * Get the predetermined representation
237
     * @return Representation\AbstractRepresentation
238
     */
239 3
    public function getRepresentation()
240
    {
241 3
        return $this->representation;
242
    }
243
244
    /**
245
     * Set the error handler object
246
     * @param AbstractHandler $error_handler
247
     */
248 25
    public function setErrorHandler(AbstractHandler $error_handler)
249
    {
250 25
        $this->error_handler = $error_handler;
251 25
    }
252
253
    /**
254
     * Get the entity manager
255
     * This will return the default manager, to choose a specific one use getEntityManagerRegistry()
256
     * @return EntityManager $em
257
     */
258
    public function getEntityManager()
259
    {
260
        return $this->getEntityManagerRegistry()->getManager();
261
    }
262
263
    /**
264
     * Get the entity manager registry
265
     * @return EntityManagerRegistry
266
     */
267 25
    public function getEntityManagerRegistry()
268
    {
269 25
        return $this->dm->getEntityManagerRegistry();
270
    }
271
272
    /**
273
     * Get the Drest Manager
274
     * @return Manager $dm
275
     */
276
    public function getDrestManager()
277
    {
278
        return $this->dm;
279
    }
280
281
    /**
282
     * Get the response object
283
     * @return Response $response
284
     */
285 25
    public function getResponse()
286
    {
287 25
        return $this->dm->getResponse();
288
    }
289
290
    /**
291
     * Get the request object
292
     * @return Request $request
293
     */
294 25
    public function getRequest()
295
    {
296 25
        return $this->dm->getRequest();
297
    }
298
299
    /**
300
     * Handle an error - set the resulting error document to the response object
301
     * @param  \Exception        $e
302
     * @param  integer           $defaultResponseCode the default response code to use if no match on exception type occurs
303
     * @param  ResponseInterface $errorDocument
304
     * @return ResultSet         the error result set
305
     */
306 5
    public function handleError(\Exception $e, $defaultResponseCode = 500, ResponseInterface $errorDocument = null)
307
    {
308 5
        if (is_null($errorDocument)) {
309 5
            $errorDocument = $this->representation->getDefaultErrorResponse();
310 5
        }
311
312 5
        $this->error_handler->error($e, $defaultResponseCode, $errorDocument);
313
314 5
        $this->getResponse()->setStatusCode($this->error_handler->getResponseCode());
315 5
        $this->getResponse()->setHttpHeader('Content-Type', $errorDocument::getContentType());
316 5
        $this->getResponse()->setBody($errorDocument->render());
317 5
    }
318
319
320
    /**
321
     * Write out as result set on the representation object that was determined - if no representation has been determined - defaults to text
322
     * @param ResultSet $resultSet
323
     */
324 20
    public function renderDeterminedRepresentation(ResultSet $resultSet)
325
    {
326 20
        $this->getResponse()->setBody($this->representation->output($resultSet));
327 20
        $this->getResponse()->setHttpHeader('Content-Type', $this->representation->getContentType());
328 20
    }
329
}
330