Passed
Push — master ( d6f2eb...39b453 )
by Iakov
02:35
created

ApiManager::addLikeConditions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 3
1
<?php
2
3
namespace Kami\ApiCoreBundle\Manager;
4
5
use Doctrine\Bundle\DoctrineBundle\Registry;
6
use Doctrine\ORM\Query;
7
use Doctrine\ORM\QueryBuilder;
8
use JMS\Serializer\Serializer;
9
use Kami\ApiCoreBundle\ApiCoreEvents;
10
use Kami\ApiCoreBundle\Event\CrudEvent;
11
use Kami\ApiCoreBundle\Form\Factory;
12
use Kami\ApiCoreBundle\Security\AccessManager;
13
use Symfony\Component\EventDispatcher\EventDispatcher;
14
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
19
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
20
21
/**
22
 * Class ApiManager
23
 *
24
 * @package Kami\ApiCoreBundle\Manager
25
 */
26
class ApiManager
27
{
28
    /**
29
     * @var Registry
30
     */
31
    private $doctrine;
32
33
34
    /**
35
     * @var Serializer
36
     */
37
    private $serializer;
38
39
    /**
40
     * @var AccessManager
41
     */
42
    private $accessManager;
43
44
    /**
45
     * @var Factory
46
     */
47
    private $formFactory;
48
49
    /**
50
     * @var EventDispatcherInterface
51
     */
52
    private $eventDispatcher;
53
54
    /**
55
     * @var int
56
     */
57
    private $perPage;
58
59
    /**
60
     * ApiManager constructor.
61
     *
62
     * @param Registry $doctrine
63
     * @param AccessManager $accessManager
64
     * @param Factory $formFactory
65
     * @param Serializer $serializer
66
     * @param EventDispatcherInterface $eventDispatcher
67
     * @param int $perPage
68
     */
69
    public function __construct(
70
        Registry $doctrine,
71
                                AccessManager $accessManager,
72
                                Factory $formFactory,
73
                                Serializer $serializer,
74
                                EventDispatcherInterface $eventDispatcher,
75
                                $perPage
76
    ) {
77
        $this->doctrine = $doctrine;
78
        $this->accessManager = $accessManager;
79
        $this->formFactory = $formFactory;
80
        $this->serializer = $serializer;
81
        $this->eventDispatcher = $eventDispatcher;
82
        $this->perPage = $perPage;
83
    }
84
85
    /**
86
     * @param Request $request
87
     * @return Response
88
     */
89
    public function getIndex(Request $request)
90
    {
91
        $entity = $this->getEntityReflectionFromRequest($request);
92
        $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_INDEX_REQUEST, new CrudEvent($entity));
93
94
        if (!$this->accessManager->canAccessResource($entity)) {
95
            throw new AccessDeniedHttpException();
96
        }
97
98
        $perPage = $request->query->getInt('per_page', $this->perPage);
99
        $currentPage = $request->query->getInt('page', 1);
100
        $data = $this->buildIndexQuery($entity, $perPage, $currentPage)->getResult();
101
102
        $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_INDEX_RESPONSE, new CrudEvent($data));
103
104
        return $this->createResponse($data, $request);
105
    }
106
107
    public function filter(Request $request)
108
    {
109
        $entity = $this->getEntityReflectionFromRequest($request);
110
111
        if(!$this->accessManager->canAccessResource($entity)) {
112
            throw new AccessDeniedHttpException();
113
        }
114
        $builder = $this
115
            ->doctrine
116
            ->getRepository($entity->getName())
117
            ->createQueryBuilder('e');
118
119
        $this->addSort($request, $entity, $builder);
120
121
        foreach ($entity->getProperties() as $property) {
122
            $this->addLikeConditions($request, $property->getName(), $builder);
123
        }
124
125
        $countBuilder = clone $builder;
126
127
        return $this->createResponse([
128
            'total' => $countBuilder->select('count(e.id)')->getQuery()->getSingleScalarResult(),
129
            'rows' => $builder
130
                ->setMaxResults($request->get('limit') ? $request->get('limit') : 20)
131
                ->setFirstResult($request->get('offset') ? $request->get('offset') : 0)
132
                ->getQuery()
133
                ->getResult()
134
        ], $request);
135
136
    }
137
138
139
    /**
140
     * @param Request $request
141
     * @return Response
142
     */
143
    public function getSingleResource(Request $request)
144
    {
145
        $entity = $this->getEntityReflectionFromRequest($request);
146
        $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_REQUEST, new CrudEvent($entity));
147
        if (!$this->accessManager->canAccessResource($entity)) {
148
            throw new AccessDeniedHttpException();
149
        }
150
151
        $entity = $this->doctrine->getManager()->getRepository($entity->getName())->find($request->get('id'));
152
153
        if (!$entity) {
154
            throw new NotFoundHttpException();
155
        }
156
157
        $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_RESPONSE, new CrudEvent($entity));
158
        return $this->createResponse($entity, $request);
159
    }
160
161
    /**
162
     * @param Request $request
163
     * @return Response
164
     */
165
    public function createResource(Request $request)
166
    {
167
        $entity = $this->getEntityReflectionFromRequest($request);
168
169
        $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_CREATE, new CrudEvent($entity));
170
        if (!$this->accessManager->canCreateResource($entity)) {
171
            throw new AccessDeniedHttpException();
172
        }
173
174
        $className = $entity->getName();
175
        $persistentObject = new $className;
176
177
        $form = $this->formFactory->getCreateForm($persistentObject);
178
        $form->handleRequest($request);
179
180
        if ($form->isSubmitted() && $form->isValid()) {
181
            $this->doctrine->getManager()->persist($persistentObject);
182
            $this->doctrine->getManager()->flush();
183
184
            $this->eventDispatcher->dispatch(ApiCoreEvents::RESOURCE_CREATED, new CrudEvent($persistentObject));
185
            return $this->createResponse($persistentObject, $request);
186
        }
187
188
        return $this->createResponse($form->getErrors(), $request, 400);
189
    }
190
191
    /**
192
     * @param Request $request
193
     * @return Response
194
     */
195
    public function editResource(Request $request)
196
    {
197
        $entity = $this->getEntityReflectionFromRequest($request);
198
        if (!$this->accessManager->canEditResource($entity)) {
199
            throw new AccessDeniedHttpException();
200
        }
201
202
        $persistentObject = $this->doctrine->getManager()
203
            ->getRepository($entity->getName())
204
            ->find($request->get('id'));
205
206
        if (!$persistentObject) {
207
            throw new NotFoundHttpException();
208
        }
209
210
        $form = $this->formFactory->getEditForm($persistentObject);
211
        $form->handleRequest($request);
212
213
        if ($form->isSubmitted() && $form->isValid()) {
214
            $this->doctrine->getManager()->persist($persistentObject);
215
            $this->doctrine->getManager()->flush();
216
217
            return $this->createResponse($persistentObject, $request);
218
        }
219
220
        return $this->createResponse($form->getErrors(), $request, 400);
221
    }
222
223
    /**
224
     * @param Request $request
225
     *
226
     * @throws AccessDeniedHttpException
227
     * @throws BadRequestHttpException
228
     * @throws NotFoundHttpException
229
     *
230
     * @return Response
231
     */
232
    public function deleteResource(Request $request)
233
    {
234
        $entity = $this->getEntityReflectionFromRequest($request);
235
236
        if (!$this->accessManager->canDeleteResource($entity)) {
237
            throw new AccessDeniedHttpException();
238
        }
239
240
        $entity = $this->doctrine->getManager()
241
            ->getRepository($entity->getName())
242
            ->find($request->get('id'));
243
244
        if (!$entity) {
245
            throw new NotFoundHttpException();
246
        }
247
248
        $this->doctrine->getManager()->remove($entity);
249
        $this->doctrine->getManager()->flush();
250
251
        return $this->createResponse('', $request, 201);
252
    }
253
254
    /**
255
     * @param \ReflectionClass $entity
256
     * @param $perPage
257
     * @param $currentPage
258
     * @return Query
259
     */
260
    private function buildIndexQuery(\ReflectionClass $entity, $perPage, $currentPage)
261
    {
262
        /** @var QueryBuilder $queryBuilder */
263
        $queryBuilder = $this->doctrine->getManager()
264
            ->createQueryBuilder()
265
            ->select('e')
266
            ->from($entity->getName(), 'e')
267
            ->setMaxResults($perPage)
268
            ->setFirstResult($perPage*($currentPage - 1))
269
        ;
270
271
        return $queryBuilder->getQuery();
272
    }
273
274
    /**
275
     * @param Request $request
276
     * @param $entity
277
     * @param $builder
278
     */
279
    private function addSort(Request $request, \ReflectionClass $entity, QueryBuilder $builder)
280
    {
281
        $sort = $request->get('sort');
282
        if ($sort) {
283
            $sort = lcfirst(implode('', array_map(function ($key) {
284
                return ucfirst($key);
285
            }, explode('_', $sort))));
286
287
            if (!$entity->hasProperty($sort)) {
288
                throw new BadRequestHttpException(sprintf('There is no such field %s', $sort));
289
            }
290
            $builder->orderBy(
291
                'e.' . $sort,
292
                in_array($request->get('order'), ['asc', 'desc']) ? $request->get('order') : 'desc'
293
            );
294
        }
295
    }
296
297
    /**
298
     * @param Request $request
299
     * @param $name
300
     * @param $builder
301
     */
302
    private function addLikeConditions(Request $request, $name, QueryBuilder $builder)
303
    {
304
        if ($value = $request->get($name)) {
305
            $builder->andWhere($builder->expr()->orX(
306
                $builder->expr()->like('e.'.$name, ':value'.$name)
307
            ));
308
            $builder->setParameter('value'.$name, '%'.$value.'%');
309
        }
310
    }
311
312
    /**
313
     * @param mixed $data
314
     * @param Request $request
315
     * @param int $status
316
     *
317
     * @return Response
318
     */
319
    private function createResponse($data, Request $request, $status = 200)
320
    {
321
        $format = $request->attributes->get('_format');
322
323
        return new Response(
324
            $this->serializer->serialize($data, $format),
325
            $status,
326
            ['Content-type' => $this->getContentTypeByFormat($format)]
327
        );
328
    }
329
330
    private function getEntityReflectionFromRequest(Request $request)
331
    {
332
        try {
333
            return new \ReflectionClass($request->attributes->get('_entity'));
334
        } catch (\ReflectionException $e) {
335
            throw new BadRequestHttpException();
336
        }
337
    }
338
339
    /**
340
     * @param string $format
341
     * @return string
342
     */
343
    private function getContentTypeByFormat($format)
344
    {
345
        switch ($format) {
346
            case 'json':
347
                return 'application/json';
348
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
349
            case 'xml':
350
                return 'application/xml';
351
                break;
352
            default:
353
                return 'text/plain';
354
                break;
355
        }
356
    }
357
}
358