Anax Database module as a database abstraction layer (DBA) for wrapping PHP PDO with an additional layer of utilities and ease of use together with ability to use configuration file and attach into an Anax installation as a servide in $di.
The module is tested using MySQL and SQLite.
There are separate modules for a Database Query Builder in anax\database-query-builder
and a Database Active Record in anax\database-active-record
.
- Install
- Development
- Class, interface, trait
- Exceptions
- Configuration file
- DI service
- Access as framework service
- Create a connection
- Perform a SELECT query
- Fetch versus FetchAll
- FetchClass
- FetchInto
- Perform an INSERT, UPDATE, DELETE query
- Last insert id
- Row count, affected rows
- Throw exception on failure
- Dependency
- License
You can install the module from anax/database
on Packagist using composer.
composer require anax/database
You can then copy the default configuration files as a start.
# In the root of your Anax installation
rsync -av vendor/anax/database/config .
To work as a developer you clone the repo and install the local environment through make. Then you can run the unit tests.
make install
make test
The following classes exists.
Class, interface, trait | Description |
---|---|
Anax\Database\Database |
Wrapper class for PHP PDO with enhanced error handling and extra utilities. |
Anax\Database\DatabaseDebug |
An alternative class that can be used for debugging database related issues. |
All exceptions are in the namespace Anax\Database\Exception\
. The following exceptions exists and may be thrown.
Exception | Description |
---|---|
Exception |
General module specific exception, for example when connection fail. |
This is a sample configuration file. It is usually stored in config/database.php
when used together with Anax.
/**
* Config file for Database.
*
* Example for MySQL.
* "dsn" => "mysql:host=localhost;dbname=test;",
* "username" => "test",
* "password" => "test",
* "driver_options" => [
* \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"
* ],
*
* Example for SQLite.
* "dsn" => "sqlite::memory:",
* "dsn" => "sqlite:$path",
*
*/
return [
"dsn" => null,
"username" => null,
"password" => null,
"driver_options" => null,
"fetch_mode" => \PDO::FETCH_OBJ,
"table_prefix" => null,
"session_key" => "Anax\Database",
"emulate_prepares" => false,
// True to be very verbose during development
"verbose" => null,
// True to be verbose on connection failed
"debug_connect" => false,
];
You can use if-statements within the configuration file to serve different configurations for local development environment, staging and or production environment.
The database is created as a framework service within $di
. The following is a sample on how the database service is created through config/di/db.php
.
/**
* Configuration file for database service.
*/
return [
// Services to add to the container.
"services" => [
"db" => [
"shared" => true,
"callback" => function () {
$db = new \Anax\Database\Database();
// Load the configuration files
$cfg = $this->get("configuration");
$config = $cfg->load("database");
// Set the database configuration
$connection = $config["config"] ?? [];
$db->setOptions($connection);
return $db;
}
],
],
];
The setup callback works like this.
- The database object is created.
- The configuration file is read.
- The configuration is applied.
You can access the database module as a framework service.
$sql = "SELECT * FROM movie;";
# $app style
$app->db->connect();
$res = $app->db->executeFetchAll($sql);
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->executeFetchAll($sql);
You must connect to the database before using it.
You may call $db->connect()
many times, the connection is however only made once, the first time, so it is safe to call the method several times.
# $app style
$app->db->connect();
# $di style
$di->get("db")->connect();
You connect and perform the query which returns a resultset.
$sql = "SELECT * FROM movie;";
# $app style
$app->db->connect();
$res = $app->db->executeFetchAll($sql);
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->executeFetchAll($sql);
The contents of $res
is depending on the configuration key which default is set to "fetch_mode" => \PDO::FETCH_OBJ,
.
You can also separate executeFetchAll()
into two separate commands execute()
and fetchAll()
.
$sql = "SELECT * FROM movie;";
# $app style
$app->db->connect();
$res = $app->db->execute($sql)->fetchAll();
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->execute($sql)->fetchAll();
With fetchAll()
you fetch all matching rows in an array. When no matching rows are found you get en ampty array.
With fetch()
you get the first item in the resultset. You may use this when your resultset only contain one row.
$sql = "SELECT * FROM movie WHERE id = 1;";
# $app style
$app->db->connect();
$res = $app->db->executeFetch($sql);
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->executeFetch($sql);
The content of $res
is now an object of type \StdClass
and you access the resultset columns by their name, for example $res->id
.
You can fetch the resultset into an object instantiated from a specified class.
$sql = "SELECT * FROM movie WHERE id = ?;";
# $app style
$app->db->connect();
$res = $app->db->executeFetchClass($sql, [1], "\Anax\SomeClass");
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->executeFetchClass($sql, [1], "\Anax\SomeClass");
The resultset is inserted into a new object of the class "\Anax\SomeClass"
.
There is also executeFetchAllClass()
which fetches an array of all matching rows as new objects of the class.
You can fetch the resultset into an existing object as public properties.
$sql = "SELECT * FROM movie WHERE id = ?;";
$obj = new SomeClass();
# $app style
$app->db->connect();
$res = $app->db->executeFetchInto($sql, [1], $obj);
# $di style
$db = $di->get("db");
$db->connect();
$res = $db->executeFetchClass($sql, [1], $obj);
The resultset is inserted into the object $obj
.
These queries, that updates the database, uses $db->execute()
and does not return a resultset.
$sql = "UPDATE movie SET title = ? WHERE id = ?;";
# $app style
$app->db->connect();
$app->db->execute($sql, ["Some title", 1]);
# $di style
$db = $di->get("db");
$db->connect();
$db->execute($sql, ["Some title", 1]);
You can check the last inserted id when doing INSERT where the primary key is auto generated.
$sql = "INSERT INTO movie (title) VALUES (?);";
# $app style
$app->db->connect();
$app->db->execute($sql, ["Some title"]);
$id = $app->lastInsertId();
# $di style
$db = $di->get("db");
$db->connect();
$db->execute($sql, ["Some title"]);
$id = $db->lastInsertId();
You can check how many rows that are affected by the last INSERT, UPDATE, DELETE statement.
$sql = "DELETE FROM movie;";
# $app style
$app->db->connect();
$app->db->execute($sql);
$num = $app->rowCount();
# $di style
$db = $di->get("db");
$db->connect();
$db->execute($sql);
$num = $db->rowCount();
Exception are in general thrown as soon as something fails.
The exception is module specific Anax\Database\Exception\Exception
and contains details from the error message from the PDO layer, either from the statement or from the PDO-object, depending on what type of error happens.
No particular dependencies. The module is usually used within an Anax installation but can also be used without Anax.
This software carries a MIT license. See LICENSE.txt for details.
.
..: Copyright (c) 2013 - 2018 Mikael Roos, [email protected]