Completed
Pull Request — master (#7)
by
unknown
01:34
created

SoqlQuery   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 343
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 14
Bugs 0 Features 0
Metric Value
wmc 24
c 14
b 0
f 0
lcom 1
cbo 0
dl 0
loc 343
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A having() 0 6 1
A group() 0 6 1
A __construct() 0 1 1
A __tostring() 0 18 4
A select() 0 13 4
A where() 0 6 1
A order() 0 6 1
A limit() 0 8 1
A handleInteger() 0 14 3
A offset() 0 8 1
A fullTextSearch() 0 6 1
B formatAssociativeArray() 0 11 5
1
<?php
2
3
/**
4
 * This file contains the SoqlQuery class and the respective constants and default values that belong to SoQL.
5
 *
6
 * @copyright 2015 Vladimir Jimenez
7
 * @license   https://github.com/allejo/PhpSoda/blob/master/LICENSE.md MIT
8
 */
9
10
namespace allejo\Socrata;
11
12
/**
13
 * An object provided for the creation and handling of SoQL queries in an object-oriented fashion.
14
 *
15
 * @package allejo\Socrata
16
 * @since   0.1.0
17
 */
18
class SoqlQuery
19
{
20
    /**
21
     * The default delimiter used to separate multiple values.
22
     */
23
    const DELIMITER = ',';
24
25
    /**
26
     * The SELECT clause in SoQL
27
     */
28
    const SELECT_KEY = '$select';
29
30
    /**
31
     * The WHERE clause in SoQL
32
     */
33
    const WHERE_KEY = '$where';
34
35
    /**
36
     * The ORDER clause in SoQL
37
     */
38
    const ORDER_KEY = '$order';
39
40
    /**
41
     * The GROUP clause in SoQL
42
     */
43
    const GROUP_KEY = '$group';
44
45
    /**
46
     * The LIMIT clause in SoQL
47
     */
48
    const LIMIT_KEY = '$limit';
49
50
    /**
51
     * The HAVING clause in SoQL
52
     */
53
    const HAVING_KEY = '$having';
54
55
    /**
56
     * The OFFSET clause in SoQL
57
     */
58
    const OFFSET_KEY = '$offset';
59
60
    /**
61
     * The SEARCH clause in SoQL
62
     */
63
    const SEARCH_KEY = '$q';
64
65
    /**
66
     * The default value for the `$select` clause in a SoQL query. By default, select all the columns
67
     */
68
    const DEFAULT_SELECT = '*';
69
70
    /**
71
     * The default order for the `$order` clause in a SoQL query. By default, order in ascending order
72
     */
73
    const DEFAULT_ORDER_DIRECTION = SoqlOrderDirection::ASC;
74
75
    /**
76
     * This array contains all of the parts to a SoqlQuery being converted into a URL where the key of an element is the
77
     * SoQL clause (e.g. $select) and the value of an element is the value to the SoQL clause (e.g. *).
78
     *
79
     * @var string[]
80
     */
81
    private $queryElements;
82
83
    /**
84
     * Write a SoQL query by chaining functions. This object will handle encoding the final query in order for it to be
85
     * used properly as a URL. By default a SoqlQuery will select all columns (excluding socrata columns; e.g. :id) and
86
     * sort by `:id` in ascending order.
87
     *
88
     * @since 0.1.0
89
     */
90
    public function __construct () {}
91
92
    /**
93
     * Convert the current information into a URL encoded query that can be appended to the domain
94
     *
95
     * @since 0.1.0
96
     *
97
     * @return string The SoQL query ready to be appended to a URL
98
     */
99
    public function __tostring ()
100
    {
101
        if (is_null($this->queryElements))
102
        {
103
            return "";
104
        }
105
106
        $query = [];
107
108
        foreach ($this->queryElements as $soqlKey => $value)
109
        {
110
            $value = (is_array($value)) ? implode(self::DELIMITER, $value) : $value;
111
112
            $query[] = sprintf("%s=%s", $soqlKey, $value);
113
        }
114
115
        return implode("&", $query);
116
    }
117
118
    /**
119
     * Select only specific columns in your Soql Query. When this function is given no parameters or is not used in a
120
     * query, the Soql Query will return all of the columns by default.
121
     *
122
     * ```php
123
     * // These are all valid usages
124
     * $soqlQuery->select();
125
     * $soqlQuery->select("foo", "bar", "baz");
126
     * $soqlQuery->select(array("foo" => "foo_alias", "bar" => "bar_alias", "baz"));
127
     * ```
128
     *
129
     * @link    https://dev.socrata.com/docs/queries/select.html SoQL $select Parameter
130
     *
131
     * @param   array|mixed $columns   The columns to select from the dataset. The columns can be specified as an array
132
     *                                 of values or it can be specified as multiple parameters separated by commas.
133
     *
134
     * @since   0.1.0
135
     *
136
     * @return  $this       A SoqlQuery object that can continue to be chained
137
     */
138
    public function select ($columns = self::DEFAULT_SELECT)
139
    {
140
        if (func_num_args() == 1)
141
        {
142
            $this->queryElements[self::SELECT_KEY] = (is_array($columns)) ? $this->formatAssociativeArray("%s AS %s", $columns) : array($columns);
143
        }
144
        else if (func_num_args() > 1)
145
        {
146
            $this->queryElements[self::SELECT_KEY] = func_get_args();
147
        }
148
149
        return $this;
150
    }
151
152
    /**
153
     * Create an array of values that have already been formatted and are ready to be converted into a comma separated
154
     * list that will be used as a parameter for selectors such was `$select`, `$order`, or `$group` in SoQL
155
     *
156
     * @param   string $format The format used in sprintf() for keys and values of an array to be formatted to
157
     * @param   array  $array  The array that will be formatted appropriately for usage within this class
158
     *
159
     * @since   0.1.0
160
     *
161
     * @return  array
162
     */
163
    private function formatAssociativeArray ($format, $array)
164
    {
165
        $formattedValues = array();
166
167
        foreach ($array as $key => $value)
168
        {
169
            $formattedValues[] = (is_string($key) && (!is_null($value))) ? rawurlencode(sprintf($format, trim($key), trim($value))) : (is_string($key) ? $key : $value);
170
        }
171
172
        return $formattedValues;
173
    }
174
175
    /**
176
     * Create a filter to selectively choose data based on certain parameters.
177
     *
178
     * Multiple calls to this function in a chain will overwrite the previous statement. To combine multiple where
179
     * clauses, use the supported SoQL operators; e.g. `magnitude > 3.0 AND source = 'pr'`
180
     *
181
     * @link    https://dev.socrata.com/docs/queries/where.html SoQL $where Parameter
182
     *
183
     * @param   string $statement The `where` clause that will be used to filter data
184
     *
185
     * @since   0.1.0
186
     *
187
     * @return  $this  A SoqlQuery object that can continue to be chained
188
     */
189
    public function where ($statement)
190
    {
191
        $this->queryElements[self::WHERE_KEY] = rawurlencode($statement);
192
193
        return $this;
194
    }
195
196
    /**
197
     * Create a filter to aggregate your results using boolean operators, similar to the HAVING clause in SQL.
198
     *
199
     * @link    https://dev.socrata.com/docs/queries/having.html SoQL $having Parameter
200
     *
201
     * @param   string $statement The `having` clause that will be used to filter data
202
     *
203
     * @since   0.2.0
204
     *
205
     * @return  $this  A SoqlQuery object that can continue to be chained
206
     */
207
    public function having ($statement)
208
    {
209
        $this->queryElements[self::HAVING_KEY] = rawurlencode($statement);
210
211
        return $this;
212
    }
213
214
    /**
215
     * Determines the order and the column the results should be sorted by. This function may be used more than once in
216
     * a chain so duplicate entries in the first column will be sorted by the second specified column specified. If
217
     * this
218
     * function is called more than once in a chain, the order does matter in which order() you call first.
219
     *
220
     * @link    https://dev.socrata.com/changelog/2015/04/27/new-higher-performance-apis.html New Higher Performance API
221
     * @link    https://dev.socrata.com/docs/queries/order.html SoQL $order Parameter
222
     *
223
     * @param   string $column      The column(s) that determines how the results should be sorted. This information
224
     *                              can be given as an array of values, a single column, or a comma separated string.
225
     *                              In order to support sorting by multiple columns, you need to use the latest version
226
     *                              of the dataset API.
227
     * @param   string $direction   The direction the results should be sorted in, either ascending or descending. The
228
     *                              {@link SoqlOrderDirection} class provides constants to use should these values ever
229
     *                              change in the future. The only accepted values are: `ASC` and `DESC`
230
     *
231
     * @see     SoqlOrderDirection  View convenience constants
232
     *
233
     * @since   0.1.0
234
     *
235
     * @return  $this   A SoqlQuery object that can continue to be chained
236
     */
237
    public function order ($column, $direction = self::DEFAULT_ORDER_DIRECTION)
238
    {
239
        $this->queryElements[self::ORDER_KEY][] = rawurlencode($column . " " . $direction);
240
241
        return $this;
242
    }
243
244
    /**
245
     * Group the resulting dataset based on a specific column. This function must be used in conjunction with
246
     * `select()`.
247
     *
248
     * For example, to find the strongest earthquake by region, we want to group() by region and provide a select of
249
     * region, MAX(magnitude).
250
     *
251
     * ```php
252
     * $soql->select("region", "MAX(magnitude)")->group("region");
253
     * ```
254
     *
255
     * @link    https://dev.socrata.com/docs/queries/group.html  The $group Parameter
256
     *
257
     * @param   string $column The column that will be used to group the dataset
258
     *
259
     * @since   0.1.0
260
     *
261
     * @return  $this   A SoqlQuery object that can continue to be chained
262
     */
263
    public function group ($column)
264
    {
265
        $this->queryElements[self::GROUP_KEY][] = $column;
266
267
        return $this;
268
    }
269
270
    /**
271
     * Set the amount of results that can be retrieved from a dataset per query.
272
     *
273
     * @link    https://dev.socrata.com/docs/queries/limit.html  SoQL $limit Parameter
274
     *
275
     * @param   int $limit The number of results the dataset should be limited to when returned
276
     *
277
     * @throws  \InvalidArgumentException  If the given argument is not an integer
278
     * @throws  \OutOfBoundsException      If the given argument is less than 0
279
     *
280
     * @since   0.1.0
281
     *
282
     * @return  $this          A SoqlQuery object that can continue to be chained
283
     */
284
    public function limit ($limit)
285
    {
286
        $this->handleInteger("limit", $limit);
287
288
        $this->queryElements[self::LIMIT_KEY] = $limit;
289
290
        return $this;
291
    }
292
293
    /**
294
     * Analyze a given value and ensure the value fits the criteria set by the Socrata API
295
     *
296
     * @param   string $variable The literal name of this field
297
     * @param   int    $number   The value to analyze
298
     *
299
     * @since   0.1.0
300
     *
301
     * @throws  \InvalidArgumentException         If the given argument is not an integer
302
     * @throws  \OutOfBoundsException             If the given argument is less than 0
303
     */
304
    private function handleInteger ($variable, $number)
305
    {
306
        if (!is_integer($number))
307
        {
308
            throw new \InvalidArgumentException(sprintf("The %s must be an integer", $variable));
309
        }
310
311
        if ($number < 0)
312
        {
313
            $message = sprintf("The %s cannot be less than 0.", $variable);
314
315
            throw new \OutOfBoundsException($message, 1);
316
        }
317
    }
318
319
    /**
320
     * The offset is the number of records into a dataset that you want to start, indexed at 0. For example, to retrieve
321
     * the “4th page” of records (records 151 - 200) where you are using limit() to page 50 records at a time, you’d ask
322
     * for an $offset of 150.
323
     *
324
     * @link    https://dev.socrata.com/docs/queries/offset.html  SoQL $offset Parameter
325
     *
326
     * @param   int $offset The number of results the dataset should be offset to when returned
327
     *
328
     * @throws  \InvalidArgumentException  If the given argument is not an integer
329
     * @throws  \OutOfBoundsException      If the given argument is less than 0
330
     *
331
     * @since   0.1.0
332
     *
333
     * @return  $this           A SoqlQuery object that can continue to be chained
334
     */
335
    public function offset ($offset)
336
    {
337
        $this->handleInteger("offset", $offset);
338
339
        $this->queryElements[self::OFFSET_KEY] = $offset;
340
341
        return $this;
342
    }
343
344
    /**
345
     * Search the entire dataset for a specified string. Think of this as a search engine instead of performing a SQL
346
     * query.
347
     *
348
     * @param   string $needle The phrase to search for
349
     *
350
     * @since   0.1.0
351
     *
352
     * @return  $this            A SoqlQuery object that can continue to be chained
353
     */
354
    public function fullTextSearch ($needle)
355
    {
356
        $this->queryElements[self::SEARCH_KEY] = rawurlencode($needle);
357
358
        return $this;
359
    }
360
}
361