Skip to content

Commit ff2ed65

Browse files
author
Artem Shteltser
committed
✅ Started developing query builder
1 parent cb05660 commit ff2ed65

File tree

6 files changed

+286
-1
lines changed

6 files changed

+286
-1
lines changed

src/Model/BaseModel.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace PowerLink\Model;
4+
5+
abstract class BaseModel
6+
{
7+
/**
8+
* Object type
9+
* @var string
10+
*/
11+
protected $object_type;
12+
13+
/**
14+
* @param string $object_type Object Type
15+
*/
16+
public function __construct(string $object_type)
17+
{
18+
$this->object_type = $object_type;
19+
}
20+
}

src/Model/QueryModel.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
<?php
32

43
namespace PowerLink\Model;

src/Query/Builder.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace PowerLink\Query;
4+
5+
class QueryBuilder
6+
{
7+
/**
8+
* The where constraints for the query.
9+
*
10+
* @var array
11+
*/
12+
public $wheres = [];
13+
}

src/Query/Syntax/Field.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace PowerLink\Query\Syntax;
4+
5+
/**
6+
* Class Field.
7+
*/
8+
class Field
9+
{
10+
/**
11+
* @var string
12+
*/
13+
protected $name;
14+
15+
/**
16+
* @param string $name
17+
*/
18+
public function __construct($name)
19+
{
20+
$this->setName($name);
21+
}
22+
23+
/**
24+
* @return string
25+
*/
26+
public function getName()
27+
{
28+
return $this->name;
29+
}
30+
31+
/**
32+
* @param string $name
33+
*
34+
* @return $this
35+
*/
36+
public function setName($name)
37+
{
38+
$this->name = (string) $name;
39+
40+
return $this;
41+
}
42+
}

src/Query/Syntax/OrderBy.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace PowerLink\Query\Syntax;
4+
5+
/**
6+
* Class OrderBy.
7+
*/
8+
class OrderBy
9+
{
10+
const ASC = 'asc';
11+
const DESC = 'desc';
12+
13+
/**
14+
* @var Field
15+
*/
16+
protected $field;
17+
18+
/**
19+
* @var string
20+
*/
21+
protected $direction;
22+
23+
/**
24+
* @var bool
25+
*/
26+
protected $useAlias;
27+
28+
/**
29+
* @param string $field
30+
* @param string $direction
31+
*/
32+
public function __construct(string $field, $direction)
33+
{
34+
$this->setField($field);
35+
$this->setDirection($direction);
36+
}
37+
38+
/**
39+
* @return string
40+
*/
41+
public function getField()
42+
{
43+
return $this->field;
44+
}
45+
46+
/**
47+
* @param Field $field
48+
*
49+
* @return $this
50+
*/
51+
public function setField($field)
52+
{
53+
$this->field = $field;
54+
55+
return $this;
56+
}
57+
58+
/**
59+
* @return string
60+
*/
61+
public function getDirection()
62+
{
63+
return $this->direction;
64+
}
65+
66+
/**
67+
* @param string $direction
68+
*
69+
* @throws \InvalidArgumentException
70+
*
71+
* @return $this
72+
*/
73+
public function setDirection($direction)
74+
{
75+
if (!in_array($direction, array(self::ASC, self::DESC))) {
76+
throw new \InvalidArgumentException(
77+
"Specified direction '$direction' is not allowed. Only ASC or DESC are allowed."
78+
);
79+
}
80+
81+
$this->direction = $direction;
82+
83+
return $this;
84+
}
85+
}

src/Query/Syntax/Where.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace PowerLink\Query\Syntax;
4+
5+
6+
/**
7+
* Class Where.
8+
*/
9+
class Where
10+
{
11+
const OPERATOR_EQUAL = '=';
12+
const OPERATOR_NOT_EQUAL = '!=';
13+
14+
const OPERATOR_GREATER_THAN = '>';
15+
const OPERATOR_LESS_THAN = '<';
16+
const OPERATOR_GREATER_THAN_OR_EQUAL = '>=';
17+
const OPERATOR_LESS_THAN_OR_EQUAL = '<=';
18+
19+
const CONJUNCTION_AND = 'AND';
20+
const CONJUNCTION_OR = 'OR';
21+
22+
const OPERATOR_START_WITH = 'start-with';
23+
const OPERATOR_END_WITH = 'end-with';
24+
25+
const OPERATOR_NOT_START_WITH = 'not-start-with';
26+
const OPERATOR_NOT_END_WITH = 'not-end-with';
27+
28+
const OPERATOR_IS_NULL = 'is-null';
29+
const OPERATOR_IS_NOT_NULL = 'is-not-null';
30+
31+
/**
32+
* Add a basic where clause to the query.
33+
*
34+
* @param string $field
35+
* @param mixed $operator
36+
* @param mixed $value
37+
* @param string $boolean
38+
* @return $this
39+
*/
40+
public function where($field, $operator = null, $value = null, $boolean = 'and')
41+
{
42+
// If the field is an array, we will assume it is an array of key-value pairs
43+
// and can add them each as a where clause. We will maintain the boolean we
44+
// received when the method was called and pass it into the nested where.
45+
if (is_array($field)) {
46+
return $this->addArrayOfWheres($field, $boolean);
47+
}
48+
49+
// Here we will make some assumptions about the operator. If only 2 values are
50+
// passed to the method, we will assume that the operator is an equals sign
51+
// and keep going. Otherwise, we'll require the operator to be passed in.
52+
[$value, $operator] = $this->prepareValueAndOperator(
53+
$value,
54+
$operator,
55+
func_num_args() === 2
56+
);
57+
58+
// If the fields is actually a Closure instance, we will assume the developer
59+
// wants to begin a nested where statement which is wrapped in parenthesis.
60+
// We'll add that Closure to the query then return back out immediately.
61+
if ($field instanceof Closure && is_null($operator)) {
62+
return $this->whereNested($field, $boolean);
63+
}
64+
65+
// If the field is a Closure instance and there is an operator value, we will
66+
// assume the developer wants to run a subquery and then compare the result
67+
// of that subquery with the given value that was provided to the method.
68+
if ($this->isQueryable($field) && !is_null($operator)) {
69+
[$sub, $bindings] = $this->createSub($field);
70+
71+
return $this->addBinding($bindings, 'where')
72+
->where(new Expression('(' . $sub . ')'), $operator, $value, $boolean);
73+
}
74+
75+
// If the given operator is not found in the list of valid operators we will
76+
// assume that the developer is just short-cutting the '=' operators and
77+
// we will set the operators to '=' and set the values appropriately.
78+
if ($this->invalidOperator($operator)) {
79+
[$value, $operator] = [$operator, '='];
80+
}
81+
82+
// If the value is a Closure, it means the developer is performing an entire
83+
// sub-select within the query and we will need to compile the sub-select
84+
// within the where clause to get the appropriate query record results.
85+
if ($value instanceof Closure) {
86+
return $this->whereSub($field, $operator, $value, $boolean);
87+
}
88+
89+
// If the value is "null", we will just assume the developer wants to add a
90+
// where null clause to the query. So, we will allow a short-cut here to
91+
// that method for convenience so the developer doesn't have to check.
92+
if (is_null($value)) {
93+
return $this->whereNull($field, $boolean, $operator !== '=');
94+
}
95+
96+
$type = 'Basic';
97+
98+
// If the field is making a JSON reference we'll check to see if the value
99+
// is a boolean. If it is, we'll add the raw boolean string as an actual
100+
// value to the query to ensure this is properly handled by the query.
101+
if (Str::contains($field, '->') && is_bool($value)) {
102+
$value = new Expression($value ? 'true' : 'false');
103+
104+
if (is_string($field)) {
105+
$type = 'JsonBoolean';
106+
}
107+
}
108+
109+
// Now that we are working with just a simple query we can put the elements
110+
// in our array and add the query binding to our array of bindings that
111+
// will be bound to each SQL statements when it is finally executed.
112+
$this->wheres[] = compact(
113+
'type',
114+
'field',
115+
'operator',
116+
'value',
117+
'boolean'
118+
);
119+
120+
if (!$value instanceof Expression) {
121+
$this->addBinding($this->flattenValue($value), 'where');
122+
}
123+
124+
return $this;
125+
}
126+
}

0 commit comments

Comments
 (0)