Passed
Push — master ( b12120...7f2d87 )
by Valentin
04:10 queued 01:33
created

CycleInterceptor::process()   B

Complexity

Conditions 9
Paths 12

Size

Total Lines 42
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 23
c 3
b 0
f 1
dl 0
loc 42
rs 8.0555
cc 9
nc 12
nop 4
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Domain;
13
14
use Cycle\ORM\ORMInterface;
15
use Spiral\Core\CoreInterceptorInterface;
16
use Spiral\Core\CoreInterface;
17
use Spiral\Core\Exception\ControllerException;
18
19
/**
20
 * Automatically resolves cycle entities based on given parameter.
21
 */
22
class CycleInterceptor implements CoreInterceptorInterface
23
{
24
    // when only one entity is presented the default parameter will be checked
25
    protected const DEFAULT_PARAMETER = 'id';
26
27
    /** @var ORMInterface @internal */
28
    protected $orm;
29
30
    /** @var array */
31
    private $cache = [];
32
33
    /**
34
     * @param ORMInterface $orm
35
     */
36
    public function __construct(ORMInterface $orm)
37
    {
38
        $this->orm = $orm;
39
    }
40
41
    /**
42
     * @inheritDoc
43
     */
44
    public function process(string $controller, string $action, array $parameters, CoreInterface $core)
45
    {
46
        $entities = $this->getDeclaredEntities($controller, $action);
47
48
        $contextCandidates = [];
49
        foreach ($entities as $parameter => $role) {
50
            $value = $this->getParameter($parameter, $parameters, count($entities) === 1);
51
            if ($value === null) {
52
                throw new ControllerException(
53
                    "Entity `{$parameter}` can not be found",
54
                    ControllerException::NOT_FOUND
55
                );
56
            }
57
58
            if (is_object($value)) {
59
                if ($this->orm->getHeap()->has($value)) {
60
                    $contextCandidates[] = $value;
61
                }
62
63
                // pre-filled
64
                continue;
65
            }
66
67
            $entity = $this->resolveEntity($role, $value);
68
            if ($entity === null) {
69
                throw new ControllerException(
70
                    "Entity `{$parameter}` can not be found",
71
                    ControllerException::NOT_FOUND
72
                );
73
            }
74
75
            $parameters[$parameter] = $entity;
76
            if ($this->orm->getHeap()->has($value)) {
77
                $contextCandidates[] = $value;
78
            }
79
        }
80
81
        if (!isset($parameters['@context']) && count($contextCandidates) === 1) {
82
            $parameters['@context'] = current($contextCandidates);
83
        }
84
85
        return $core->callAction($controller, $action, $parameters);
86
    }
87
88
    /**
89
     * @param string $role
90
     * @param array  $parameters
91
     * @param bool   $useDefault
92
     * @return mixed
93
     */
94
    protected function getParameter(string $role, array $parameters, bool $useDefault = false)
95
    {
96
        if (!$useDefault) {
97
            return $parameters[$role] ?? null;
98
        }
99
100
        return $parameters[$role] ?? $parameters[self::DEFAULT_PARAMETER] ?? null;
101
    }
102
103
    /**
104
     * @param string $role
105
     * @param mixed  $parameter
106
     * @return object|null
107
     */
108
    protected function resolveEntity(string $role, $parameter): ?object
109
    {
110
        return $this->orm->getRepository($role)->findByPK($parameter);
111
    }
112
113
    /**
114
     * @param string $controller
115
     * @param string $action
116
     * @return array
117
     */
118
    private function getDeclaredEntities(string $controller, string $action): array
119
    {
120
        $key = sprintf('%s:%s', $controller, $action);
121
        if (array_key_exists($key, $this->cache)) {
122
            return $this->cache[$key];
123
        }
124
125
        $this->cache[$key] = [];
126
        try {
127
            $method = new \ReflectionMethod($controller, $action);
128
        } catch (\ReflectionException $e) {
129
            return [];
130
        }
131
132
        foreach ($method->getParameters() as $parameter) {
133
            if ($parameter->getClass() === null) {
134
                continue;
135
            }
136
137
            if ($this->orm->getSchema()->defines($parameter->getClass()->getName())) {
138
                $this->cache[$key][$parameter->getName()] = $this->orm->resolveRole($parameter->getClass()->getName());
139
            }
140
        }
141
142
        return $this->cache[$key];
143
    }
144
}
145