Completed
Push — develop ( f03c1d...7ff4c1 )
by Sander
02:01
created

QueriesTaxonomies   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 73.68%

Importance

Changes 0
Metric Value
dl 0
loc 205
ccs 56
cts 76
cp 0.7368
rs 10
c 0
b 0
f 0
wmc 25

8 Methods

Rating   Name   Duplication   Size   Complexity  
A orWhereTaxonomy() 0 3 1
A getValidTermField() 0 7 3
A whereNestedTaxonomy() 0 19 4
A parseNestedTaxonomy() 0 7 2
A addArrayOfWhereTaxonomies() 0 7 2
A extractNestedTaxonomyClosures() 0 13 3
A validateTaxonomyFields() 0 17 2
C whereTaxonomy() 0 62 8
1
<?php
2
3
namespace Sanderdekroon\Parlant\Builder;
4
5
use Closure;
6
use InvalidArgumentException;
7
8
trait QueriesTaxonomies
9
{
10 4
    public function whereTaxonomy($taxonomy, $field = null, $operator = null, $value = null, $includeChildren = true, $relation = null, $level = 1)
11
    {
12
        // If the taxonomy is an array, we will assume it is an array of key-value pairs
13
        // and can add them each as a where clause. We will maintain the boolean we
14
        // received when the method was called and pass it into the nested where.
15 4
        if (is_array($taxonomy)) {
16
            return $this->addArrayOfWhereTaxonomies($taxonomy);
17
        }
18
19
        // If the taxonomy parameter is a close we'll start a nested meta query.
20 4
        if ($taxonomy instanceof Closure) {
21 1
            $nestedTaxonomy = $this->extractNestedTaxonomyClosures($taxonomy);
22 1
            $this->appendBinding('whereTaxonomies', $this->whereNestedTaxonomy($nestedTaxonomy, $relation));
0 ignored issues
show
Unused Code introduced by
The call to Sanderdekroon\Parlant\Bu...::whereNestedTaxonomy() has too many arguments starting with $relation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

22
            $this->appendBinding('whereTaxonomies', $this->/** @scrutinizer ignore-call */ whereNestedTaxonomy($nestedTaxonomy, $relation));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
23
24 1
            return $this;
25
        }
26
27
        // If the field variable is a closure, we'll start a nested taxonomy query
28
        // and use the supplied taxonomy as default taxonomy in the query.
29 4
        if ($field instanceof Closure) {
30
            $nestedTaxonomy = $this->extractNestedTaxonomyClosures($field, $taxonomy);
31
            $this->appendBinding('whereTaxonomies', $this->whereNestedTaxonomy($nestedTaxonomy, $relation));
32
33
            return $this;
34
        }
35
36
        // Here we will make some assumptions about the operator. If only 3 values are
37
        // passed to the method, we will assume that the operator is an equals sign
38
        // and keep going. Otherwise, we'll require the operator to be passed in.
39 4
        list($value, $operator) = $this->prepareValueAndOperator(
40 4
            $value,
41 4
            $operator,
42 4
            (func_num_args() == 3 || is_null($value)),
43
            true // This is needed since tax_query has different default operator.
44 4
        );
45
46
        // If the given operator is not found in the list of valid operators we will
47
        // assume that the developer is just short-cutting the '=' operators and
48
        // we will set the operators to '=' and set the values appropriately.
49 4
        if ($this->invalidOperator($operator)) {
50
            list($value, $operator) = [$operator, 'IN'];
51
        }
52
53
        // Validate the supplied field against the known fields of an taxonomy
54
        // query within WP_Query. If no valid field is found, we'll default
55
        // back to the WordPress default which is term_id.
56 4
        $field = $this->getValidTermField($field);
57
58 4
        if (!empty($relation)) { //Do a relation check here.
59 1
            $this->setBinding('whereTaxonomyRelation', $this->getBinding('whereTaxonomyRelation') ?: [] + [$level => $relation]);
60 1
        }
61
62 4
        $this->appendBinding('whereTaxonomies', compact(
63 4
            'taxonomy',             // Taxonomy name
64 4
            'field',                // Taxonomy term field
65 4
            'operator',             // The term operator
66 4
            'value',                // The value of the term
67 4
            'includeChildren',      // Include/exclude children
68
            'level'                 // Unimplemented, undocumented.
69 4
        ));
70
71 4
        return $this;
72
    }
73
74
    /**
75
     * Query the meta values (custom post fields) of posts and set the relation to OR
76
     * @param  string       $taxonomy         The field name
77
     * @param  string       $operator
78
     * @param  mixed        $value
79
     * @param  string       $type           The type comparison, for example NUMERIC or CHAR
80
     * @param  integer      $level          The query level, currently unimplemented
81
     * @return $this
82
     */
83 1
    public function orWhereTaxonomy($taxonomy, $field = null, $operator = null, $value = null, $includeChildren = true, $level = 1)
84
    {
85 1
        return $this->whereTaxonomy($taxonomy, $field, $operator, $value, $includeChildren, 'OR', $level);
86
    }
87
88
    /**
89
     * Resolve the closures and replace them with NestedTaxonomy classes.
90
     * @param  Closure $closure
91
     * @return NestedTaxonomy
92
     */
93 1
    protected function extractNestedTaxonomyClosures($closure, $taxonomyName = null)
94
    {
95 1
        $nestedTaxonomy = call_user_func($closure->bindTo(new NestedTaxonomy($taxonomyName)));
96
97 1
        $query = $nestedTaxonomy->getQuery();
98 1
        foreach ($query as $key => $taxonomy) {
99 1
            if ($taxonomy['taxonomy'] instanceof Closure) {
100
                $query[$key] = $this->extractNestedTaxonomyClosures($taxonomy['taxonomy']);
101
            }
102 1
        }
103
104 1
        $nestedTaxonomy->replaceQuery($query);
105 1
        return $nestedTaxonomy;
106
    }
107
108
    /**
109
     * Nest multiple taxonomy queries by supplying a query. If the closure contains
110
     * another closure, it is resolved recursivly.
111
     * @param  Closure $closure
112
     * @return NestedTaxonomy          Returns a NestedTaxonomy instance which is further processed by the compiler.
113
     */
114 1
    protected function whereNestedTaxonomy($nestedTaxonomy)
115
    {
116 1
        if (!$nestedTaxonomy instanceof NestedTaxonomy) {
117
            throw new InvalidArgumentException('Invalid class supplied for nested taxonomy query');
118
        }
119
120 1
        $query = $nestedTaxonomy->getQuery();
121
122 1
        foreach ($query as $key => $taxonomy) {
123 1
            if ($taxonomy instanceof NestedTaxonomy) {
124
                $query[$key] = $this->whereNestedTaxonomy($taxonomy);
125
                continue;
126
            }
127
128 1
            $query[$key] = $this->parseNestedTaxonomy($taxonomy);
129 1
        }
130
131 1
        $nestedTaxonomy->replaceQuery($query);
132 1
        return $nestedTaxonomy;
133
    }
134
135
    /**
136
     * Parse the nested taxonomy fields by validating them. If the taxonomy fields is
137
     * an instance of NestedTaxonomy, we'll resolve that recursively.
138
     * @param  array|NestedTaxonomy $taxonomy
139
     * @return array
140
     */
141 1
    protected function parseNestedTaxonomy($taxonomy)
142
    {
143 1
        if ($taxonomy instanceof NestedTaxonomy) {
144
            $taxonomy = $this->whereNestedTaxonomy($taxonomy);
145
        }
146
147 1
        return $this->validateTaxonomyFields($taxonomy);
0 ignored issues
show
Bug introduced by
It seems like $taxonomy can also be of type Sanderdekroon\Parlant\Builder\NestedTaxonomy; however, parameter $fields of Sanderdekroon\Parlant\Bu...alidateTaxonomyFields() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

147
        return $this->validateTaxonomyFields(/** @scrutinizer ignore-type */ $taxonomy);
Loading history...
148
    }
149
150
    /**
151
     * Do some basic validating of the meta fields. Checks if the operator is valid and
152
     * if the meta type (if supplied any) is valid with the current grammar.
153
     * @param  array $fields
154
     * @return array
155
     */
156 1
    protected function validateTaxonomyFields($fields)
157
    {
158 1
        extract($fields);
159
160
        // If the given operator is not found in the list of valid operators we will
161
        // assume that the developer is just short-cutting the '=' operators and
162
        // we will set the operators to '=' and set the values appropriately.
163 1
        if ($this->invalidOperator($operator)) {
164
            list($value, $operator) = [$operator, '='];
165
        }
166
167
        // If no field is given or if it's an invalid one, we'll default back
168
        // to the term_id field. The $field is checked against the
169
        // values in the supplied posttype grammar.
170 1
        $field = $this->getValidTermField($field);
171
172 1
        return compact('taxonomy', 'field', 'operator', 'value', 'includeChildren', 'level');
173
    }
174
175
    /**
176
     * Adds arrays of where metas to the query.
177
     * @param array $taxonomyArray
178
     */
179
    protected function addArrayOfWhereTaxonomies($taxonomyArray)
180
    {
181
        foreach ($taxonomyArray as $array) {
182
            $this->whereTaxonomy(...$array);
0 ignored issues
show
Bug introduced by
$array is expanded, but the parameter $taxonomy of Sanderdekroon\Parlant\Bu...nomies::whereTaxonomy() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
            $this->whereTaxonomy(/** @scrutinizer ignore-type */ ...$array);
Loading history...
183
        }
184
185
        return $this;
186
    }
187
188
    /**
189
     * Return a valid taxonomy term field, like term_id or term_slug.
190
     * @param  string $type
191
     * @return string       Returns 'term_id' if none is supplied or if it's invalid
192
     */
193 4
    protected function getValidTermField($field = null)
194
    {
195 4
        if (is_null($field) || !in_array($field, $this->getGrammar()->getTaxonomyFields())) {
196 1
            return 'term_id';
197
        }
198
199 3
        return strtolower($field);
200
    }
201
202
    protected abstract function setBinding($key, $data);
203
    
204
    protected abstract function getBinding($key);
205
206
    protected abstract function getGrammar();
207
    
208
    protected abstract function appendBinding($key, $data);
209
    
210
    protected abstract function invalidOperator($operator);
211
    
212
    protected abstract function prepareValueAndOperator($value, $operator, $useDefault = false, $termDefault = false);
213
}
214