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

RequestModelFactory::getRequestData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

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