Chapter 3. Db

Table of Contents

Atomik_Db and Instances
Managing instances
Using Atomik_Db and a default instance
Working with Atomik_Db_Instance
Connecting to a database
Querying the database
Manipulating data
Creating queries using Atomik_Db_Query
Building queries
Executing the query
Manipulating query results
Caching queries

The Db component provides database features over PDO. Thus, it is advice to know how to use PDO before using it. Check out http://php.net/pdo for more information.

Atomik_Db and Instances

The Db component allows you to manage as many connections as you want. Each connection is bound to a manager class called an instance of type Atomik_Db_Instance.

The Atomik_Db class can be used to manage all instances and also offers a way to access statically all methods from a default instance.

Managing instances

Each instances managed by Atomik_Db must be named.

You can add instances to Atomik_Db using addAvailableInstance(). The first argument is the name and the second the instance object.

Example 3.1. Adding instances to Atomik_Db

					
Atomik_Db::addAvailableInstance('db1', $instance);
				

You can then check if an instance is available using isInstanceAvailable() and retrieve all instances using getAvailableInstances().

Finally, it is possible to create and register an instance using createInstance(). It takes as first argument the instance name and then the same as argument as Atomik_Db_Instance's constructor. There is a fifth parameter, which is true by default, that, if true, will set the new instance as the default one.

Example 3.2. Creating instances using Atomik_Db

					
$instance = Atomik_Db::createInstance('db1', $dsn, $username, $password);
				

Using Atomik_Db and a default instance

All Atomik_Db_Instance methods are available as static methods on the Atomik_Db class. Calling these methods will forward the call to the default instance.

The default instance can be set using Atomik_Db::setInstance(). It takes as argument an instance name or object. It can be retrieve using getInstance().

Example 3.3. Setting a default instance and using static methods

					
Atomik_Db::setInstance($instance);
$result = Atomik_Db::findAll('table');
				

Thus, all methods covered in the next section about instances can also be used on the Atomik_Db class.

Working with Atomik_Db_Instance

Connecting to a database

Atomik_Db_Instance supports lazy connections. This means that it will connect to the database only if a query is performed.

Connection information can be defined when creating the object.

Example 3.4. Creating a Atomik_Db_Instance object

					
$instance = new Atomik_Db_Instance($dsn, $username, $password);
				

You can then force the connection by calling Atomik_Db_Instance::connect() without any arguments.

If you created a Atomik_Db_Instance object without any constructor arguments, you can use connect() to connect to the database.

Example 3.5. Connecting to a database using connect()

					
$instance->connect('mysql:host=localhost;dbname=mydb', 'root', '');
				

Tip

The PDO object which Atomik_Db_Instance uses can be accessed using the pdo property.

Querying the database

Using PDO-like methods

The Atomik_Db_Instance class provides three alias methods to PDO methods: query(), exec() and prepare(). The two last one behave exactly the same way. However query() is a little different.

Under the hood it creates a PDO statement using prepare and executes it. Thus, the method also allows an additional argument which can contains an array to pass to the execute call of the statement.

Example 3.6. Using Atomik_Db_Instance::query()

						
$results = $instance->query('select * from posts');
$results = $instance->query('select * from posts where id=?', array(1));
					

Example 3.7. Using other PDO methods with Atomik_Db_Instance

						
$statement = $instance->prepare('select * from posts');
$statement->execute();

$results = $instance->exec("insert into posts (content) values ('my new post')");
					

Using find methods

The Atomik_Db_Instance class also provides two powerful methods to query the database: find() and findAll(). They are exactly the same but the first one will return only one record whereas the second all of them.

Conditions are specified as an associative array. The keys are database fields. An sql string can also be used instead of the array.

Example 3.8. Finding records from one table

						
// all records from the posts table
$results = $instance->findAll('posts');

// only records from the author 3
$results = $instance->findAll('posts', array('author' => 3));
// or
$results = $instance->findAll('posts', 'author = 3');
					

You can also specify an order by and a limit clause. Respectively as the third and fourth arguments.

Example 3.9. Finding records with order by and limit clauses

						
// all records from the posts table ordered by creation_date
$results = $instance->findAll('posts', null, 'creation_date ASC');

// the first 10 records from the posts table
$results = $instance->findAll('posts', null, '', '10');

// the first 10 records from the posts table ordered by creation_date
$results = $instance->findAll('posts', null, 'creation_date' , '10');

// records 10 to 20 from the posts table
$results = $instance->findAll('posts', null, '', '10, 10');
					

Example 3.10. Working with the result of find methods

						
$posts = $instance->findAll('posts');
foreach ($posts as $post) {
	echo $post['title'];
	echo $post['content'];
}
					

Counting records

You can perform SELECT COUNT(*) queries using Atomik_Db_Instance::count(). This method takes the same parameters as Atomik_Db_Instance::find().

Example 3.11. Counting records from one table

						
$numberOfPosts = $instance->count('posts');
					

Manipulating data

Insert

The Atomik_Db_Instance::insert() method can be use to insert data into the database. It takes as first argument the table name and as second an associative array where keys are fields name.

Example 3.12. Inserting data into the database

						
$instance->insert('posts', array('title' => 'my first posts', 'content' => 'hello world'));
// will execute: insert into posts (title, content) values('my first post', 'hello world')
					

All values are automatically escaped.

Update

Atomik_Db_Instance::update() works pretty much the same. However it takes as the last argument an array of conditions (the same way as in find methods) or an sql string.

Example 3.13. Updating data

						
$instance->update('posts', array('content' => 'updated hello world'), array('id' => 1));
// will execute: update posts set content = 'updated hello world' where posts.id = 1
					

Delete

Finally, the Atomik_Db_Instance::delete() works the same as find methods (without the order by and limit arguments).

Example 3.14. Deleting data

						
$instance->delete('posts', array('id' => 1));
// will execute: delete from posts where posts.id = 1

$instance->delete(array('posts', array('id' => 1)));
// will execute: delete from posts where posts.id = 1

$instance->delete('posts', 'id = 1');
// will execute: delete from posts where id = 1
					

Creating queries using Atomik_Db_Query

Atomik_Db_Query provides a way to create sql queries without writing a line of sql! Atomik_Db_Instance uses Atomik_Db_Query under the hood for all its requests.

Atomik_Db_Query can be used independently of instances and only requires a PDO object to be executed. The result of such a query is returned as an Atomik_Db_Query_Result object. This object manages a PDOStatement.

Note

As Atomik_Db_Instance is using Atomik_Db_Query, all results returned by its methods are expressed using Atomik_Db_Query_Result.

Building queries

Using Atomik_Db_Query can be thought as similar to writing sql queries. Methods can be chained so that the flow of a query is not disrupted.

Example 3.15. Selecting data using Atomik_Db_Query

					
$query = new Atomik_Db_Query();
$query->select()->from('users')->where('id = ?', 1);
				

select() takes as arguments which fields to select.

from() takes as argument a table name. This method can be called several times to query from multiple tables. An alias can be specified as second argument.

where() can be used in multiple ways. As shown above, it can be an sql string. This string can contain positional parameter which can then be specified as method arguments.

The method can also take as first argument an array where keys are field's name. All values will be automatically escaped. This can be prevented using the expr() method of the query object. It takes as argument the value which won't be escaped.

Multiple call to where() can be performed. They will be concatenated using the AND sql operator. If you want to use OR instead you can use the orWhere() method.

Example 3.16. Specifying where clauses

					
$query = new Atomik_Db_Query();
$query->select()->from('users')->where('id = 1');
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->where('name = ?', 'peter')->where('password = ?', 'foo');
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->where('join_date = ?', $query->expr('NOW()'));
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->where(array('id' => 1))->orWhere('id = 2');
				

Atomik_Db_Query also allows you to perform joins using join(), group by using groupBy(). You can also specify an having clause using having() (which works the same as where());

An order by clause can be specified using orderBy() which takes a field name as first argument and optionally the direction (ASC or DESC) as second.

Example 3.17. Specifying an order by clause

					
$query = new Atomik_Db_Query();
$query->select()->from('users')->orderBy('name');
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->orderBy('name ASC');
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->orderBy('name', 'DESC');
				

Finally, you can specify a limit clause which takes either a single argument which will be the length or two arguments where the first one will be the offset and the second the length.

Example 3.18. Specifying a limit clause

					
$query = new Atomik_Db_Query();
$query->select()->from('users')->limit(10);
				
					
$query = new Atomik_Db_Query();
$query->select()->from('users')->limit(10, 10);
				

Atomik_Db_Query can also be used to generate ddl statements (insert, update and delete) using the same approach.

Example 3.19. Building ddl statements

					
$query = new Atomik_Db_Query();
$query->insertInto('users')->values(array('username' => 'peter', 'password' => 'foo'));
				
					
$query = new Atomik_Db_Query();
$query->update('users')->set(array('password' => 'bar'))->where('username = ?', 'peter');
				
					
$query = new Atomik_Db_Query();
$query->delete()->from('users')->where('username = ?', 'peter');
				

Note

Each time select(), insertInto(), update() or delete() are called, the query is reseted.

You can define a table prefix for all queries using the static method setDefaultTablePrefix().

Executing the query

You can get the generated sql using toSql() or using the object in a string context (echoing for example).

If you're using parameters in your query, they won't be included in the sql string (the question mark, or the key will be kept). All parameters can be retrieved using getParams().

Example 3.20. Executing the query against a PDO object

					
$stmt = $pdo->prepare($query->toSql());
$stmt->execute($query->getParams());
				

However, this can be simplified by simply calling the execute() method of the query object with a PDO object as argument.

Example 3.21. Executing a query

					
$result = $query->execute($pdo);
				

When using this method, the result is returned as a Atomik_Db_Query_Result object that will cover in the next section.

You can also pass query objects to Atomik_Db_Instance::query() without the need to specify the parameters as second argument.

Example 3.22. Executing a query with Atomik_Db_Instance::query()

					
$result = $instance->query($query);
				

You can define a parameter at any time using setParam().

It is also possible to define a pdo object for a query using setPdo() or a default pdo object for all queries using the setDefaultPdo() static method. In this case, it's not needed to pass a pdo object to execute().

Manipulating query results

Rather than directly returning the PDOStatement object, Atomik_Db_Query::execute() returns an Atomik_Db_Query_Result object. The reason is that PDOStatement can only be iterate over once whereas the result object can be iterate as anytime you want.

For the most part, result objects work the same way as PDOStatement and have the same methods. However, one of the main differences is that the fetch mode can only be set before any rows are fetched. This is to ensure that all cached data are coherent.

As a result of the previous condition, the fetch() method does not have a fetch mode argument.

Example 3.23. Iterating over results

					
while ($row = $result->fetch()) {
	// do something
}
				
					
foreach ($result as $row) {
 // do something
}
				
					
for ($i = 0; $i < $result->rowCount(); $i++) {
	$row = $result[$i];
}
				

You can check if the data have already been fetched using isCached(). You can also reload the data using reload() (this will re-execute the query and reset the result object).

fetchAll() is also available. There's also an alias method named toArray(). Note that fetchColumn() is not available.

You can still access the PDOStatement using getStatement() (unless the query is cached) and the associated query using getQuery().

Caching queries

We've seen that query result can be queried over many times without re-executing the query. But what about the same query being executed multiple times. Atomik_Db_Query allows you to cache queries so the same query with the same parameters won't be executed a second time.

For a query to be cached, call setCacheable() with true as argument. You can also specify to cache all queries by default using the static method setCacheAllQueries().

You can check if query result are cached using isCached() or empty the cache using emptyCache().