Completed
Push — 2.0 ( 0357a9...3fe951 )
by Peter
08:22 queued 10s
created

LogicX::isSatisfiedBy()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.4444
c 0
b 0
f 0
cc 8
nc 8
nop 1
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification\Logic;
16
17
use Doctrine\ORM\QueryBuilder;
18
use Happyr\DoctrineSpecification\Filter\Filter;
19
use Happyr\DoctrineSpecification\Filter\Satisfiable;
20
use Happyr\DoctrineSpecification\Query\QueryModifier;
21
use Happyr\DoctrineSpecification\Specification\Specification;
22
23
/**
24
 * This class should be used when you combine two or more Expressions.
25
 */
26
abstract class LogicX implements Specification
27
{
28
    const AND_X = 'andX';
29
30
    const OR_X = 'orX';
31
32
    /**
33
     * @var string
34
     */
35
    private $expression;
36
37
    /**
38
     * @var Filter[]|QueryModifier[]
39
     */
40
    private $children;
41
42
    /**
43
     * Take two or more Expression as parameters.
44
     *
45
     * @param string               $expression
46
     * @param Filter|QueryModifier ...$children
47
     */
48
    public function __construct(string $expression, ...$children)
49
    {
50
        $this->expression = $expression;
51
        $this->children = $children;
0 ignored issues
show
Documentation Bug introduced by
It seems like $children of type array<integer,object<Hap...n\Query\QueryModifier>> is incompatible with the declared type array<integer,object<Hap...n\Query\QueryModifier>> of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
52
    }
53
54
    /**
55
     * @param QueryBuilder $qb
56
     * @param string       $context
57
     *
58
     * @return string
59
     */
60
    public function getFilter(QueryBuilder $qb, string $context): string
61
    {
62
        $children = [];
63
        foreach ($this->children as $spec) {
64
            if ($spec instanceof Filter) {
65
                $filter = $spec->getFilter($qb, $context);
66
67
                if ($filter) {
68
                    $children[] = $filter;
69
                }
70
            }
71
        }
72
73
        if (!$children) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $children of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
74
            return '';
75
        }
76
77
        $expression = [$qb->expr(), $this->expression];
78
79
        if (!is_callable($expression)) {
80
            throw new \InvalidArgumentException(
81
                sprintf('Undefined "%s" method in "%s" class.', $this->expression, get_class($qb->expr()))
82
            );
83
        }
84
85
        return (string) call_user_func_array($expression, $children);
86
    }
87
88
    /**
89
     * @param QueryBuilder $qb
90
     * @param string       $context
91
     */
92
    public function modify(QueryBuilder $qb, string $context): void
93
    {
94
        foreach ($this->children as $child) {
95
            if ($child instanceof QueryModifier) {
96
                $child->modify($qb, $context);
97
            }
98
        }
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function filterCollection(iterable $collection): iterable
105
    {
106
        foreach ($collection as $candidate) {
107
            if ($this->isSatisfiedBy($candidate)) {
108
                yield $candidate;
109
            }
110
        }
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function isSatisfiedBy($candidate): bool
117
    {
118
        $has_satisfiable_children = false;
119
120
        foreach ($this->children as $child) {
121
            if (!$child instanceof Satisfiable) {
122
                continue;
123
            }
124
125
            $has_satisfiable_children = true;
126
127
            $satisfied = $child->isSatisfiedBy($candidate);
128
129
            if ($satisfied && self::OR_X === $this->expression) {
130
                return true;
131
            }
132
133
            if (!$satisfied && self::AND_X === $this->expression) {
134
                return false;
135
            }
136
        }
137
138
        return !$has_satisfiable_children || self::AND_X === $this->expression;
139
    }
140
141
    /**
142
     * Add another child to this logic tree.
143
     *
144
     * @param Filter|QueryModifier $child
145
     */
146
    protected function append($child): void
147
    {
148
        $this->children[] = $child;
149
    }
150
}
151