Passed
Push — master ( b4e3f0...d6a119 )
by Robbie
04:16 queued 02:31
created

src/Search/Criteria/SearchCriteria.php (2 issues)

1
<?php
2
3
namespace SilverStripe\FullTextSearch\Search\Criteria;
4
5
use SilverStripe\FullTextSearch\Search\Adapters\SearchAdapterInterface;
6
use SilverStripe\FullTextSearch\Search\Queries\AbstractSearchQueryWriter;
7
8
/**
9
 * Class SearchCriteria
10
 * @package SilverStripe\FullTextSearch\Criteria
11
 */
12
class SearchCriteria implements SearchCriteriaInterface
13
{
14
    /**
15
     * @param string
16
     */
17
    const CONJUNCTION_AND = 'AND';
18
19
    /**
20
     * @param string
21
     */
22
    const CONJUNCTION_OR = 'OR';
23
24
    /**
25
     * A collection of SearchCriterion and SearchCriteria.
26
     *
27
     * @var SearchCriteriaInterface[]
28
     */
29
    protected $clauses = array();
30
31
    /**
32
     * The conjunctions used between Criteria (AND/OR).
33
     *
34
     * @var string[]
35
     */
36
    protected $conjunctions = array();
37
38
    /**
39
     * @var SearchAdapterInterface|null
40
     */
41
    protected $adapter = null;
42
43
    /**
44
     * You can pass through a string value, Criteria object, or Criterion object for $target.
45
     *
46
     * String value might be "SiteTree_Title" or whatever field in your index that you're trying to target.
47
     *
48
     * If you require complex filtering then you can build your Criteria object first with multiple layers/levels of
49
     * Criteria, and then pass it in here when you're ready.
50
     *
51
     * If you have your own Criterion object that you've created that you want to use, you can also pass that in here.
52
     *
53
     * @param string|SearchCriterion $target
54
     * @param mixed $value
55
     * @param string|null $comparison
56
     * @param AbstractSearchQueryWriter $searchQueryWriter
57
     */
58
    public function __construct(
59
        $target,
60
        $value = null,
61
        $comparison = null,
62
        AbstractSearchQueryWriter $searchQueryWriter = null
63
    ) {
64
        $this->addClause($this->getCriterionForCondition($target, $value, $comparison, $searchQueryWriter));
65
    }
66
67
    /**
68
     * Static create method provided so that you can perform method chaining.
69
     *
70
     * @param $target
71
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
72
     * @param null $comparison
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $comparison is correct as it would always require null to be passed?
Loading history...
73
     * @param AbstractSearchQueryWriter $searchQueryWriter
74
     * @return SearchCriteria
75
     */
76
    public static function create(
77
        $target,
78
        $value = null,
79
        $comparison = null,
80
        AbstractSearchQueryWriter $searchQueryWriter = null
81
    ) {
82
        return new SearchCriteria($target, $value, $comparison, $searchQueryWriter);
83
    }
84
85
    /**
86
     * @return null|SearchAdapterInterface
87
     */
88
    public function getAdapter()
89
    {
90
        return $this->adapter;
91
    }
92
93
    /**
94
     * @param SearchAdapterInterface $adapter
95
     * @return $this
96
     */
97
    public function setAdapter(SearchAdapterInterface $adapter)
98
    {
99
        $this->adapter = $adapter;
100
101
        return $this;
102
    }
103
104
    /**
105
     * @param string $ps Current prepared statement.
106
     * @return void
107
     * @throws \Exception
108
     */
109
    public function appendPreparedStatementTo(&$ps)
110
    {
111
        $adapter = $this->getAdapter();
112
113
        if (!$adapter instanceof SearchAdapterInterface) {
114
            throw new \Exception('No adapter has been applied to SearchCriteria');
115
        }
116
117
        $ps .= $adapter->getOpenComparisonContainer();
118
119
        foreach ($this->getClauses() as $key => $clause) {
120
            $clause->setAdapter($adapter);
121
            $clause->appendPreparedStatementTo($ps);
122
123
            // There's always one less conjunction then there are clauses.
124
            if ($this->getConjunction($key) !== null) {
125
                $ps .= $adapter->getConjunctionFor($this->getConjunction($key));
126
            }
127
        }
128
129
        $ps .= $adapter->getCloseComparisonContainer();
130
    }
131
132
    /**
133
     * @param string|SearchCriteriaInterface $target
134
     * @param mixed $value
135
     * @param string|null $comparison
136
     * @param AbstractSearchQueryWriter $searchQueryWriter
137
     * @return $this
138
     */
139
    public function addAnd(
140
        $target,
141
        $value = null,
142
        $comparison = null,
143
        AbstractSearchQueryWriter $searchQueryWriter = null
144
    ) {
145
        $criterion = $this->getCriterionForCondition($target, $value, $comparison, $searchQueryWriter);
146
147
        $this->addConjunction(SearchCriteria::CONJUNCTION_AND);
148
        $this->addClause($criterion);
149
150
        return $this;
151
    }
152
153
    /**
154
     * @param string|SearchCriteriaInterface $target
155
     * @param mixed $value
156
     * @param string|null $comparison
157
     * @param AbstractSearchQueryWriter $searchQueryWriter
158
     * @return $this
159
     */
160
    public function addOr(
161
        $target,
162
        $value = null,
163
        $comparison = null,
164
        AbstractSearchQueryWriter $searchQueryWriter = null
165
    ) {
166
        $criterion = $this->getCriterionForCondition($target, $value, $comparison, $searchQueryWriter);
167
168
        $this->addConjunction(SearchCriteria::CONJUNCTION_OR);
169
        $this->addClause($criterion);
170
171
        return $this;
172
    }
173
174
    /**
175
     * @param string|SearchCriteriaInterface $target
176
     * @param mixed $value
177
     * @param string $comparison
178
     * @param AbstractSearchQueryWriter $searchQueryWriter
179
     * @return SearchCriteriaInterface
180
     */
181
    protected function getCriterionForCondition(
182
        $target,
183
        $value,
184
        $comparison,
185
        AbstractSearchQueryWriter $searchQueryWriter = null
186
    ) {
187
        if ($target instanceof SearchCriteriaInterface) {
188
            return $target;
189
        }
190
191
        return new SearchCriterion($target, $value, $comparison, $searchQueryWriter);
192
    }
193
194
    /**
195
     * @return SearchCriteriaInterface[]
196
     */
197
    protected function getClauses()
198
    {
199
        return $this->clauses;
200
    }
201
202
    /**
203
     * @param SearchCriteriaInterface $criterion
204
     */
205
    protected function addClause($criterion)
206
    {
207
        $this->clauses[] = $criterion;
208
    }
209
210
    /**
211
     * @return string[]
212
     */
213
    protected function getConjunctions()
214
    {
215
        return $this->conjunctions;
216
    }
217
218
    /**
219
     * @param int $key
220
     * @return string|null
221
     */
222
    protected function getConjunction($key)
223
    {
224
        $conjunctions = $this->getConjunctions();
225
        if (!array_key_exists($key, $conjunctions)) {
226
            return null;
227
        }
228
229
        return $conjunctions[$key];
230
    }
231
232
    /**
233
     * @param string $conjunction
234
     */
235
    protected function addConjunction($conjunction)
236
    {
237
        $this->conjunctions[] = $conjunction;
238
    }
239
}
240