Welcome to the next step in making your Laravel ToDo application more robust and enterprise-ready! In this lesson, you will learn how to integrate a database into your app, which will form the backbone for storing and managing data efficiently. Database integration is crucial for any modern web application, as it allows you to handle and manipulate data on a large scale.
Previously, you explored setting up a basic MVC structure within Laravel, which laid the foundation for our app. Now, let's move on to anchoring the application with a database to store your ToDo items persistently.
In this segment, you will learn how to connect a Laravel application to a database using configuration files. You'll also get an overview of setting up the database schema and modeling data with Laravel's Eloquent ORM.
First, let's remind ourselves of the .env
file used in Laravel for environment configuration. This is where we specify the database connection details. Here's an example of the database configuration in the .env
file:
Plain text1DB_CONNECTION=sqlite 2DB_HOST=127.0.0.1 3DB_PORT=3306 4DB_DATABASE=database_name 5DB_USERNAME=db_username 6DB_PASSWORD=db_password
Let's see what each of these configurations does:
DB_CONNECTION
: Specifies the type of database. Laravel supports multiple databases like MySQL, SQLite, and PostgreSQL. In this example, it’s set to SQLite, which is file-based and simpler for development.DB_HOST
andDB_PORT
: Define where the database server is running. For local development,127.0.0.1
(localhost) is often used, and3306
is the default port for MySQL. SQLite, however, doesn’t require a host or port since it’s a file-based database.DB_DATABASE
: The name of the database (or the path for SQLite). For MySQL, PostgreSQL, and other server-based databases, this is the database name. For SQLite, provide a file path likedatabase/database.sqlite
.DB_USERNAME
andDB_PASSWORD
: The username and password used to connect to the database. SQLite doesn’t use these credentials, but they are required for MySQL or PostgreSQL.
Note: Laravel’s .env
file is designed to support various database types, not just SQLite. Including settings like DB_HOST
and DB_PORT
allows you to switch between different database types (e.g., MySQL, PostgreSQL) by simply changing DB_CONNECTION
in the .env
file. This way, if you move from SQLite (for local development) to MySQL (for production), you won’t need to restructure the entire file—just update a few values. By default, the .env
template includes these database settings to maintain consistency across projects, regardless of the chosen database. While SQLite ignores host, port, username, and password settings, having these fields pre-defined in the .env
file provides flexibility, consistency, and prepares the application for easy database transitions.
To understand how Laravel interacts with the database, let's look at the database configuration file located at config/database.php
. This file contains the database connection settings for different environments, such as mysql
, sqlite
, pgsql
, and sqlsrv
.
Here's an example of the database configuration for SQLite:
php1'sqlite' => [ 2 'driver' => 'sqlite', 3 'url' => env('DATABASE_URL'), 4 'database' => env('DB_DATABASE', database_path('database.sqlite')), 5 'prefix' => '', 6 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 7],
In this configuration, the database
key specifies the path to the SQLite database file. By default, it's set to database.sqlite
in the database
directory. You can change this path to store the database file in a different location. We'll not modify this configuration in our practices, as the default settings are sufficient for our ToDo app.
Now, let's see how Laravel interacts with the database using Eloquent, the ORM provided by Laravel. Eloquent allows you to define models that represent database tables and interact with them using object-oriented syntax. Let's create a model for the Todo
table in our database:
php1namespace App\Models; 2use Illuminate\Database\Eloquent\Factories\HasFactory; 3 4class Todo extends Model 5{ 6 use HasFactory; 7 protected $fillable = ['title', 'description']; 8}
In this model, we define the Todo
class that extends the Model
class provided by Laravel.
The use HasFactory
statement enables the factory pattern for creating model instances. Factories are used to generate fake data for testing purposes.
The fillable
property specifies the columns that can be mass-assigned when creating a new Todo
instance - mass assignment is the process of assigning an array of attributes to a model, like the SQL INSERT
statement with multiple columns.
With the model in place, we can now interact with the Todo
table in the database using Eloquent. This interaction will happen in the service layer of our application, where we'll define methods to create, read, update, and delete Todo
items. Let's see how the service layer will interact with the database:
php1<?php 2 3namespace App\Services; 4 5use App\Models\Todo; 6use Illuminate\Support\Facades\DB; 7 8class TodoService 9{ 10 public function create($title, $description = null) 11 { 12 return Todo::create(compact('title', 'description')); 13 } 14 15 public function findAll() 16 { 17 return Todo::all(); 18 } 19 20 public function findOne($id) 21 { 22 return Todo::find($id); 23 } 24 25 public function update($id, $title, $description) 26 { 27 $todo = Todo::find($id); 28 if ($todo) { 29 $todo->update(compact('title', 'description')); 30 } 31 return $todo; 32 } 33 34 public function delete($id) 35 { 36 return Todo::destroy($id); 37 } 38}
In this service class, we define the following methods:
create
: Creates a newTodo
item in the database using theTodo::create
method with the provided title and description. Thecompact
function creates an array from the provided variables to pass as arguments to thecreate
method.findAll
: Retrieves allTodo
items from the database using theTodo::all
method.findOne
: Retrieves a singleTodo
item by its ID using theTodo::find
method with the provided ID.update
: Updates aTodo
item by its ID with the provided title and description. TheTodo::update
method is called on the retrievedTodo
instance with the new title and description.delete
: Deletes aTodo
item by its ID using theTodo::destroy
method.
Note, that the controller will not change much from the previous lesson, as it will still interact with the service layer to handle requests and responses. The service layer will handle the database interactions using the Eloquent model:
php1<?php 2 3namespace App\Http\Controllers; 4 5use App\Services\TodoService; 6use Illuminate\Http\Request; 7 8class TodoController extends Controller 9{ 10 protected $todoService; 11 12 public function __construct(TodoService $todoService) 13 { 14 $this->todoService = $todoService; 15 } 16 17 public function index() 18 { 19 $todos = $this->todoService->findAll(); 20 return view('todos.index', ['title' => 'ToDo List', 'todos' => $todos]); 21 } 22 23 public function show($id) 24 { 25 $todo = $this->todoService->findOne($id); 26 return view('todos.show', ['title' => 'Todo Details', 'todo' => $todo]); 27 } 28 29 public function store(Request $request) 30 { 31 $this->todoService->create($request->title, $request->description); 32 return redirect('/todos'); 33 } 34 35 public function update($id, Request $request) 36 { 37 $this->todoService->update($id, $request->title, $request->description); 38 return redirect('/todos'); 39 } 40 41 public function destroy($id) 42 { 43 $this->todoService->delete($id); 44 return redirect('/todos'); 45 } 46}
In this controller, we inject the TodoService
instance into the constructor to interact with the database. The controller methods call the corresponding methods in the service layer to handle the CRUD operations for Todo
items.
This is the moment, to pause and appreciate the beauty of the MVC architecture in action, where the controller acts as the intermediary between the user interface and the database and doesn't contain any business logic. So when we move our data from the JSON file to the database, we don't need to change the controller logic, only the service layer.
Finally, we'll use the same routing logic as before to handle the requests and direct them to the controller methods:
php1<?php 2 3use App\Http\Controllers\TodoController; 4 5Route::get('/todos', [TodoController::class, 'index']); 6Route::get('/todos/{id}', [TodoController::class, 'show']); 7Route::post('/todos', [TodoController::class, 'store']); 8Route::put('/todos/{id}', [TodoController::class, 'update']); 9Route::delete('/todos/{id}', [TodoController::class, 'destroy']);
With this setup, we have successfully integrated a database into our Laravel application and defined the database schema and data models using Eloquent. The service layer interacts with the database using the Eloquent model, and the controller handles the requests and responses, following the MVC architecture.
Integrating a database with your Laravel application is essential for many reasons. It enables you to store data persistently, support complex queries, and provide reliable performance for large datasets. With database integration, your application can scale and handle real-world data management needs.
Additionally, mastering database integration will not only make your ToDo app more robust but also empower you with skills that are highly valued in the software industry. From building applications that manage user profiles to e-commerce sites, a firm grasp of database interaction is vital.
Prepared to level up your Laravel application? Let's dive into practice and bring your app to life with powerful database capabilities!