Passed
Push — master ( ed8525...da3e44 )
by Iakov
03:51
created

BuildSelectQueryStep   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 113
rs 10
c 0
b 0
f 0
wmc 20

7 Methods

Rating   Name   Duplication   Size   Complexity  
A requiresBefore() 0 4 1
A execute() 0 14 2
A join() 0 14 3
B getTarget() 0 21 5
A addSelectIfEligible() 0 8 3
B isRelation() 0 8 5
A __construct() 0 4 1
1
<?php
2
3
namespace Kami\ApiCoreBundle\RequestProcessor\Step\Common;
4
5
6
use Doctrine\Common\Annotations\Reader;
7
use Doctrine\Common\Util\Inflector;
8
use Doctrine\ORM\Mapping\ManyToMany;
9
use Doctrine\ORM\Mapping\ManyToOne;
10
use Doctrine\ORM\Mapping\OneToMany;
11
use Doctrine\ORM\Mapping\OneToOne;
12
use Doctrine\ORM\QueryBuilder;
13
use Kami\ApiCoreBundle\Annotation\Relation;
14
use Kami\ApiCoreBundle\RequestProcessor\ProcessingException;
15
use Kami\ApiCoreBundle\RequestProcessor\Step\AbstractStep;
16
use Kami\ApiCoreBundle\Security\AccessManager;
17
18
class BuildSelectQueryStep extends AbstractStep
19
{
20
    /**
21
     * @var AccessManager
22
     */
23
    private $accessManager;
24
25
    /**
26
     * @var Reader
27
     */
28
    private $reader;
29
30
    /**
31
     * @var array
32
     */
33
    protected $aliases = [];
34
35
    private $accessible = [];
36
37
    public function __construct(AccessManager $accessManager, Reader $reader)
38
    {
39
        $this->accessManager = $accessManager;
40
        $this->reader = $reader;
41
    }
42
43
    public function execute()
44
    {
45
        /** @var \ReflectionClass $reflection */
46
        $reflection = $this->getFromResponse('reflection');
47
        /** @var QueryBuilder $queryBuilder */
48
        $queryBuilder = $this->getFromResponse('query_builder');
49
        $queryBuilder->from($reflection->getName(), 'e');
50
51
        foreach ($reflection->getProperties() as $property) {
52
            $this->addSelectIfEligible($property, $queryBuilder);
53
        }
54
        $queryBuilder->addSelect(sprintf('partial e.{id, %s}', implode(', ', $this->accessible)));
55
56
        return $this->createResponse(['query_builder' => $queryBuilder]);
57
    }
58
59
60
    public function requiresBefore()
61
    {
62
        return [
63
            GetQueryBuilderStep::class
64
        ];
65
    }
66
67
    /**
68
     * @param $property
69
     * @param $queryBuilder
70
     */
71
    protected function addSelectIfEligible(\ReflectionProperty $property, QueryBuilder $queryBuilder)
72
    {
73
        if ($this->accessManager->canAccessProperty($property)) {
74
            if (!$this->isRelation($property)) {
75
                $this->accessible[] = $property->getName();
76
                return;
77
            }
78
            $this->join($property, $queryBuilder);
79
        }
80
81
    }
82
83
    protected function isRelation(\ReflectionProperty $property)
84
    {
85
        return (
86
            $this->reader->getPropertyAnnotation($property, Relation::class) ||
87
            $this->reader->getPropertyAnnotation($property, OneToOne::class) ||
88
            $this->reader->getPropertyAnnotation($property, OneToMany::class) ||
89
            $this->reader->getPropertyAnnotation($property, ManyToOne::class) ||
90
            $this->reader->getPropertyAnnotation($property, ManyToMany::class)
91
        );
92
    }
93
94
    protected function join(\ReflectionProperty $property, QueryBuilder $queryBuilder)
95
    {
96
        $target = $this->getTarget($property);
97
        $alias = Inflector::tableize($property->getName());
98
        $queryBuilder->join(sprintf('e.%s', $property->getName()), $alias);
99
        $accessible = [];
100
101
        foreach ($target->getProperties() as $property) {
102
            if ($this->accessManager->canAccessProperty($property)) {
103
                $accessible[] = $property->getName();
104
            }
105
        }
106
107
        $queryBuilder->addSelect(sprintf('partial %s.{%s}', $alias, implode(',', $accessible)));
108
    }
109
110
    protected function getTarget(\ReflectionProperty $property)
111
    {
112
        $target = '';
113
114
        $relation = $this->reader->getPropertyAnnotation($property, Relation::class);
115
        if ($relation->target) {
116
            $target = $relation->target;
117
        } else {
118
            foreach ([OneToOne::class, OneToMany::class, ManyToOne::class, ManyToMany::class] as $possibility) {
119
                if ($annotation = $this->reader->getPropertyAnnotation($property, $possibility)) {
120
                    $target = $annotation->targetEntity;
121
                    break;
122
                }
123
            }
124
        }
125
126
        try {
127
            return new \ReflectionClass($target);
128
        } catch (\ReflectionException $e) {
129
            throw new ProcessingException(sprintf(
130
                'Could not find target entity for relation %s', $property->getName())
131
            );
132
        }
133
    }
134
}