Database

This chapter talks about the basics of configuring the database connection, creating tables, running database scripts from the extensions and manually building database queries.

Note To map your application data to database tables in a comfortable way, the recommended way is the Pagekit Object-relational mapper (ORM) which is described in its own chapter.

Configuration

Database credentials are stored in config.php. Pagekit supports mysqland sqlite.

  1. 'database' => [
  2. 'connections' => [
  3. 'mysql' => [
  4. 'host' => 'localhost',
  5. 'user' => 'root',
  6. 'password' => 'PASSWORD',
  7. 'dbname' => 'DATABASE',
  8. 'prefix' => 'PREFIX_',
  9. ],
  10. ],
  11. ],
  12. ...

Working with database prefixes

All table names include the prefix of your Pagekit installation. To dynamically address tables in the backend, use the table name with the @ symbol as a placeholder for the prefix. As a convention you should start the table name with your extension name, e.g. options table for the foobar extension: @foobar_option

Database utility

You can manage your database schema using the database service utility (see the following examples).

  1. $util = $this['db']->getUtility();

Check if tables exists

  1. if ($util->tablesExist(['@table1', '@table2'])) {
  2. // tables exists
  3. }

Create table

Use Utility::createTable($table, \Closure $callback) to create a table, the first parameter passed to the callback will be a Doctrine\DBAL\Schema\Table instance.

  1. $util->createTable('@foobar_option', function($table) {
  2. $table->addColumn('id', 'integer', ['unsigned' => true, 'length' => 10, 'autoincrement' => true]);
  3. $table->addColumn('name', 'string', ['length' => 64, 'default' => '']);
  4. $table->addColumn('value', 'text');
  5. $table->addColumn('autoload', 'boolean', ['default' => false]);
  6. $table->setPrimaryKey(['id']);
  7. $table->addUniqueIndex(['name'], 'OPTION_NAME');
  8. });

The $table object is an instance of \Doctrine\DBAL\Schema\Table. You can find its class reference in the official Doctrine documentation.

When creating a column using addColumn, you might want to look at the available data types and the availabe column options from the Doctrine documentation as well.

Creating a table is commonly done in the install hook of the scripts.php inside your extension. Read more about Migrations in the next section.

Migrations

Database migrations are defined in the 'updates' section of the scripts.php in your extension. A full example for the scripts.php can be found in the Hello extension. Remember to link that file from your composer.json so that it is actually executed:

  1. "extra": {
  2. "scripts": "scripts.php"
  3. },

Within the scripts.php, you can hook into different events of the extension lifecycle.

Event hookDescription
installCalled when the extension is installed. Usually you create your tables here.
enableCalled when the extension is enabled in the admin area.
uninstallCalled when the extension is removed. This is the place to tidy up whatever you have created, i.e. drop all tables of your extension.
updatesRun any code when your extension is updated. Expects an array where each key is the version number from which this code should be run. Example:
  1. ```
  2. /*
  3. * Runs all updates that are newer than the current version.
  4. *
  5. */
  6. 'updates' => [
  7. '0.5.0' => function ($app) {
  8. // executed when upgrading from a version older than 0.5.0
  9. },
  10. '0.9.0' => function ($app) {
  11. // executed when upgrading from a version older than 0.9.0
  12. },
  13. ],
  14. ```

Alter an existing table

To alter an existing table, use the existing tools of the underlying Doctrine DBAL. To add columns to an existing table, you can include the following snippets in one of the updates version hooks of your extension's scripts.php.

  1. use Doctrine\DBAL\Schema\Comparator;
  2. // ...
  3. $util = App::db()->getUtility();
  4. $manager = $util->getSchemaManager();
  5. if ($util->tableExists('@my_table')) {
  6. $tableOld = $util->getTable('@my_table');
  7. $table = clone $tableOld;
  8. $table->addColumn('title', 'string', ['length' => 255]);
  9. $comparator = new Comparator;
  10. $manager->alterTable($comparator->diffTable($tableOld, $table));
  11. }

The $table object is an instance of \Doctrine\DBAL\Schema\Table. You can find its class reference in the official Doctrine documentation.

Queries

There are several ways of accessing the database. Pagekit offers an abstracting on the underlying MySQL or SQLite, so there is no need to use PDO or similar mechanisms.

1. Query builder

The QueryBuilder allows for a more comfortable way of creating queries.

Example:

  1. $result = Application::db()->createQueryBuilder()->select('*')->from('@blog_post')->where('id = :id', ['id' => 1])->execute()->fetchAll();

Get a query builder object

  1. use Pagekit\Application;
  2. // ...
  3. $query = Application::db()->createQueryBuilder();

Basic selects and conditions

MethodDescription
select($columns = ['*'])Creates and adds a "select" to the query.
from($table)Creates and sets a "from" to the query.
where($condition, array $params = [])Creates and adds a "where" to the query.
orWhere($condition, array $params = [])Creates and adds a "or where" to the query.

Example:

  1. // create query
  2. $query = Application::db()->createQueryBuilder();
  3. // fetch title and content of all blog posts that do not have any comments
  4. $comments = $query
  5. ->select(['title', 'content'])
  6. ->from('@blog_post')
  7. ->where('comment_count = ?', [0])
  8. ->get();

Query execution

MethodDescription
get($columns = [''])Execute the query and get all results.
first($columns = [''])Execute the query and get the first result.
count($column = '')Execute the query and get the "count" result.
execute($columns = [''])Execute the "select" query.
update(array $values)Execute the "update" query with the given values.
delete()Execute the "delete" query.

Aggregate functions

MethodDescription
min($column)Execute the query and get the "min" result.
max($column)Execute the query and get the "max" result.
sum($column)Execute the query and get the "sum" result.
avg($column)Execute the query and get the "avg" result.

Example:

  1. // create query
  2. $query = $query = Application::db()->createQueryBuilder();
  3. // determine total number of blog comments
  4. $count = $query
  5. ->select(['comment_count'])
  6. ->from('@blog_post')
  7. ->sum('comment_count');

Advanced query methods

MethodDescription
whereIn($column, $values, $not = false, $type = null)Creates and adds a "where in" to the query.
orWhereIn($column, $values, $not = false)Creates and adds a "or where in" to the query.
whereExists($callback, $not = false, $type = null)Creates and adds a "where exists" to the query.
orWhereExists(Closure $callback, $not = false)Creates and adds a "or where exists" to the query.
whereInSet($column, $values, $not = false, $type = null)Creates and adds a "where FIND_IN_SET" equivalent to the query.
groupBy($groupBy)Creates and adds a "group by" to the query.
having($having, $type = null)Creates and adds a "having" to the query.
orHaving($having)Creates and adds a "or having" to the query.
orderBy($sort, $order = null)Creates and adds an "order by" to the query.
offset($offset)Sets the offset of the query which means that the results will not start with the first result but with the result defined by the integer index $offset. This is useful for paging.
limit($limit)Sets the limit of the query. $limit defines the maximum count of results to be returned.
getSQL()Gets the query SQL.

Joins

MethodDescription
join($table, $condition = null, $type = 'inner')Creates and adds a "join" to the query.
innerJoin($table, $condition = null)Creates and adds an "inner join" to the query.
leftJoin($table, $condition = null)leftJoin($table, $condition = null)
rightJoin($table, $condition = null)Creates and adds a "right join" to the query.

2. ORM Queries

When you have set up ORM in your extension, you can create very readable queries using your model class.

Example:

  1. $result = Role::where(['id <> ?'], [Role::ROLE_ANONYMOUS])->orderBy('priority')->get();

The following methods are available (defined in the ModelTrait).

MethodDescription
create($data = [])Creates a new instance of this model from the passed in data array.
where($condition, array $params = [])Specify a where condition. Question marks in the condition are replaced by the parameters that you pass in. Returns a QueryBuilder object so you can chain method calls for more specific queries. Example: User::where(['name = ?'], ['peter'])
find($id)Retrieves a model entity by its identifier.
findAll()Retrieves all entities of this model.
save(array $data = [])Saves the model entity.
delete()Deletes the model entity.
toArray(array $data = [], array $ignore = [])Returns the model data as an array. Pass in a list of property keys to be included as the $data parameter. Pass in a list of property keys to be excluded as the $ignore parameter.
query()Returns a ORM\QueryBuilder instance to use any methods from that class. This instance offers all methods from the regular query builder, plus some additional ones, specifically for ORM.

ORM Query Builder: Additional methods

MethodDescription
get()Executes the query and gets all results.
first()Executes the query and gets the first result.
related($related)Set the relations that will be eager loaded.
getRelations()Gets all relations of the query.
getNestedRelations($relation)Gets all nested relations of the query.

Example:

  1. $comments = Comment::query()->related(['post' => function ($query) {
  2. return $query->related('comments');
  3. }])->get();

3. Raw queries

The plainest way to query the database is by sending raw queries to the database. This is basically just a wrapper around PDO.

  1. $result = Application::db()->executeQuery('select * from @blog_post')->fetchAll();
  2. $result = Application::db()->executeQuery('select * from @blog_post WHERE id = :id', ['id' => 1])->fetchAll();

Insert

Inserting data in the database can be done using the database connection instance that you can fetch via Application::db() (remember to use Pagekit\Application; at the top of your file).

Use the method insert($tableExpression, array $data, array $types = array())

Example:

  1. Application::db()->insert('@system_page', [
  2. 'title' => 'Home',
  3. 'content' => "<p>Hello World</p>",
  4. 'data' => '{"title":true}'
  5. ]);

When using ORM, you just need to create a new model instance and call the save() method.

ORM

With the Object-relational mapping (ORM) in Pagekit, you can bind a Model class to a database table. While this takes a few more lines to setup than the QueryBuilder, the ORM takes a lot of manual work out of your hands. Using ORM is the recommended way of managing how you store and retrieve your application data to and from the database. Read more about the Pagekit ORM.