Completed
Pull Request — master (#9)
by Roman
03:37
created

BuildSelectQueryStep::isRelation()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 6
nc 5
nop 1
dl 0
loc 8
rs 8.8571
c 0
b 0
f 0
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\Graph\Relation\DoctrineRelation;
0 ignored issues
show
Bug introduced by
The type Kami\ApiCoreBundle\Graph\Relation\DoctrineRelation was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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