Database
Bifröst is not an ORM and it will never be one! It´s usage is to have a nice and secure way to write queries for an PDO supported database. If you don´t use database specific commands it will be, thanks to pdo, database independent.
How Create a new database instance
To create a new Bifröst instance you need a Config object. The Config object have different options like username, password, the DSN for the connection and the default DateTimeFormat for your Database. You will use the default PDO DSN string without username and password! You also can add PDO options with the method
Don´t overwrite the ErrorMode (PDO::ATTR_ERRMODE)!
$config->addPdoOption('PDO::MYSQL_ATTR_INIT_COMMAND', 'SET NAMES utf8');
Examples for PDO DSN:
mysql (PHP Documentation)
- mysql:host=localhost;dbname=testdb
- mysql:host=localhost;port=3307;dbname=testdb
- mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
sqlite (PHP Documentation)
- sqlite:/opt/databases/mydb.sq3
- sqlite::memory:
Usage:
$config = new Config();
$config->setUsername('username');
$config->setPassword('secrect');
$config->setDsn('mysql:host=localhost;dbname=testdb');
$config->setDateTimeFormat('Y-m-d H:i:s'); // This is the default
$config->addPdoOption('PDO::MYSQL_ATTR_INIT_COMMAND', 'SET NAMES utf8');
$database = new Database($config);
You should not switch between databases with a SQL command. This will cause troubles! Bifröst ist designed to be used with multiple database instances.
Bifröst will not establish a connection to the database until it is really required to. That means it´s cheap to produce all your database objects -but still you should use a factory or DI-Container. I tried also to look at detail on the object count references to be long running process compatible without memory leaks.
For Example:
$masterConfig = new Config();
...
$slaveConfig = new Config();
...
$masterDatabase = new Database($masterConfig);
$slaveDatabase = new Database($slaveConfig);
// or
$product1Config = new Config();
...
$product2Config = newConfig();
...
$product1Database = new Database($product1Config);
$product2Database = new Database($product2Config);
Run SQL
You can run directly SQL with the Bifröst instance. This is usefully for fixtures, init scripts and UnitTests which will create a new database or alter some tables, etc. You should use this method just for that case!
...
$db = new Database($config);
$createSQL = file_get_contents(__DIR__ . '/fixtures.sql');
$db->run($createSQL);
AddActionAfterConnect
Some PDO drivers (namely SQLite) support the possibility to add functions which are available to SQL. This feature is currently marked as experimental but Bifröst supports it. It´s important to know, that the actions will be added after a connection is established and just can be added before a connection is established. The Method addActionAfterConnect awaits a Closure (PHP 5.3 compatible) which gets a PDO instance as first param.
Example:
$config = new Config();
$config->setUsername('user');
$config->setDsn('sqlite::memory:');
$sqliteDb = new Database($config);
$sqliteDb->addActionAfterConnect(function (PDO $pdo) {
$pdo->sqliteCreateFunction('md5rev', function ($string) {
return strrev(md5($string));
}, 1);
});
$sqliteDb->run(self::$createSQL);
$row = $sqliteDb->query("SELECT username, md5rev(username) FROM users
WHERE id = :ID")
->bindInt('ID', 1)
->fetchRow();
Query
The Query class use a fluent interface. It´s designed to be nice, logical and natural. It enforces strong typing, so you need to parse and cast your values. Internally it uses the PreparedStatement from PDO. In that combination, you should be not vulnerable to SQL Injection and type problems with your SQL ('1' != 1). Look at Examples if you wan´t to see usage examples.
How to use Bifröst to query your database
You need a Bifröst instance and then you call the method "query" with your SQL query. Bifröst will automatically handle the rest for you (like establish connection to the database, is this a new query?, can I reuse this already prepared query, etc).
Example
...
$database = new Database($config);
$database->query("SELECT * FROM `users`")
->fetchAll();
Usage
You define your query with placeholders. A placeholder should be uppercase and start with a double dash like: :ID, :START_DATE, :END_DATE, :USERNAME.
SELECT * FROM `users` WHERE id = :ID
Now you can bind values to the :ID placeholder with the bind methods. Caution: You don´t use the double dash in the key with the bind methods! )
$database = new Database($config);
$database->query('SELECT * FROM `users` WHERE id = :ID')
->bindInt('ID', 1)
Bind methods
The bind methods needs the key they should binded to and the value which should be bindet. The value must be typesafe or else it will throw an \Sindri\Database\Exception\InvalidArgumentException
bindInt($key, $value)
bindFloat($key, $value)
bindString($key, $value)
If your query is in prepared state (Fetch-Method, execute(), prepare(...) ) and you try to bind a key, which is not known to it, it will throw an \Sindri\Database\Exception\InvalidArgumentException.
BindArray method
The bind array method does some internal handling. It determines the type of the values and bind with the correct method. It´s also possible to bind an array with no elements. This will not break your SQL query if you write your query in the correct syntax. The syntax is
"SELECT * FROM `users` WHERE id IN (:IDS)"
It´s important to understand how Array-Binding works. If you bind an array with 4 entries to the key IDS Bifröst will transform it in something like ID_0, ID_1, ID_2, ID_3 and bind its value to it. When the query get´s in prepared state (Fetch-Method, execute(), prepare(...) ), then you get an \Sindri\Database\Exception\InvalidArgumentException, if you try to reuse the query with a differend sized array!
bindArray($key, $value)
BindDate methods
There are two bindDate methods. One will bind your DateTime object with your timezone setting the other one will bind your DateTime object converted to the UTC timezone. It is possible to modfy for the complete query the Date-Time-Format without changing the default Date-Time-Format of your Database. If you call setDateTimeFormat, this will change the format for the whole life-cycle (think about prepared queries!)
setDateTimeFormat($format)
Will use your timezone settings
bindDate($key, DateTime $date)
Will convert the date in UTC (does not modify your object!)
bindDateUTC($key, DateTime $date)
Execute method
This method is for UPDATE, INSERST, etc. which will not fetch value from the database. This method will establish a connection. After calling execute you can´t register any new "actionAfterConnect". The query is also be prepared now. This is important for Array-Binding. See "BindArray method" for more informationen about that topic.
execute()
Prepare method
With the prepare method, you can prepare your sql queries for later usage. The syntax may looks a bit weired at first, so.
Say we want a query which selects users which ids in the array(1,2,3,4) and a register date later then 2013-01-01. We need this query more then one time and want to prepare it.
$userQuery = $database->query("SELECT * FROM `users`
WHERE id IN (:IDS) and `registered` > :STARTDATE")
->prepare(array(
array('IDS', 4), // Important! bindArray with key IDS and 4 entries
'STARTDATE' // The key STARTDATE is now known
));
// now we can run the prepared query like
$userQuery->bindArray('IDS', array(1,2,3,4)
->bindDateUTC('STARTDATE', new Date('2013-01-01'))
->fetchAll();
Puh... the method:
prepare(array $bindings)
Fetch methods
How you will get your query results.
This will return all the result in assoc
fetchAll()
This will just return the first value of the first row. If there is no result, it will return the $nullValue (default '')
fetchValue($nullValue = '')
Thi will return the $column as an array
fetchColumn($column = 0)
This will return a row in assoc
fetchRow()
Transactions
todo (version 0.8)
Stored Procedures
todo (version 0.9)
Examples
Assumed the following table (users)
id | username | passwort | registered |
---|---|---|---|
1 | bill | god | 2013-01-02 15:08:23 |
2 | jack | love | 2012-06-12 11:36:42 |
3 | jones | secret | 2013-03-07 18:44:51 |
4 | seb | leet | 2013-05-01 13:22:01 |
5 | peter | foo | 2012-04-02 01:21:03 |
6 | hans | nerd | 2012-10-12 07:38:33 |
SQL (saved as example.sql)
CREATE TABLE [users] (
[id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[username] varCHAR(50) UNIQUE NULL,
[password] varCHAR(32) NULL,
[registered] DATETIME NULL
);
INSERT INTO users (id, username, password, registered) VALUES (1, 'bill', 'god', '2013-01-02 15:08:23');
INSERT INTO users (id, username, password, registered) VALUES (2, 'jack', 'love', '2012-06-12 11:36:42');
INSERT INTO users (id, username, password, registered) VALUES (3, 'jones', 'secret', '2013-03-07 18:44:51');
INSERT INTO users (id, username, password, registered) VALUES (4, 'seb', 'leet', '2013-05-01 13:22:01');
INSERT INTO users (id, username, password, registered) VALUES (5, 'peter', 'foo', '2012-04-02 01:21:03');
INSERT INTO users (id, username, password, registered) VALUES (6, 'hans', 'nerd', '2012-10-12 07:38:33');
Initialize Code
use \Sindri\Database\Config;
use \Sindri\Database\Database;
$config = new Config();
$config->setDsn('sqlite::memory:');
$db = new Database($config);
$db->run(file_get_contents(__DIR__ . '/example.sql'));
Simple Query
$result = $db->query('SELECT * FROM `users` WHERE id = :ID')
->bindInt('ID', 2)
->fetchRow();
var_dump($result);
// Result
array(4) {
'id' =>
string(1) "2"
'username' =>
string(4) "jack"
'password' =>
string(4) "love"
'registered' =>
string(19) "2012-06-12 11:36:42"
}
More will come =)