Cookie settings

I use cookies

Necessary cookies keep the website working and store your choice. With your consent, I can also use functional cookies to remember theme and interactive grid preferences, and analytics cookies to measure website usage.

You can accept optional cookies all at once, reject them, or choose individual categories. You can change your consent later; details are available in the Cookies Policy.

PavelZanek.com
article Article

June 24, 2023

Laravel CRUD

Learn how to implement the basic data management operations – Create, Read, Update and Delete – in the Laravel framework and how to efficiently link them with the database and user interface.

Laravel CRUD

What is CRUD?

CRUD is a fundamental concept that is necessary for managing data in any web application. CRUD is an acronym for Create, Read, Update, and Delete. These four operations form the basic functions needed to interact with a database.

In Laravel, working with CRUD is made easier thanks to its ORM (Object-Relational Mapping) called Eloquent. Eloquent allows developers to work with database tables as objects and provides a simple interface (you could say API) for common database operations.

Creating (Create) in Laravel is done using the create() method, which creates a new record in the database. Reading (Read) is done using methods like get(), all(), find(), etc., which load records from the database. Updating (Update) is done using the update() method, which modifies an existing record in the database. And finally, deleting (Delete) is done using the delete() method, which removes a record from the database.

Introduction to our demonstration

Imagine that we want to create an ExampleItem model, which will contain these attributes:

  • title (string)
  • body (string / text)
  • is_active (boolean)
  • type (string)

Prerequisite is to have the Laravel framework installed. Since I tested this code on an already modified application, you will also find the localization of the application and the restriction of access to only users with the superadmin role.

The file structure is adapted for the demonstration, so in addition to the file names, I also specify the folder in which the file is located. However, the usage is entirely up to you, you can adjust the structure according to your needs.

The Pest package is then used as part of the testing.

Creating a migration

Migrations in Laravel are like a versioning system for your database. They allow you to change the database structure using PHP code instead of manually writing SQL. Migrations are also a great tool for sharing and updating the database structure among team members.

Creating a migration in Laravel is simple thanks to the Artisan command. The "php artisan make:migration" command creates a new migration file in the "database/migrations" directory. The file name contains the date and time of creation, which allows Laravel to determine in what order the migrations should be run.

Each migration file contains two methods: "up" and "down". The "up" method is used to add new tables, columns, or indexes to your database. The "down" method should perform the opposite operations to those performed in the "up" method.

After creating the migration, you can run the migration using the "php artisan migrate" command. This command runs all migrations that have not yet been run in the order they were created.

Migration (database/migrations):

<?php

use App\Enums\Examples\ExampleItemType;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('example_items', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->mediumText('body')->nullable();
            $table->boolean('is_active')->default(false);
            $table->string('type', 8)->default(ExampleItemType::TYPE1->value);
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('example_items');
    }
};

Creating a model

Models are one of the key aspects of the framework. They are representations of database tables and allow you to work with database records as objects. This means that you can perform CRUD operations on your models without having to write SQL.

Creating a model in Laravel is simple. You use the Artisan command "php artisan make:model", which creates a new model in the "app/Models" directory. The model name should be in singular and start with a capital letter.

Each model in Laravel extends the "Illuminate\\Database\\Eloquent\\Model" class and contains methods that allow interaction with the database. You can also define your own methods and properties on your models.

Models also contain so-called Eloquent relationships, which allow you to define relationships between different models. For example, if you have a "User" model and a "Post" model, you can define a relationship that says that one user can have many posts.

"ExampleItem" Model (app\\Models\\Examples):

<?php

namespace App\Models\Examples;

use App\Enums\Examples\ExampleItemType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class ExampleItem extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $fillable = [
        'title',
        'body',
        'is_active',
        'type',
    ];

    protected $casts = [
        'is_active' => 'boolean',
        'type' => ExampleItemType::class,
    ];
}

Creating actions

In Laravel, you can define actions as separate classes that represent individual operations that your application can perform. This is an alternative approach to the traditional model where actions are defined as methods in controllers.

Creating an action as a separate file in Laravel is simple. First, you need to create a new class in the "app/Actions" directory. The class, for example, can have an "execute()" method that performs the desired operation.

This approach has several advantages. One of them is that your actions are well isolated, easily extendable, and testable. Each action has a clearly defined responsibility and it's easy to test it independently of other parts of your application.

"CreateExampleItemAction" Action (app\\Actions\\Examples\\ExampleItems):

<?php

namespace App\Actions\Examples\ExampleItems;

use App\Models\Examples\ExampleItem;

class CreateExampleItemAction
{
    public function execute(array $validatedData): ExampleItem
    {
        return ExampleItem::create($validatedData);
    }
}

"UpdateExampleItemAction" Action (app\\Actions\\Examples\\ExampleItems):

<?php

namespace App\Actions\Examples\ExampleItems;

use App\Models\Examples\ExampleItem;

class UpdateExampleItemAction
{
    public function execute(ExampleItem $exampleItem, array $validatedData): ExampleItem
    {
        $exampleItem->update($validatedData);

        return $exampleItem;
    }
}

"RemoveExampleItemAction" Action (app\\Actions\\Examples\\ExampleItems):

<?php

namespace App\Actions\Examples\ExampleItems;

use App\Models\Examples\ExampleItem;

class RemoveExampleItemAction
{
    public function execute(ExampleItem $exampleItem): void
    {
        $exampleItem->delete();
    }
}

Creating enum

Creating Enums in Laravel is a powerful way to define a type of variable that allows for many different, but specific values. This feature is particularly useful when you want to limit the possibilities of a variable's value, ensuring that it only holds a value from a specific set. In Laravel, Enums are not built-in, but they can be easily implemented using PHP classes.

Creating Enums in Laravel can be a great way to make your code more readable and maintainable, especially when dealing with variables that should only have a specific set of values. They can be used in various parts of your Laravel application, such as in models, controllers, and even in your database migrations.

In addition to making your code cleaner and easier to understand, Enums also help to reduce errors in your code. By limiting the possible values of a variable, you can ensure that it only ever holds a value that it's supposed to, reducing the likelihood of unexpected values causing bugs in your code.

Remember, Enums are just one of the many advanced features that Laravel offers to make your PHP development easier and more efficient. Whether you're building a small personal project or a large enterprise application, Laravel has the tools and features you need to build robust, scalable, and maintainable PHP applications.

"ExampleItemType" Enum (app\\Enums\\Examples):

<?php

namespace App\Enums\Examples;

use Illuminate\Support\Collection;

enum ExampleItemType: string
{
    case TYPE1 = 'Type 1';
    case TYPE2 = 'Type 2';

    public static function all(): Collection
    {
        return collect(ExampleItemType::cases())->map(
            fn(ExampleItemType $code) => $code->details()
        );
    }

    public function details(): array
    {
        return match($this) 
        {
            self::TYPE1 => [
                'name'  => 'Type Name 1',
                'value' => 'Type 1',
            ],

            self::TYPE2 => [
                'name'  => 'Type Name 2',
                'value' => 'Type 2',
            ],
        };
    }
}

Creating validation / validation requests

Validation requests are special classes that serve to validate input data before they are processed by your application. Using validation requests, you can ensure that the data your application receives is always in the expected format and meets all the rules you have set.

Creating a validation request in Laravel is simple. First, you need to create a validation class using the Artisan command "php artisan make:request", which creates a new validation request in the "app/Http/Requests" directory.

The validation class contains two methods: "authorize()" and "rules()". The "authorize()" method determines whether the user is authorized to make the request. The "rules()" method then returns an array of validation rules that should be applied to the input data.

"StoreXYZRequest" and "UpdateXYZRequest" are two common types of validation requests in Laravel. "StoreXYZRequest" is used when creating a new record and "UpdateXYZRequest" is used when updating an existing record. However, you can, for example, create only one validation request that will be used when creating and editing a record - which is handy if the validation conditions for creating and editing are the same.

"StoreExampleItemRequest" Validation Request (app\\Http\\Requests\\Examples\\ExampleItems):

<?php

namespace App\Http\Requests\Examples\ExampleItems;

use App\Enums\Examples\ExampleItemType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;

class StoreExampleItemRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body' => 'nullable|string',
            'is_active' => 'required|boolean',
            'type' => [
                'required',
                'string',
                'max:8',
                new Enum(ExampleItemType::class),
            ],
        ];
    }

    protected function prepareForValidation()
    {
        $this->merge([
            'is_active' => $this->has('is_active') ? true : false,
        ]);
    }
}

"UpdateExampleItemRequest" Validation Request (app\\Http\\Requests\\Examples\\ExampleItems):

<?php

namespace App\Http\Requests\Examples\ExampleItems;

use App\Enums\Examples\ExampleItemType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;

class UpdateExampleItemRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body' => 'nullable|string',
            'is_active' => 'required|boolean',
            'type' => [
                'required',
                'string',
                'max:8',
                new Enum(ExampleItemType::class),
            ],
        ];
    }

    protected function prepareForValidation()
    {
        $this->merge([
            'is_active' => $this->has('is_active') ? true : false,
        ]);
    }
}

Creating a controller

Controllers are a key part of any application built on this framework. They are classes that handle all HTTP requests that your application receives and control how your application should respond.

Creating a controller in Laravel is simple again thanks to the Artisan tool, which is part of Laravel. The "php artisan make:controller" command creates a new controller in the "app/Http/Controllers" directory. The controller name should be in singular and start with a capital letter.

Controllers in Laravel can either be simple, which are controllers with one action (uses the "__invoke" method), or resource, which are controllers that handle a whole set of CRUD operations. Resource controllers are ideal for working with database records, such as users, blog posts, or any other entities in your application. Of course, a controller can also be created according to your ideas, so it does not have to be either with one method, nor a resource one.

"ExampleItemController" Controller (app\\Http\\Controllers\\Examples):

<?php

namespace App\Http\Controllers\Examples;

use App\Actions\Examples\ExampleItems\CreateExampleItemAction;
use App\Actions\Examples\ExampleItems\RemoveExampleItemAction;
use App\Actions\Examples\ExampleItems\UpdateExampleItemAction;
use App\Enums\Examples\ExampleItemType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Examples\ExampleItems\StoreExampleItemRequest;
use App\Http\Requests\Examples\ExampleItems\UpdateExampleItemRequest;
use App\Models\Examples\ExampleItem;

class ExampleItemController extends Controller
{
    public function index()
    {
        $exampleItems = ExampleItem::orderBy('created_at', 'desc')->paginate(20);
        return view('examples.example-items.index', ['exampleItems' => $exampleItems]);
    }

    public function create()
    {
        return view('examples.example-items.create', ['exampleItemTypes' => ExampleItemType::all()]);
    }

    public function store(StoreExampleItemRequest $request)
    {
        (new CreateExampleItemAction())->execute($request->validated());
        return redirect()->route('example-items.index')->with(['flashType' => 'success', 'flashMessage' => __('Example Item was successfully created.')]);
    }

    public function show(ExampleItem $exampleItem)
    {
        abort_if(!$exampleItem->is_active, 404, "Example Item is not active.");
        return view('examples.example-items.show', ['exampleItem' => $exampleItem]);
    }

    public function edit(ExampleItem $exampleItem)
    {
        return view('examples.example-items.edit', ['exampleItem' => $exampleItem, 'exampleItemTypes' => ExampleItemType::all()]);
    }

    public function update(UpdateExampleItemRequest $request, ExampleItem $exampleItem)
    {
        (new UpdateExampleItemAction())->execute($exampleItem, $request->validated());
        return redirect()->route('example-items.index')->with(['flashType' => 'success', 'flashMessage' => __('Example Item was successfully updated.')]);
    }

    public function destroy(ExampleItem $exampleItem)
    {
        (new RemoveExampleItemAction())->execute($exampleItem);
        return redirect()->route('example-items.index')->with(['flashType' => 'success', 'flashMessage' => __('Example Item was successfully removed.')]);
    }
}

Creating a template

Templates are a key tool for creating dynamic web pages. Laravel uses the "Blade" template engine, which allows you to insert PHP code directly into HTML templates.

Creating a template in Laravel is simple. First, you need to create a template file in the "resources/views" directory. Template files have a ".blade.php" extension.

Templates can contain any valid HTML code as well as special Blade directives for inserting PHP code. For example, you can use the directive "{{ $variable }}" to output the value of a variable, or directives @if, @foreach, and others to control the flow of the application.

In addition, you can use the template inheritance system, which allows you to define "base" templates with a general page structure and then create "inherited" templates that extend this structure and add specific content.

[Index, Create, Edit, Show Blade templates as fully fetched above]

Routing

Routing is one of the most important aspects of any web application, and Laravel provides a powerful and flexible routing system that makes it easy to define routes for your application.

Routes in Laravel are defined in route files, which are located in the "routes" directory of your application. Laravel provides several different route files for different types of routes, including web routes, API routes, and console routes.

Each route is defined using an HTTP method (such as GET, POST, PUT, or DELETE), a path, and an action to be performed when the route is invoked. The action can be either a function (closure function), or a reference to a controller method.

Laravel also supports advanced routing features such as wildcard routing, named routing, middleware routing, parameter constraint routing, and so on.

Routing for "web" (routes):

...
Route::group([
    'prefix' => "{language}", 
    'where' => ['language' => '[a-zA-Z]{2}']
    ], function () {

    // Admin
    Route::group(['middleware' => ['role:superadmin']], function () {
        ...
        Route::resource('/examples/example-items', ExampleItemController::class);
    });

});
...

Creating a factory

Factories are tools that make it easy to generate test data for your database. Factories allow you to define "templates" (not to be confused with blade templates) for creating new instances of your models with random or predefined data.

Creating a factory in Laravel is simple again thanks to Artisan. The "php artisan make:factory" command creates a new factory in the "database/factories" directory. The name of the factory should correspond to the name of the model for which the factory is intended.

Each factory contains a "definition()" method, which returns an array of attributes to be used when creating a new instance of the model. You can use Laravel functions like "faker" to generate random data.

Factories are ideal for creating large amounts of data for testing or for populating your database before development or during development.

"ExampleItemFactory" Factory (database/factories/Examples):

<?php

namespace Database\Factories\Examples;

use App\Enums\Examples\ExampleItemType;
use App\Models\Examples\ExampleItem;
use Illuminate\Database\Eloquent\Factories\Factory;

class ExampleItemFactory extends Factory
{
    protected $model = ExampleItem::class;

    public function definition()
    {
        return [
            'title' => $this->faker->sentence(4),
            'body' => $this->faker->realText(500),
            'is_active' => $this->faker->boolean(),
            'type' => $this->faker->boolean() ? ExampleItemType::TYPE1 : ExampleItemType::TYPE2,
        ];
    }
}

Creating tests

Testing is a fundamental part of developing any application, and Laravel provides excellent tools for automated testing of your code. Laravel allows you to easily create and run tests for your application.

Creating a test in Laravel is simple, as you might expect, thanks to the Artisan tool. The "php artisan make:test" command creates a new test in the "tests/Feature" or "tests/Unit" directory, depending on what type of test you want to create.

Each test is a class that contains one or more test methods. These methods contain assertions that verify that your code is working as it should. Laravel provides a wide range of assertions to verify various aspects of your application, including HTTP responses, database interactions, and much more.

You can run tests using the "php artisan test" command, which runs all tests in your application and prints the results to the console.

Tests for the controller

"ExampleItemControllerTest" Test (tests/Feature/App/Controllers/Examples):

[full Pest controller test code as fetched above]

Tests for actions

"CreateExampleItemActionTest", "UpdateExampleItemActionTest", "RemoveExampleItemActionTest" Tests (tests/Feature/App/Actions/Examples/ExampleItems):

[full Pest action test code as fetched above]

Conclusion

In this tutorial, we have thoroughly familiarized ourselves with the process of creating CRUD operations in Laravel. We have gone through all the key steps, from creating migrations and models, through defining actions and validation requests, to creating templates, factories, and tests. I hope this tutorial has helped you understand how to work with Laravel and has provided you with a solid foundation for further development of your own applications.

Categories

alternate_email

Let's stay in touch

Subscribe to get the latest Laravel and infrastructure insights straight to your inbox.