Passed
Pull Request — master (#32)
by Rustam
02:02
created

RequestModelFactory::parameterIsRequestModel()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6.0359

Importance

Changes 0
Metric Value
cc 6
eloc 9
c 0
b 0
f 0
nc 7
nop 2
dl 0
loc 18
ccs 9
cts 10
cp 0.9
crap 6.0359
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\RequestModel;
6
7
use Psr\Http\Message\ServerRequestInterface;
8
use ReflectionClass;
9
use ReflectionException;
10
use ReflectionNamedType;
11
use ReflectionParameter;
12
use ReflectionUnionType;
13
use Yiisoft\Injector\Injector;
14
use Yiisoft\Router\CurrentRouteInterface;
15
use Yiisoft\Validator\RulesProviderInterface;
16
use Yiisoft\Validator\ValidatorInterface;
17
18
final class RequestModelFactory
19
{
20
    private Injector $injector;
21
    private ValidatorInterface $validator;
22
    private CurrentRouteInterface $currentRoute;
23
24 16
    public function __construct(ValidatorInterface $validator, Injector $injector, CurrentRouteInterface $currentRoute)
25
    {
26 16
        $this->validator = $validator;
27 16
        $this->injector = $injector;
28 16
        $this->currentRoute = $currentRoute;
29 16
    }
30
31
    /**
32
     * @param ServerRequestInterface $request
33
     * @param ReflectionParameter[] $handlerParameters
34
     *
35
     * @throws ReflectionException
36
     *
37
     * @return array
38
     */
39 10
    public function createInstances(ServerRequestInterface $request, array $handlerParameters): array
40
    {
41 10
        $requestModelInstances = [];
42 10
        foreach ($this->getModelRequestClasses($handlerParameters) as $modelClass) {
43
            /** @var RequestModelInterface $modelInstance */
44 9
            $modelInstance = $this->injector->make($modelClass);
45 9
            $requestModelInstances[] = $this->processModel($request, $modelInstance);
46
        }
47
48 9
        return $requestModelInstances;
49
    }
50
51 9
    private function processModel(ServerRequestInterface $request, RequestModelInterface $model): RequestModelInterface
52
    {
53 9
        $requestData = $this->getRequestData($request);
54 9
        $model->setRequestData($requestData);
55 9
        if ($model instanceof RulesProviderInterface) {
56 2
            $result = $this->validator->validate($model, $model->getRules());
57 2
            if (!$result->isValid()) {
58 1
                throw new RequestValidationException($result->getErrors());
59
            }
60
        }
61
62 8
        return $model;
63
    }
64
65
    /**
66
     * @param ReflectionParameter[] $handlerParameters
67
     *
68
     * @return array
69
     */
70 10
    private function getModelRequestClasses(array $handlerParameters): array
71
    {
72 10
        $modelClasses = [];
73 10
        foreach ($handlerParameters as $parameter) {
74 10
            if ($this->parameterIsRequestModel($parameter, $parameterType)) {
75
                /** @var ReflectionNamedType $parameterType */
76 9
                $modelClasses[] = $parameterType->getName();
77
            }
78
        }
79
80 10
        return $modelClasses;
81
    }
82
83
    /**
84
     * @param ReflectionParameter $parameter
85
     * @param ReflectionNamedType|ReflectionUnionType $parameterType
86
     *
87
     * @throws ReflectionException
88
     *
89
     * @return bool
90
     */
91 10
    private function parameterIsRequestModel(ReflectionParameter $parameter, &$parameterType): bool
92
    {
93 10
        if (!$parameter->hasType()) {
94
            return false;
95
        }
96
        /** @var ReflectionNamedType|ReflectionUnionType $reflectionType */
97 10
        $reflectionType = $parameter->getType();
98
99
        /** @var ReflectionNamedType[] $types */
100 10
        $types = $reflectionType instanceof ReflectionNamedType ? [$reflectionType] : $reflectionType->getTypes();
101
102 10
        foreach ($types as $type) {
103 10
            if (!$type->isBuiltin() && (new ReflectionClass($type->getName()))->implementsInterface(RequestModelInterface::class)) {
104 9
                $parameterType = $type;
105 9
                return true;
106
            }
107
        }
108 1
        return false;
109
    }
110
111 9
    private function getRequestData(ServerRequestInterface $request): array
112
    {
113
        return [
114 9
            'query' => $request->getQueryParams(),
115 9
            'body' => $request->getParsedBody(),
116 9
            'attributes' => $request->getAttributes(),
117 9
            'headers' => $request->getHeaders(),
118 9
            'files' => $request->getUploadedFiles(),
119 9
            'cookie' => $request->getCookieParams(),
120 9
            'router' => $this->currentRoute->getArguments(),
121
        ];
122
    }
123
}
124