Test Failed
Push — master ( 0fcd0f...4e8aa2 )
by Pavel
04:49 queued 16s
created

DoctrineCriteriaConfigurator   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 17
c 1
b 0
f 0
lcom 1
cbo 5
dl 0
loc 106
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
D configure() 0 41 9
A filterDoctrineProperty() 0 15 4
A getRootEntities() 0 18 3
1
<?php
2
3
namespace ScayTrase\Api\Cruds\Adaptors\Doctrine;
4
5
use Doctrine\ORM\Query\Expr;
6
use Doctrine\ORM\QueryBuilder;
7
use ScayTrase\Api\Cruds\CriteriaConfiguratorInterface;
8
use ScayTrase\Api\Cruds\Exception\FilterException;
9
use ScayTrase\Api\Cruds\Exception\MapperException;
10
use ScayTrase\Api\Cruds\PropertyMapperInterface;
11
12
final class DoctrineCriteriaConfigurator implements CriteriaConfiguratorInterface
13
{
14
    const ALIAS_SEPARATOR = '.';
15
    /** @var  PropertyMapperInterface */
16
    private $mapper;
17
18
    /**
19
     * DoctrineCriteriaConfigurator constructor.
20
     *
21
     * @param PropertyMapperInterface $mapper
22
     */
23
    public function __construct(PropertyMapperInterface $mapper)
24
    {
25
        $this->mapper = $mapper;
26
    }
27
28
    /** {@inheritdoc} */
29
    public function configure(QueryBuilder $builder, $criteria)
30
    {
31
        if (null === $criteria) {
32
            return;
33
        }
34
35
        if (!is_array($criteria)) {
36
            throw FilterException::invalidType('array', gettype($criteria));
37
        }
38
39
        $aliases   = $builder->getRootAliases();
40
        $entities  = $this->getRootEntities($builder);
41
        $rootAlias = $aliases[0];
42
43
        try {
44
            foreach ((array)$criteria as $apiProperty => $value) {
45
                $alias = $rootAlias;
46
                if (false !== strpos($apiProperty, self::ALIAS_SEPARATOR)) {
47
                    list($alias, $apiProperty) = explode(self::ALIAS_SEPARATOR, $apiProperty, 2);
48
                    if ('' === $alias) {
49
                        $alias = $rootAlias;
50
                    }
51
                }
52
53
                if (!in_array($alias, $aliases, true)) {
54
                    throw FilterException::unknown($apiProperty);
55
                }
56
57
                $entity         = $entities[$alias];
58
                $mappedProperty = $this->mapper->getObjectProperty($entity, $apiProperty);
59
60
                if (null !== $mappedProperty) {
61
                    $entityProperty = $alias . '.' . $mappedProperty;
62
                    $this->filterDoctrineProperty($builder, $entityProperty, $value);
63
                    unset($criteria[$apiProperty]);
64
                }
65
            }
66
        } catch (MapperException $e) {
67
            throw new FilterException(sprintf('Error getting object property: %s', $e->getMessage()));
68
        }
69
    }
70
71
    /**
72
     * @param QueryBuilder $builder
73
     * @param string       $property
74
     * @param mixed        $value
75
     *
76
     * @throws FilterException
77
     */
78
    private function filterDoctrineProperty(QueryBuilder $builder, $property, $value)
79
    {
80
        switch (true) {
81
            case is_array($value):
82
                $builder->andWhere($builder->expr()->in($property, $value));
83
                break;
84
            case null === $value:
85
                $builder->andWhere($builder->expr()->isNull($property));
86
                break;
87
            case !is_scalar($value):
88
                throw FilterException::invalid($property, $value);
89
            default:
90
                $builder->andWhere($builder->expr()->eq($property, $value));
91
        }
92
    }
93
94
    /**
95
     * @param QueryBuilder $builder
96
     *
97
     * @return string[]
98
     */
99
    private function getRootEntities(QueryBuilder $builder)
100
    {
101
        $entities = [];
102
103
        foreach ((array)$builder->getDQLPart('from') as $fromClause) {
104
            if (is_string($fromClause)) {
105
                $spacePos = strrpos($fromClause, ' ');
106
                $from     = substr($fromClause, 0, $spacePos);
107
                $alias    = substr($fromClause, $spacePos + 1);
108
109
                $fromClause = new Expr\From($from, $alias);
110
            }
111
112
            $entities[$fromClause->getAlias()] = $fromClause->getFrom();
113
        }
114
115
        return $entities;
116
    }
117
}
118