Understanding ORMs
August 04, 2024When developing applications that interact with databases, one of the challenges developers face is mapping the object-oriented programming (OOP) paradigm with relational database systems. This is where Object-Relational Mapping (ORM) comes into play. In this post, we’ll delve into what an ORM is, its benefits, and how it works, with examples using Laravel’s Eloquent ORM.
What is an ORM?
ORM, or Object-Relational Mapping, is a technique that allows developers to interact with a database using an object-oriented programming language. Instead of writing raw SQL queries to perform CRUD (Create, Read, Update, Delete) operations, developers can use ORM to interact with the database using the programming language’s syntax and constructs.
In essence, ORM acts as a bridge between the application and the database, mapping database tables to classes and records to objects.
How Does ORM Work?
ORM works by defining classes in the programming language that represent tables in the database. Each instance of a class corresponds to a row in the database table. Attributes of the class map to columns in the table, and methods on the class allow for database operations.
For example, consider a simple users
table in a database:
| id | name | email |
| --- | --------- | ----------------- |
| 1 | John Doe | [email protected] |
| 2 | Jane Doe | [email protected] |
Using an ORM, you could represent this table with a User
class:
class User {
public $id;
public $name;
public $email;
}
With an ORM, you can perform database operations using methods on this class instead of writing raw SQL queries.
Benefits of Using an ORM
Simplicity: ORM provides an easy-to-use, object-oriented API for interacting with the database, reducing the need for complex SQL queries.
Database Abstraction: ORMs abstract the database system, allowing you to switch between different databases without changing your code.
Security: ORMs help prevent SQL injection attacks by using parameterized queries.
Productivity: With ORM, developers can write less boilerplate code and focus on business logic, enhancing productivity.
Maintainability: Code is easier to maintain and understand, as ORM often follows object-oriented principles and patterns.
Basic CRUD Operations with Eloquent
Let’s explore some basic CRUD operations using Eloquent to illustrate how ORMs work in practice.
Setting Up Eloquent
To get started with Eloquent, you need to define a model for your database table. Each model corresponds to a table in your database.
Consider the following users
table:
| id | name | email |
| --- | --------- | ----------------- |
| 1 | John Doe | [email protected] |
| 2 | Jane Doe | [email protected] |
To work with this table using Eloquent, create a User
model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// You can specify the table name if it's different from the plural of the class name
// protected $table = 'users';
// Specify the attributes that are mass assignable
protected $fillable = ['name', 'email'];
}
Creating Records
You can create a new record in the database using the create
method:
use App\Models\User;
// Creating a new user
$user = User::create([
'name' => 'Alice Smith',
'email' => '[email protected]'
]);
Eloquent will automatically insert a new row into the users
table.
Alternatively, you can use the save
method:
$user = new User();
$user->name = 'Bob Johnson';
$user->email = '[email protected]';
$user->save();
Reading Records
To retrieve records from the database, you can use Eloquent’s fluent query builder:
Retrieving All Records
$users = User::all();
foreach ($users as $user) {
echo $user->name . ' - ' . $user->email;
}
Retrieving a Single Record
You can retrieve a single record using the find
method or the where
method:
// Find by primary key
$user = User::find(1);
// Find by specific attribute
$user = User::where('email', '[email protected]')->first();
echo $user->name . ' - ' . $user->email;
Updating Records
Updating records is straightforward with Eloquent. You can use the update
method on a model instance:
$user = User::find(1);
$user->name = 'Johnathan Doe';
$user->save();
Or you can use the update
method on the query builder:
User::where('email', '[email protected]')->update(['name' => 'Jane Smith']);
Deleting Records
Deleting records can be done using the delete
method:
// Delete a specific user
$user = User::find(1);
$user->delete();
// Delete using query builder
User::where('name', 'Bob Johnson')->delete();
Advanced Features of Eloquent
Eloquent offers several advanced features that make working with databases even more powerful and intuitive:
Relationships
Eloquent provides support for defining relationships between models, such as:
- One-to-One: A user has one profile.
- One-to-Many: A post has many comments.
- Many-to-Many: A user belongs to many roles.
Here’s an example of a one-to-many relationship:
// User model
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// Post model
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
You can access related models using dynamic properties:
$user = User::find(1);
foreach ($user->posts as $post) {
echo $post->title;
}
Eager Loading
Eloquent supports eager loading, which allows you to load related models efficiently:
// Load users and their posts in a single query
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->name . ' has ' . $user->posts->count() . ' posts.';
}
Query Scopes
Eloquent provides query scopes to define common query logic that can be reused. There are two types of scopes: global and local.
Local Scopes
Local scopes allow you to define common query conditions inside your model:
class User extends Model
{
public function scopeActive($query)
{
return $query->where('active', 1);
}
}
// Usage of local scope
$activeUsers = User::active()->get();
Global Scopes
Global scopes apply conditions to all queries for a particular model:
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
protected static function booted()
{
static::addGlobalScope('active', function (Builder $builder) {
$builder->where('active', 1);
});
}
}
Accessors and Mutators
Accessors and mutators allow you to manipulate data before it’s retrieved or saved to the database.
Accessors
Accessors format attributes when retrieving them from the database:
class User extends Model
{
public function getFullNameAttribute()
{
return $this->name . ' (' . $this->email . ')';
}
}
// Usage
$user = User::find(1);
echo $user->full_name; // Outputs: John Doe ([email protected])
Mutators
Mutators modify attribute values before they are saved to the database:
class User extends Model
{
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
}
// Setting the password
$user = User::find(1);
$user->password = 'newpassword123';
$user->save();