Skip to content

Commit f3a31d3

Browse files
committed
Releasing 1.2.0
2 parents 36ca463 + 670e649 commit f3a31d3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2253
-10
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ composer.phar
55
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
66
# composer.lock
77
/composer.lock
8+
/nbproject/private/
9+
/set-local-test-env.sh

README.md

+146-5
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ The interface usually delivers `stdClass` objects by default. However, you can n
6565
your own data class so the data will be populated in such a class:
6666

6767
```
68-
$arr = $db->queryList('SELECT * FROM #__devtest', 'MyNamespace\MyDataClass');
69-
$obj = $db->querySingle('SELECT * FROM #__devtest WHERE uid='.$uid, 'MyNamespace\MyDataClass');
68+
$arr = $db->queryList('SELECT * FROM #__devtest', 'MyNamespace\\MyDataClass');
69+
$obj = $db->querySingle('SELECT * FROM #__devtest WHERE uid='.$uid, 'MyNamespace\\MyDataClass');
7070
```
7171

7272
## Inserting, Updating and deleting objects
@@ -144,10 +144,10 @@ However, you can tell `DAO` your specifics:
144144

145145
```
146146
// Uses a specific class for the data
147-
$dao = \TgDatabase\DAO($db, '#__users', 'MyNamespace\User`);
147+
$dao = \TgDatabase\DAO($db, '#__users', 'MyNamespace\\User`);
148148
149149
// Uses a specific class and another primary key attribute
150-
$dao = \TgDatabase\DAO($db, '#__users', 'MyNamespace\User`, 'id');
150+
$dao = \TgDatabase\DAO($db, '#__users', 'MyNamespace\\User`, 'id');
151151
```
152152

153153
`DAO` can actually handle non-numeric primary keys. The usage is not recommended though.
@@ -251,7 +251,7 @@ That way you can further abstract data access, e.g.
251251
class Users extends DAO {
252252
253253
public function __construct($database) {
254-
parent::__construct($database, '#__users', 'MyNamespace\User');
254+
parent::__construct($database, '#__users', 'MyNamespace\\User');
255255
}
256256
257257
public function findByEmail($email) {
@@ -344,6 +344,147 @@ $users = $myModel->get('users')->find();
344344

345345
Imagine, how much error-proned code you would have to write yourself!
346346

347+
# Criteria API
348+
Version 1.2 introduces a basic form of `Criteria` which gives you more freedom to express SQL conditions
349+
when searching for rows and objects. It is designed using the Hibernate ORM Criteria template. So much
350+
of the code may appear familiar to you.
351+
352+
The Criteria API was created additional to the Data Model and DAO API and enhances it. So you can still use
353+
the v1.0 way of searching objects while already starting the Criteria API. However, it is planned to
354+
replace the `queryList()` and `querySingle()` methods in `Database` as well as `count()` and
355+
`find()` methods in `DAO` by the Criteria API in next major versions. At the moment both APIs exist
356+
independently.
357+
358+
**Disclaimer:** The Criteria API cannot yet produce `GROUP BY` clauses as they are more complex to build.
359+
It will be added later.
360+
361+
## Creating a Criteria
362+
Two ways exist: Creating a `Criteria` object from the `Database` object, or alternatively from the `DAO`
363+
object:
364+
365+
```
366+
// From Database object
367+
$criteria = $database->createCriteria('#__users');
368+
369+
// From DAO object
370+
$criteria = $userDAO->createCriteria();
371+
```
372+
373+
You can define model classes (the objects returned from `SELECT` queries) and aliases when creating a `Criteria`:
374+
375+
```
376+
// From Database object
377+
$criteria = $database->createCriteria('#__users', 'MyNamespace\\User', 'a');
378+
379+
// From DAO object
380+
$criteria = $userDAO->createCriteria('a');
381+
```
382+
383+
As the DAO already knows about the model class, there is no need to mention it when creating from a `DAO`.
384+
385+
The alias is assigned to the underlying table name and will be added automatically when required in restrictions.
386+
387+
## Using Restrictions
388+
Restrictions are expressions that can be used in `WHERE` clauses (and in `JOIN` - see below). The helper
389+
class `Restrictions` helps you to create them:
390+
391+
```
392+
$expr1 = Restrictions::eq('name', 'myUsername');
393+
$expr2 = Restrictions::isNotNull('email');
394+
$criteria->add($expr1, $expr);
395+
```
396+
397+
The most common restrictions are provided: eq, ne, lt, gt, ge, le, like, isNull, isNotNull, between. You can also
398+
use restrictions between two properties:
399+
400+
```
401+
// Check for equalitity of name and email of a user.
402+
$expr = Restrictions::eq('name', 'email');
403+
```
404+
405+
And it is possible to combine restrictions with `and()` and `or()`:
406+
407+
```
408+
$expr = Restrictions::or($expr1, $expr2, $expr3);
409+
```
410+
411+
## Using Projections
412+
Basic projections - the aggregation of columns of different rows - are available:
413+
414+
```
415+
$proj = Projections::rowCount();
416+
$criteria->setProjection($proj);
417+
```
418+
419+
You will find projections for: count, distinct, sum, avg, min, max. Please notice that
420+
the returned model class is always the `stdClass` when using projections.
421+
422+
## Subcriteria and JOINs
423+
This is most likely the biggest advance in using the Criteria API. The traditional API methods
424+
were not able to use subcriteria when searching for objects depending from other tables.
425+
426+
Let's assume you want to find all books in a database whose author name start with an A.
427+
The main criteria comes from books then as it is our desired model class to be returned
428+
429+
```
430+
$criteria = $bookDAO->createCriteria('a');
431+
```
432+
433+
Next we join the authors table and add it to the main criteria using the respective restriction
434+
to join them properly:
435+
436+
```
437+
$authors = $authorDAO->createCriteria('b');
438+
$restriction = Restrictions::eq(array('a','author'), array('b','uid'));
439+
$criteria->addCriteria($authors, $restriction);
440+
```
441+
442+
And finally we apply the search condition for the author:
443+
444+
```
445+
$authors->add(Restrictions::like('name', 'A%'));
446+
```
447+
448+
## Getting the result
449+
That's the most easiest part:
450+
451+
```
452+
$criteria->list();
453+
```
454+
455+
You can set restrictions on the result:
456+
457+
```
458+
$criteria->setFirstResult(10);
459+
$criteria->setMaxResults(20);
460+
```
461+
462+
## Advantages and Limitations
463+
The Criteria API will further ease in searching object in a database and return model classes, using more
464+
complex expressions and restrictions. You will be able to dynamically apply restrictions depending on
465+
the requirements of your front-end users and your application. And you won't need the DAO once you
466+
created the `Criteria` object. It is self-contained.
467+
468+
However, some limitations exist:
469+
470+
' Criteria API supports basic use cases so far (searching objects with simple restrictions).
471+
* Only MySQL / MariaDB SQL dialect is produced (but can be extended to other dialects easily when you stick to the API).
472+
* GROUP BY clauses are not implemented
473+
* Multiple projections are not yet supported (such as `SELECT MAX(name), MIN(name) FROM #__users`) - will be extended.
474+
* Criteria API is not yet built into DAOs to make it more exchangeable. This might break the DAO API and hence will
475+
be added in one of the next major versions.
476+
* A few of the limitations may be ovecome by using the `SqlExpression` and `SqlProjection` classes:
477+
478+
```
479+
// Use a specific restriction not supported
480+
$myExpr = Restrictions::sql('my-sql-fragment');
481+
482+
// Use a specific projection not supported
483+
$myProj = Projections::sql('name, email');
484+
```
485+
486+
But feel free to raise an issue (see below) when you need some extension that is not yet supported.
487+
347488
# Contribution
348489
Report a bug, request an enhancement or pull request at the [GitHub Issue Tracker](https://github.com/technicalguru/php-database/issues).
349490

composer.json

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
"TgDatabase\\" : "src/TgDatabase/"
2424
}
2525
},
26+
"autoload-dev" : {
27+
"psr-4" : {
28+
"TgDatabase\\" : "tests/TgDatabase/"
29+
}
30+
},
2631
"extra": {
2732
"branch-alias": {
2833
"dev-master": "1.0-dev"

nbproject/project.properties

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include.path=${php.global.include.path}
2+
php.version=PHP_73
3+
source.encoding=UTF-8
4+
src.dir=.
5+
tags.asp=false
6+
tags.short=false
7+
web.root=.

nbproject/project.xml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://www.netbeans.org/ns/project/1">
3+
<type>org.netbeans.modules.php.project</type>
4+
<configuration>
5+
<data xmlns="http://www.netbeans.org/ns/php-project/1">
6+
<name>php-database</name>
7+
</data>
8+
</configuration>
9+
</project>

set-test-env.sh

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
3+
#######################################################
4+
#
5+
# TEST HOWTO
6+
#
7+
# Copy this file to e.g. set-local-test-env.sh and
8+
# set the various variables below. Then source the
9+
# file on you bash shell:
10+
#
11+
# ~/git/php-database$ . ./set-local-test-env.sh
12+
#
13+
# and start the PHP Unit tests:
14+
#
15+
# ~/git/php-database$ ./vendor/bin/phpunit tests
16+
#
17+
#######################################################
18+
19+
# The Database connect information
20+
DB_TEST_HOST=www.example.com
21+
DB_TEST_PORT=3306
22+
DB_TEST_NAME=test
23+
DB_TEST_PREFIX=test
24+
DB_TEST_USER=username
25+
DB_TEST_PASS=password
26+
27+
export DB_TEST_HOST
28+
export DB_TEST_PORT
29+
export DB_TEST_NAME
30+
export DB_TEST_PREFIX
31+
export DB_TEST_USER
32+
export DB_TEST_PASS
33+

src/TgDatabase/Criteria.php

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace TgDatabase;
4+
5+
/**
6+
* Criteria is a simplified API for retrieving entities by composing Criterion objects. This is a very
7+
* convenient approach for functionality like "search" screens where there is a variable number of
8+
* conditions to be placed upon the result set.
9+
*/
10+
interface Criteria {
11+
12+
/**
13+
* Add a restriction to constrain the results to be retrieved.
14+
* @return Criteria - this criteria for method chaining.
15+
*/
16+
public function add(Criterion ...$criterion);
17+
18+
/**
19+
* Add an ordering to the result set.
20+
*/
21+
public function addOrder(Order ...$order);
22+
23+
/**
24+
* Set a projection for the criteria.
25+
*/
26+
public function setProjection(Projection $projection);
27+
28+
/**
29+
* Set the index of the first result to be retrieved.
30+
* @return Criteria - this criteria for method chaining.
31+
*/
32+
public function setFirstResult(int $firstResult);
33+
34+
/**
35+
* Set a limit upon the number of rows to be retrieved.
36+
* @return Criteria - this criteria for method chaining.
37+
*/
38+
public function setMaxResults(int $maxResults);
39+
40+
/**
41+
* Create a new join criteria.
42+
*/
43+
public function createCriteria($tableName, $alias, $joinCriterion);
44+
45+
/**
46+
* Add a new join criteria.
47+
*/
48+
public function addCriteria(Criteria $criteria, $joinCriterion);
49+
50+
/**
51+
* Queries the database.
52+
*/
53+
public function list();
54+
55+
/**
56+
* Queries the database and returns all defined rows.
57+
*/
58+
public function first();
59+
}

src/TgDatabase/Criterion.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace TgDatabase;
4+
5+
/**
6+
* An object-oriented representation of a query criterion that may be used as a restriction in a Criteria query.
7+
* Built-in criterion types are provided by the Restrictions factory class. This interface might be implemented
8+
* by application classes that define custom restriction criteria.
9+
*/
10+
interface Criterion {
11+
12+
/**
13+
* Render the SQL fragment.
14+
* @param Criteria $localCriteria - local criteria object (e.g. subquery)
15+
* @param Criteria $overallCriteria - overall criteria object
16+
* @return string - the SQL fragment representing this criterion.
17+
*/
18+
public function toSqlString($localCritera, $overallCriteria);
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace TgDatabase\Criterion;
4+
5+
use TgDatabase\Projection;
6+
use TgDatabase\Criteria;
7+
8+
class AggregateProjection implements Projection {
9+
10+
public function __construct($functionName, $propertyName) {
11+
$this->functionName = $functionName;
12+
$this->propertyName = $propertyName;
13+
}
14+
15+
/**
16+
* Render the SQL fragment.
17+
* @param Criteria $localCriteria - local criteria object (e.g. subquery)
18+
* @param Criteria $overallCriteria - overall criteria object
19+
* @return string - the SQL fragment representing this criterion.
20+
*/
21+
public function toSqlString($localCriteria, $overallCriteria) {
22+
return $this->functionName.'('.$overallCriteria->quoteName($localCriteria->getAlias(), $this->propertyName).')';
23+
}
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace TgDatabase\Criterion;
4+
5+
use TgDatabase\Projection;
6+
use TgDatabase\Criteria;
7+
8+
class AliasedProjection implements Projection {
9+
10+
public function __construct($projection, $alias) {
11+
$this->projection = $projection;
12+
$this->alias = $alias;
13+
}
14+
15+
/**
16+
* Render the SQL fragment.
17+
* @param Criteria $localCriteria - local criteria object (e.g. subquery)
18+
* @param Criteria $overallCriteria - overall criteria object
19+
* @return string - the SQL fragment representing this criterion.
20+
*/
21+
public function toSqlString($localCriteria, $overallCriteria) {
22+
return $this->projection->toSqlString($localCriteria, $overallCriteria).' AS '.$overallCriteria->quoteName($this->alias);
23+
}
24+
}
25+

0 commit comments

Comments
 (0)