Published 7 Sep 2023
Business
Any application you use these days often interacts with a database. A database is at the core of a large-scale application. To store large amounts of data and retrieve them efficiently, speed matters, especially in these times when things have to be done quickly.
Laravel makes interacting with databases extremely simple across a variety of supported databases using raw SQL, a fluent query builder, and the Eloquent ORM. Currently, Laravel provides first-party support for five databases:
Raw SQL, Query Builders and ORMs are the three common approaches for accessing data from a database. This blog will cover the three approaches along with the advantages and disadvantages of each approach. Here is a tabular view of the strengths and weaknesses of each approach.
The more advanced a query, the better it is to use fewer abstractions, with SQL having no abstraction, Query Builder having some level of abstraction and ORMs having the highest level of abstraction.
As the name suggests, we directly interact with the database using SQL queries. Let's take a look at the advantages and disadvantages:
Let's check out some queries using raw SQL.
Select statements are used to select/retrieve data from the database. Let's take an example where we have the following tables - orders,order_details,products,and customers and perform some queries on them.
CREATE TABLE order_details (
order_detail_id INT PRIMARY KEY,
order_id INT,
product_id INT,
quantity INT,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(255),
unit_price DECIMAL(10, 2),
stock_quantity INT
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
);
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(255),
);
Let's consider an example:
We need to get a list of products along with their total sales, the total sales being in descending order. We have tables containing the data of orders with the name order_details and tables containing products with the name products.
Let's break it into smaller parts. Then this would be the list of tasks we need to perform in order:
WITH Sales AS (
SELECT product_id, SUM(quantity) AS total_sales
FROM order_details
GROUP BY product_id
)
SELECT products.product_name, Sales.total_sales
FROM products
JOIN Sales ON products.product_id = Sales.product_id
ORDER BY Sales.total_sales DESC;
Let's see another query:
We need to retrieve a list of orders along with the names of the customers who placed them and the names of the products that were ordered. The orders should have an order date after January 1, 2022.
Again let's break it into smaller parts. Then this would be the list of tasks we need to perform in order:
SELECT
orders.order_id,
customers.customer_name,
products.product_name
FROM
orders
JOIN customers ON orders.customer_id = customers.customer_id
JOIN order_details ON orders.order_id = order_details.order_id
JOIN products ON order_details.product_id = products.product_id
Now add the where condition to get the orders that have an order date after January 1, 2022.
SELECT
orders.order_id,
customers.customer_name,
products.product_name
FROM
orders
JOIN customers ON orders.customer_id = customers.customer_id
JOIN order_details ON orders.order_id = order_details.order_id
JOIN products ON order_details.product_id = products.product_id
WHERE
orders.order_date > '2022-01-01';
These queries are used to insert the data into the table. The insert queries can be combined with the select queries to select data from another table and insert it into the table required.
Let's consider a situation where you have a users table, but you want to create another table for premium users in the users table called premium_users and insert the premium users' records into that table.
INSERT INTO premium_users(user_id,first_name,last_name)
SELECT user_id,first_name,last_name
FROM users
WHERE users.status = 'premium';
Let's look at another example:
We need to insert data into the clients table based on data from the suppliers table and ensure that only new clients (those that don't already exist in the clients table) are inserted.
We need to select the data from the suppliers table and then apply an additional condition that prevents from selecting the clients that already exist, for this we will use WHERE NOT EXISTS clause.
INSERT INTO clients
(client_id, client_name, client_type)
SELECT supplier_id AS client_id,
supplier_name AS client_name,
'advertising' AS client_type
FROM suppliers
WHERE NOT EXISTS (SELECT * FROM clients
WHERE clients.client_id = suppliers.supplier_id);
In case you are wondering what a query builder is, it is an interface that allows developers to construct database queries more abstractly instead of using raw SQL statements. This makes it more readable and easy to interact with the database. Let's take a look at the documentation:
Laravel's database query builder provides a convenient, fluent interface for creating and running database queries. It can be used to perform most database operations in your application and works perfectly with all of Laravel's supported database systems.
It means that Laravel provides a fluent way to construct and execute database queries. It allows you to chain methods, and execute complex methods with ease and makes your code more readable and maintainable. One of the advantages of using Laravel's query builder is protection against SQL injections. For this Laravel uses the PDO parameter and it doesn't support binding column names, which means that you should never take column names as input from the user.
Let's consider an example:
The tables we have are orders,customers and products whose schema is given below.
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
);
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(255),
);
CREATE TABLE order_items (
id INT PRIMARY KEY,
order_id INT,
total DECIMAL(10, 2),
);
Let's consider the following query:
We need to retrieve information about the total sales for each customer.
Let's break it into smaller parts.
$orders = DB::table('orders')
->join('customers', 'orders.customer_id', '=', 'customers.id')
->join('order_items', 'orders.id', '=', 'order_items.order_id')
->groupBy('customers.name')
->select('customers.name',
DB::raw('SUM(order_items.total) as total_sales'))
->get();
Laravel's Query Builder provides an insert method that can be used to insert records into the table. The insertOrIgnore method will ignore errors while inserting records into the database. The insertUsing method will insert new records into the table while using a subquery to determine the data that should be inserted.
Let's take an example
Let's insert data from users table into another table called pruned_users given that the users updated_at column is less than or equal to one month ago.
We need to insert the values of id,name,email into the pruned_users from the users table. We will be using the insert_using method since we need to apply a condition to determine which data should be inserted into pruned_users table. This will be the subquery.
In the subquery, we need to select id,name,email from the users table with the condition that updated_at is less than a month, for which we will use where method.
DB::table('pruned_users')->insertUsing(
['id', 'name', 'email'],
DB::table('users')->select(
'id', 'name', 'email'
)->where('updated_at', '<=', now()->subMonth()));
Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well.
Complex relationships in Eloquent ORM can involve multiple levels of hasMany and belongsTo relationships between models.
Let's consider an example involving three related models: Author, Book, and Chapter. Each author can have multiple books, and each book can have multiple chapters. We'll set up these relationships to demonstrate complex relationships in Eloquent.
Author Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
protected $table = 'authors';
public function books()
{
return $this->hasMany(Book::class);
}
}
Book Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
protected $table = 'books';
public function author()
{
return $this->belongsTo(Author::class);
}
public function chapters()
{
return $this->hasMany(Chapter::class);
}
}
Chapter Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Chapter extends Model
{
protected $table = 'chapters';
public function book()
{
return $this->belongsTo(Book::class);
}
}
With these models, we have set up the following relationships:
Let's consider the following example:
We need to fetch the list of users along with their associated posts, including columns like name, email, title, and created_at.
Let's checkout User Model and Post Model. The User model should have hasMany relationship to show that a user has multiple posts.
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
The Post Model should have a belongsTo relationship to associate each post with a user.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Now, we need to select the user and post details after joining them on id column.
$usersWithPosts = User::select('users.id', 'users.email', 'posts.title', 'posts.created_at')
->join('posts', 'users.id', '=', 'posts.user_id')
->get();
If we want to create a new user, then the create method automatically creates a new record in the users table with the specified data and returns the created user model instance, which is then assigned to the variable $user.
$user = User::create([
'name' => 'Alice',
'email' => 'alice@example.com',
]);