Skip to content

Commit 70ac60b

Browse files
committed
- Documentation extended
- Added some exceptions - DBOrderSpec: If a statement contains an alias that was not defined before, then an exception is thrown now
1 parent 183a0e6 commit 70ac60b

10 files changed

+224
-111
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ Here a few things to keep in mind:
2121
## Some simplified examples
2222

2323
* [Initialization](doc/initialization.md)
24-
* [Simple select](doc/simple-select.md)
25-
* [Complex select](doc/complex-select.md)
26-
* [Optional conditions](doc/optional-conditions.md)
24+
* [Select](doc/select.md)
2725
* [Insert](doc/insert.md)
2826
* [Update](doc/update.md)
2927
* [Delete](doc/delete.md)

doc/complex-select.md

Lines changed: 0 additions & 37 deletions
This file was deleted.

doc/initialization.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# Initialization
22

33
```PHP
4-
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
4+
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', '', [
5+
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
6+
PDO::ATTR_EMULATE_PREPARES => true
7+
]);
58
```
69

10+
* `...charset=utf8...` you can use whatever charset you need. The query builder will not make any further modifications to the data. Data is always passed through 1:1.
11+
* `PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION`: Will throw exceptions instead of failing silently.
12+
* `PDO::ATTR_EMULATE_PREPARES => true`: There is an old PHP-PDO-bug. If you use a named parameter more than once using prepared statements and `PDO::ATTR_EMULATE_PREPARES => false`, only the first value will be used.
13+
714
```PHP
815
$mysql = new MySQL($pdo);
916
$mysql->getAliasRegistry()->add('t', 'testdb.test__');

doc/optional-conditions.md

Lines changed: 0 additions & 29 deletions
This file was deleted.

doc/select.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Select
2+
3+
This page covers many concepts on how to retrieve data using the query builder. But there are still a lot of tricks how to use the query builder, which are not documented on this page yet.
4+
5+
Feel free to submit a PR and improve something about this site.
6+
7+
## Fields
8+
9+
```php
10+
use Kir\MySQL\Builder\Expr\DBOrderSpec;use Kir\MySQL\Databases\MySQL;
11+
12+
$pdo = new PDO(/* ... */);
13+
$db = new MySQL($pdo);
14+
15+
// One method call for each field specifications
16+
$rows = $db->select()
17+
->field('t.field1')
18+
->field('t.field2', 'alias')
19+
->from('t', 'some_table')
20+
->fetchRows();
21+
22+
// Field specifications in an array notation
23+
$rows = $db->select()
24+
->fields([
25+
'alias1' => 't.field1',
26+
'alias2' => 't.field2'
27+
])
28+
->from('t', 'some_table')
29+
->fetchRows();
30+
```
31+
32+
## Receiving data
33+
34+
```php
35+
use Kir\MySQL\Builder\Expr\DBOrderSpec;use Kir\MySQL\Databases\MySQL;
36+
37+
$db = new MySQL(new PDO(/* ... */));
38+
39+
$select = $db->select()
40+
->field('t.field1')
41+
->field('t.field2')
42+
->from('t', 'test');
43+
44+
// Get multiple rows as a Key/Value-Array (like PDO::FETCH_ASSOC)
45+
// Result is guaranteed to be an array<int, array<string, string>>.
46+
// Might throw an exception.
47+
$rows = $select->fetchRows();
48+
49+
// Get one row as a Key(=Fieldnames)/Value(=Values)-Array (like PDO::FETCH_ASSOC)
50+
// Result is guaranteed to be an array<string, string> or an empty array.
51+
// Might throw an exception.
52+
$row = $select->fetchRow();
53+
54+
// Get the value of the first column of the result-set.
55+
// No value means `null`.
56+
$value = $select->fetchValue();
57+
```
58+
59+
### Keep the original database types
60+
61+
PDO returns all data as `string` or `null`. However, via the meta data of the last query you can read out the types of the last query. With the method `setPreserveTypes()` one can have the types used in the database largely reconstructed on output. This costs a little performance. On modern systems this should not be too noticeable. But in fact only integers and decimal numbers are translated at the moment. Everything else remains `string` or `null`. If a `null` value is returned from the database, then of course this value is kept.
62+
63+
## Optional conditions
64+
65+
Sometimes you want to set conditions based on dynamic data. One example would be a grid-list with dynamic filters for certain columns. Following the example below, you can predefine _optional conditions_ which will execute when the corresponding data in the `$filter`-Array is present.
66+
67+
```PHP
68+
use Kir\MySQL\Databases\MySQL;
69+
use Kir\MySQL\Builder\Expr\DBExprFilter;
70+
use Kir\MySQL\Builder\Value\DBOptionalValue;
71+
72+
$filter = [
73+
'name' => 'Peter',
74+
'date' => [
75+
'start' => '2016-05-01',
76+
'end' => '2016-05-31',
77+
],
78+
];
79+
80+
$query = (new MySQL(new PDO(/* ... */)))
81+
/* ... */
82+
->from('t', 'test')
83+
/* ... */
84+
->where(new DBExprFilter('t.name = ?', $filter, 'name'))
85+
->where(new DBExprFilter('t.date >= ?', $filter, 'date.start')) // Key in dot-notation
86+
->where(new DBExprFilter('t.date <= ?', $filter, ['date', 'end'])) // Key in array-notation
87+
->limit(new DBOptionalValue($filter, ['count']))
88+
->offset(new DBOptionalValue($filter, ['offset']))
89+
->fetchRows();
90+
```
91+
92+
You can also define validation rules to only match certain data in `$filter`.
93+
94+
## Sorting from data
95+
96+
```php
97+
use Kir\MySQL\Databases\MySQL;
98+
use Kir\MySQL\Builder\Expr\DBOrderSpec;
99+
100+
$db = new MySQL(new PDO(/* ... */));
101+
102+
// [alias => dir, alias => dir, ...]
103+
$_GET = ['field3' => 'ASC', 'field1' => 'ASC', 'field2' => 'ASC', 'field4' => 'ASC'];
104+
105+
$rows = $db->select()
106+
->field('t.field1')
107+
->field('t.field2')
108+
->from('t', 'test')
109+
->orderBy(new DBOrderSpec([
110+
'field1' => 't.field1', // alias => db-expression
111+
'field2' => 'REVERSE(t.field2)', // alias => db-expression
112+
'field4' => 't.field2' // alias => db-expression
113+
], $_GET))
114+
->fetchRows();
115+
```
116+
117+
*⚠ Any aliases specified in the search instructions but for which no DB expression has been defined will result in an exception. It is better that it will bang in such a case instead of just not working.*
118+
119+
## Some examples
120+
121+
```PHP
122+
$subSelect = function ($id) use ($mysql) {
123+
return $mysql->select()
124+
->field('t.id')
125+
->from('t', 'table')
126+
->where('t.foreign_id=?', $id);
127+
};
128+
129+
$select = $mysql->select()
130+
->field('COUNT(*)', 'customer_count')
131+
->from('t1', 't#test1')
132+
->joinInner('t2', 't#test2', 't2.test_id = t1.id AND t2.field1 = ?', 123)
133+
->joinLeft('t3', 't#test3', 't3.test_id = t1.id')
134+
->joinRight('t4', 't#test4', 't4.test_id = t1.id')
135+
->joinRight('t5', $subSelect(10), 't5.test_id = t1.id')
136+
->orderBy('t1.field1')
137+
->orderBy('t1.field2', 'DESC')
138+
->limit(100)
139+
->offset(50);
140+
```
141+
142+
```PHP
143+
if($contition === true) {
144+
$select->where('t1.somefield = ?', $someValue);
145+
}
146+
```
147+
148+
```PHP
149+
$rows = $select->fetchRows();
150+
foreach($rows as $row) {
151+
print_r($row);
152+
}
153+
```
154+
155+
## Complex select
156+
157+
```php
158+
$dateStart = '2016-05-01';
159+
$dateEnd = '2016-05-31';
160+
161+
$tableA = $db->select()
162+
->field('a.field1')
163+
->field('a.field2')
164+
->from('a', 'table_a')
165+
->where('a.date BETWEEN ? AND ?', $dateStart, $dateEnd);
166+
167+
$tableB = $db->select()
168+
->field('b.field1')
169+
->field('b.field2')
170+
->from('b', 'table_b')
171+
->where('b.date BETWEEN ? AND ?', $dateStart, $dateEnd);
172+
173+
$tableC = $db->select()
174+
->field('t.field1')
175+
->field('t.field2')
176+
->from('t', 'table_c')
177+
->where('t.date BETWEEN ? AND ?', $dateStart, $dateEnd);
178+
179+
echo $db->select()
180+
->from('t',
181+
$db->select()
182+
->field('a.field1')
183+
->field('COALESCE(b.field2, a.field2)', 'field2')
184+
->from('a', $tableA)
185+
->joinLeft('b', $tableB, 'b.id=a.id')
186+
->where('NOT ISNULL(a.field1)')
187+
->union($tableC)
188+
);
189+
```
190+
191+
[Back](../README.md)
192+

doc/simple-select.md

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/Builder/Expr/DBOrderSpec.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ public function getFields() {
4343
$max = $this->options['max_sort_instructions'] ?? 16;
4444
foreach($this->sortingInstruction as $alias => $direction) {
4545
$direction = strtolower($direction) === 'desc' ? 'DESC' : 'ASC';
46-
if(array_key_exists($alias, $this->sortSpecification)) {
47-
$fields[] = [$this->sortSpecification[$alias], $direction];
46+
if(!array_key_exists($alias, $this->sortSpecification)) {
47+
throw new DBSortAliasNotFoundException('Sorting: Alias for DB-Expression not found');
4848
}
49+
$fields[] = [$this->sortSpecification[$alias], $direction];
4950
if($max < 1) {
50-
throw new RuntimeException();
51+
throw new DBSortTooManyInstructionsException();
5152
}
5253
$max--;
5354
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kir\MySQL\Builder\Expr;
4+
5+
use RuntimeException;
6+
7+
class DBSortAliasNotFoundException extends RuntimeException {
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kir\MySQL\Builder\Expr;
4+
5+
use Kir\MySQL\Builder\Exception;
6+
7+
class DBSortTooManyInstructionsException extends Exception {
8+
}

src/Builder/RunnableSelect.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ private function fetchAll(Closure $callback = null, $mode, $arg0 = null) {
246246
$data = $statement->fetchAll();
247247
if($this->preserveTypes) {
248248
$columnDefinitions = FieldTypeProvider::getFieldTypes($statement);
249-
$data = array_map(static function ($row) use ($columnDefinitions) { return FieldValueConverter::convertValues($row, $columnDefinitions); }, $data);
249+
$data = array_map(static function ($row) use ($columnDefinitions) {
250+
return FieldValueConverter::convertValues($row, $columnDefinitions);
251+
}, $data);
250252
}
251253
if($callback !== null) {
252254
return call_user_func(static function ($resultData = []) use ($data, $callback) {

0 commit comments

Comments
 (0)