Laravel Models 101: A Beginner's Guide

Laravel Models 101: A Beginner's Guide

Laravel models are the backbone of its robust MVC (Model-View-Controller) framework. They represent the data layer in your application, encapsulating the logic for interacting with the database. In this guide, we will walk through the basics of Laravel models, focusing on how to use them effectively, from defining models and relationships to using Eloquent, Laravel's powerful ORM (Object-Relational Mapping).


1. What is a Laravel Model?

In Laravel, a model is a class that represents a table in your database. It allows you to interact with data, define relationships between different tables, and perform CRUD (Create, Read, Update, Delete) operations without writing complex SQL queries.

At the core, models in Laravel use Eloquent, Laravel's built-in ORM, which provides an expressive syntax to interact with databases.


2. Creating Models in Laravel

Laravel offers an Artisan command to quickly generate models. To create a model, use the following command:

php artisan make:model Product

This will generate a model file in the app/Models directory:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    // Your model logic here
}

With Migration:

You can also create a model with a corresponding migration using the -m flag:

php artisan make:model Product -m

This will create both the model and a migration file where you can define your database schema.


3. Basic Model Properties

Laravel models come with some default properties and behaviors. Here are a few key properties you should know:

  • $table: This property specifies the database table that the model is associated with. By default, Laravel assumes the table name is the plural form of the model name. If you want to specify a different table, you can override this:

      protected $table = 'products';
    
  • $primaryKey: By default, Laravel assumes the primary key column is named id. If your primary key has a different name, you can specify it:

      protected $primaryKey = 'product_id';
    
  • $timestamps: Laravel automatically maintains created_at and updated_at fields for models. If you don't want this behavior, set the $timestamps property to false:

      public $timestamps = false;
    
  • $connection: If your model needs to connect to a different database than the default one, you can specify the connection:

      protected $connection = 'mysql2';
    

4. Eloquent ORM: Querying and Manipulating Data

Laravel's Eloquent ORM allows you to interact with the database using simple and expressive syntax. Here are some common operations:

Retrieving Data

  • Retrieve all records:

      $products = Product::all();
    
  • Find a record by primary key:

      $product = Product::find(1);
    
  • Retrieve the first record that matches a condition:

      $product = Product::where('name', 'Laptop')->first();
    

Inserting Data

To create a new record in the database, you can either use the create method or manually assign values and save:

$product = new Product();
$product->name = 'Laptop';
$product->price = 999.99;
$product->save();

Alternatively, using the create method with mass assignment:

Product::create([
    'name' => 'Laptop',
    'price' => 999.99,
]);

(Note: Be sure to define the $fillable property for mass assignment, as explained later.)

Updating Data

To update a model:

$product = Product::find(1);
$product->price = 899.99;
$product->save();

Deleting Data

To delete a model:

$product = Product::find(1);
$product->delete();

Or, delete by a condition:

Product::where('price', '>', 1000)->delete();

5. Defining Relationships in Models

Eloquent makes defining relationships between different models easy. Relationships are crucial when you have data that connects tables (e.g., users and posts).

Here are the types of relationships you can define in your models:

One-to-One

A one-to-one relationship means that one model is related to exactly one other model:

class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

One-to-Many

A one-to-many relationship means that one model can have multiple related models:

class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Many-to-Many

A many-to-many relationship means that one model can belong to many other models and vice versa:

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

6. Mass Assignment and Fillable Properties

Mass assignment allows you to easily insert multiple values into a model. However, for security purposes, Laravel requires you to define the $fillable property to specify which fields can be mass assigned.

class Product extends Model
{
    protected $fillable = ['name', 'price'];
}

If you don’t specify $fillable, Laravel will throw a MassAssignmentException when trying to mass assign attributes.


7. Model Events and Observers

In Laravel 10+, a new way to register model observers was introduced through the #[ObservedBy] attribute. This feature simplifies how we attach observers to Eloquent models, eliminating the need to manually register observers within a service provider like AppServiceProvider. Instead, you can directly link an observer class to a model using the ObservedBy attribute on the model itself.

Understanding the #[ObservedBy] Attribute

The #[ObservedBy] attribute is a PHP 8+ attribute that you can attach to a model class. It makes the connection between a model and its observer explicit and more readable. Laravel automatically detects and registers the observer when the application is booted, without needing to manually declare it in the service provider.

Example of Using the ObservedBy Attribute

Step 1: Create an Observer

First, create an observer using the Artisan command:

php artisan make:observer ProductObserver --model=Product

This will generate a new observer class in the app/Observers directory. The observer can contain methods for handling various model events such as created, updated, deleted, etc.

namespace App\Observers;

use App\Models\Product;

class ProductObserver
{
    public function created(Product $product)
    {
        // Logic for when a product is created
    }

    public function updated(Product $product)
    {
        // Logic for when a product is updated
    }

    public function deleted(Product $product)
    {
        // Logic for when a product is deleted
    }
}

Step 2: Attach the #[ObservedBy] Attribute to the Model

Next, apply the #[ObservedBy] attribute to the Product model to link it with the observer.

namespace App\Models;

use App\Observers\ProductObserver;
use Illuminate\Database\Eloquent\Model;

#[ObservedBy(ProductObserver::class)]
class Product extends Model
{
    // Your model logic here
}

By adding the #[ObservedBy] attribute, Laravel automatically registers the ProductObserver for the Product model, making it unnecessary to manually register the observer in a service provider.

No Need to Register in AppServiceProvider

Previously, observers had to be registered manually in a service provider like this:

use App\Models\Product;
use App\Observers\ProductObserver;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Product::observe(ProductObserver::class);
    }
}

With the #[ObservedBy] attribute, this step is no longer required. Laravel handles it automatically during the model’s lifecycle.

Benefits of the #[ObservedBy] Attribute

  • Improved Readability: The observer is explicitly associated with the model, making the code easier to understand. You can see the observer relationship directly in the model, which improves code navigation and readability.

  • No Service Provider Boilerplate: You avoid the need for boilerplate code in the AppServiceProvider, reducing the setup and maintenance effort.

  • Centralized Logic: All the logic related to the model’s behavior (including observers) is consolidated within the model class.

Example Scenario

Let’s say you have a User model, and every time a new user is created, you want to send a welcome email. Using the new #[ObservedBy] attribute, here’s how you would set it up:

Step 1: Create the Observer

php artisan make:observer UserObserver --model=User

In the generated observer:

namespace App\Observers;

use App\Models\User;
use Illuminate\Support\Facades\Mail;

class UserObserver
{
    public function created(User $user)
    {
        Mail::to($user->email)->send(new WelcomeEmail($user));
    }
}

Step 2: Attach the #[ObservedBy] Attribute to the User Model

namespace App\Models;

use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Model;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
    // Your model logic here
}

Now, whenever a new user is created, the UserObserver will automatically send a welcome email.


8. Eloquent Accessors and Mutators

Accessors and mutators allow you to modify data before it is retrieved or saved to the database.

Accessor

An accessor allows you to modify how a field is retrieved:

class Product extends Model
{
    public function getPriceAttribute($value)
    {
        return '$' . number_format($value, 2);
    }
}

Now, when you retrieve the price field, it will be formatted with a dollar sign.

Mutator

A mutator allows you to modify how a field is saved to the database:

class Product extends Model
{
    public function setPriceAttribute($value)
    {
        $this->attributes['price'] = $value * 100; // Store price as cents
    }
}

9. Timestamps and Soft Deletes

Laravel automatically handles timestamps on models using the created_at and updated_at fields. These are updated whenever a record is created or modified.

Disabling Timestamps

If your table doesn’t have these columns, you can disable them:

public $timestamps = false;

Soft Deletes

Soft deletes allow you to "delete" a record without removing it from the database. Instead, the record’s deleted_at field is updated.

To enable soft deletes, add the SoftDeletes trait to your model:

use Illuminate\Database\Eloquent\SoftDeletes;

class Product extends Model
{
    use SoftDeletes;
}

Make sure to add the deleted_at column to your migration:

$table->softDeletes();

Now, when you delete a record, it will remain in the database but won’t be retrieved in queries unless explicitly included.


10. Conclusion

Laravel models are the key to interacting with your database using clean, readable code. Eloquent ORM makes it easy to query and manipulate data, while also offering advanced features like relationships, accessors, mutators, and soft deletes. By understanding these fundamental concepts, you can build efficient and maintainable data-driven applications.

This concludes our Laravel Models 101 guide. With these basics under your belt, you can start building sophisticated applications that manage complex data with ease.

Happy coding!