Building SQL queries with the Query Builder
Introductionβ
Bow provides an API for building queries. The table method lets you build an SQL query based on the table name and returns an instance of Bow\Database\QueryBuilder::class.
use Bow\Database\Database;
$builder = Database::table('users');
// => Instance \Bow\Database\QueryBuilder::class
You can also use the app_db_table helper:
$builder = app_db_table('users');
On Bow's QueryBuilder instance there are several methods that let you build an SQL query.
For example, the toSql method, which lets you display the query you built.
$builder->toSql();
// select * from `users`
Retrieving informationβ
To retrieve information with the builder, you should use the get method, which returns a collection; first, which returns null or a stdclass object; and last, which behaves like first except that it returns the last element of the query execution result instead.
Example with getβ
$builder = Database::table('users');
$users = $builder->get();
foreach ($users as $user) {
echo $user->name;
}
You can pass an array to get that is a list of the columns to project, like this: $builder->get(['name']).
Example with firstβ
$user = app_db_table('users')->first();
// Empty
is_null($user)
// Ok
echo $user->name;
Example with lastβ
$user = $builder->last();
Adding restrictionsβ
Simple restrictionβ
With the builder, you can add simple restrictions when building the SQL query using the where method.
$users = app_db_table('users')->where('id', 1)->get();
$users = app_db_table('users')->where('id', '!=', 1)->get();
The OR clauseβ
You can chain restrictions by adding an or to your query. The orWhere method lets you do this:
$users = app_db_table('users')->where('id', 1)->orWhere('name', 'Papac')->get();
You can see the result of building the query with the toSql method.
$sql = app_db_table('users')->where('id', 1)->orWhere('id', 3)->toSql();
// => select * from users where id = ? or id = ?
toSql() returns the query with ? placeholders, not the literal
values β the query is executed as a prepared statement with parameter
binding, which protects against SQL injection.
whereRaw / orWhereRawβ
For conditions that the builder does not express natively, use the raw variants with parameter binding:
$users = app_db_table('users')
->whereRaw('LOWER(name) = ?', ['papac'])
->orWhereRaw('created_at >= NOW() - INTERVAL 7 DAY')
->get();
Additional clausesβ
whereNull / whereNotNullβ
The whereNull method checks that the value of the given column is NULL:
$users = app_db_table('users')->whereNull('name')->get();
The whereNotNull method checks that the column's value is not NULL:
$users = app_db_table('users')->whereNotNull('age')->get();
whereIn / whereNotInβ
The whereIn method checks that a given column's value is contained in the given array:
$users = app_db_table('users')->whereIn('age', [27, 30])->get();
The whereNotIn method checks that the given column's value is not contained in the given array:
$users = app_db_table('users')->whereNotIn('age', [27, 30])->get();
whereBetween / whereNotBetweenβ
The whereBetween method checks that a column's value is between two values:
$users = app_db_table('users')->whereBetween('votes', [1, 100])->get();
The whereNotBetween method checks that a column's value falls outside two values:
$users = app_db_table('users')->whereNotBetween('votes', [1, 100])->get();
Joinsβ
// INNER JOIN
$rows = app_db_table('orders')
->join('users', 'orders.user_id', '=', 'users.id')
->get();
// LEFT JOIN
$rows = app_db_table('orders')
->leftJoin('users', 'orders.user_id', '=', 'users.id')
->get();
// RIGHT JOIN
$rows = app_db_table('orders')
->rightJoin('users', 'orders.user_id', '=', 'users.id')
->get();
For joins with multiple conditions, chain andOn / orOn after
a join:
$rows = app_db_table('orders')
->join('users', 'orders.user_id', '=', 'users.id')
->andOn('orders.team_id', '=', 'users.team_id')
->get();
Ordering, grouping, and limitingβ
orderByβ
The orderBy method lets you sort the query result based on a given column. The first argument to the orderBy method should be the column you want to sort by, while the second argument controls the sort direction and can be asc or desc:
$users = app_db_table('users')->orderBy('name', 'desc')->get();
groupBy and havingβ
The groupBy and having methods can be used to group the query results. The signature of the having method is similar to that of the where method:
$users = app_db_table('orders')
->groupBy('price')
->having('price', '>', 100)
->get();
jump and takeβ
To limit the number of results returned by the query or to skip a given number of results in the query, you can use the jump (to skip) and take (to return a number) methods:
use Bow\Database\Database;
$users = Database::table('users')->jump(10)->take(5)->get();
Aggregatesβ
The query builder also provides a variety of aggregation methods such as count, max, min, avg, and sum.
You can call any of these methods after building your query.
$users = app_db_table('users')->count();
$price = app_db_table('orders')->max('price');
$avg = app_db_table('orders')->avg('price');
Checking whether records existβ
Instead of using the count method to determine whether any records match your query's constraints, you can use the exists method:
$exists = app_db_table('users')->where('id', 1)->exists();
Specifying a select clauseβ
Of course, you may not always want to select all the columns of a database table. Using the select method, you can specify a custom select clause for the query:
$price = app_db_table('orders')->select('price')->get();
// Or select multiple columns
$price = app_db_table('orders')->select(['id', 'price'])->get();
Inserting informationβ
The query builder provides an insert method to insert records into the database table.
The insert method accepts an array of column names and values:
app_db_table('users')->insert(
['email' => 'exemple@gmail.com', 'age' => 27]
);
You can insert multiple records in a single call by passing an array of arrays:
app_db_table('users')->insert([
['email' => 'a@example.com', 'age' => 27],
['email' => 'b@example.com', 'age' => 31],
['email' => 'c@example.com', 'age' => 24],
]);
Retrieving the last inserted IDβ
To insert a record and retrieve the auto-incremented value of the new record
in a single operation, use insertAndGetLastId:
$id = app_db_table('users')->insertAndGetLastId(
['email' => 'exemple@gmail.com', 'age' => 27]
);
Updatingβ
Of course, in addition to inserting records into the database, the query builder can also update existing records using the update method. The update method, like the insert method, accepts an array of column-and-value pairs containing the columns to update. You can constrain the update query using where clauses:
app_db_table('users')->where('id', 1)->update(
['email' => 'exemple@gmail.com', 'age' => 27]
);
Deleting a recordβ
The query builder can also be used to delete records from the table via the delete method.
You can constrain delete statements by adding where clauses before calling the delete method:
app_db_table('users')->delete();
app_db_table('users')->where('age', '>', 27)->delete();
If you want to empty the entire table, which will delete all rows and reset the auto-increment ID to zero, you can use the truncate method:
app_db_table('pets')->truncate();
Increment / decrementβ
The increment and decrement methods atomically modify the value
of a numeric column without loading the row first. The step ($step) is
optional and defaults to 1:
// votes = votes + 1
app_db_table('users')->where('id', 1)->increment('votes');
// stock = stock - 3
app_db_table('products')->where('id', 42)->decrement('stock', 3);
DISTINCTβ
To return only the distinct values of a column:
$categories = app_db_table('products')->distinct('category')->get();
Row lockingβ
Within a transaction, you can add a lock to prevent concurrent modifications:
use Bow\Database\Database;
Database::transaction(function () {
// Exclusive lock (SELECT ... FOR UPDATE)
$user = app_db_table('users')->where('id', 1)->lockForUpdate()->first();
// Shared lock (SELECT ... LOCK IN SHARE MODE)
$order = app_db_table('orders')->where('id', 42)->sharedLock()->first();
// ... operations on the locked rows
});
Is something missing?
If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.