Now, we are finally going to start using everything we’ve learned about Laravel so far, and use it to create a real project.
Let’s start with something easier. In this article, we are going to create a mini blog, and it only contains posts, without categories or tags. Each post has a title and a content. This is not a full-featured blog application, but through this example, I’m going to demonstrate exactly how to retrieve data from the database, how to create/update new information, how to save them in the database, as well as how to delete them.
First, let’s deal with the database. We’ll create a migration file for the post table. This table should contain the title and the content. Generate a migration file with the following command:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
returnnewclassextends Migration
{
/**
* Run the migrations.
*/publicfunctionup(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('title');
$table->text('content');
});
}
/**
* Reverse the migrations.
*/publicfunctiondown(): void
{
Schema::dropIfExists('posts');
}
};
In this example, we created five columns in the post table:
id() creates an id column, which is commonly used for indexing.
timestamps() creates two columns, created_at and uptated_at. These two columns will be automatically updated when the record is created and updated.
string('title') creates a column title with type VARCHAR, whose default length is 255 bytes.
string('content') creates the content column.
To apply the changes, run the following command:
1
php artisan migrate
And a new posts table should be generated:
Now, we can create the corresponding model for this table.
1
php artisan make:model Post
app/Models/Post.php
1
2
3
4
5
6
7
8
9
10
11
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
classPostextends Model
{
use HasFactory;
}
And generate the corresponding resource controller:
The output contains information such as request methods, controller methods, as well as route names. These information are very important, and we are going to need them later in this article.
Now it is time for us to dive into the application itself. When building real-life applications, it is unlikely for you to create all the controllers first, and then design the templates, and then move on to the routers. Instead, you need to think from the user’s perspective, and think about what actions the use might want to take.
In general, the user should have the ability to perform four operations to each resource, which in our case, is the Post.
Create: The user should be able to create new resources and save them in the database.
Read: The user should be able to read resources, both retrieving a list of resources, as well as checking the details of a specific resource.
Update: The user should be able to update existing resources, and update the corresponding database record.
Delete: The user should be able to remove a resource from the database.
Together, they are referred to as the CRUD operations.
Right now, our database is still empty, so the user might want to create new posts. SO let’s start with the create (C) action. To complete this create action, you need two things:
A create() controller method, which displays a form, allowing the user to fill out the title and content.
A store() controller method, which saves the newly created post to the database, and redirect the user to the list page.
The create() method matches the URL pattern /posts/create (GET method), and the store() method matches the URL /post (POST method).
Here is a brief review on HTTP methods in case you need a refresher:
The GET method is the most commonly used HTTP request method. It is used to request data and resources from the server.
The POST method is used to send data to the server, used for creating/updating a resource.
The HEAD method works just like the GET method. Except the HTTP response will only contain the head and not the body. This method is usually used by developers for debugging purposes.
The PUT method is similar to POST, with one small difference. When you POST a resource that already exists on the server, this action would not cause any difference. The PUT method, however, will duplicate that resource, every time you make the request.
The DELETE method removes a resource from the server.
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Contracts\View\View;
...classPostControllerextends Controller
{
.../**
* Show the form for creating a new resource.
*/publicfunctioncreate(): View
{
return view('posts.create');
}
...}
This method will be executed when you send a GET request to /posts/create, and it points to the view views/posts/create.blade.php. Notice line 16, Response has been changed to View since this methods needs to return a view instead.
Next, we should create the corresponding view. We can start with the layout:
Line 17 and 22, the double curly braces ({{ }}) allows you to execute PHP code inside the template, and in the case, the route('posts.index') method will return the route whose name is posts.index. You may check the route names by referring to the output of the php artisan route:list command.
Next, let’s move on to the create view. You need to be organized here. Since this create view is for post related actions, I created a post directory to store this view.
There are a few things we need to notice when using forms to transfer data.
Line 10, the action attribute defines what happens when this form is submitted, and in this case, it instructs the browser to visit the route posts.store, with a POST HTTP method.
Line 11, {{ csrf_field() }}. CSRF is a malicious attack targeting web applications, and this csrf_field() function provides protection against that type of attack. You can read more about CSRF (Cross-site request forgery) here .
Line 13 and 17, pay attention to the name attribute. When the form is submitted, the user input will be tied to a variable, whose name is specified by the name attribute. For instance, when name="title", the user input will be tied to the variable title, and we can access its value using $request->input('title'). We’ll see exactly how this works later.
Line 20, the type attribute must be set to submit for this form to work.
Now start the dev server and go to http://127.0.0.1:8000/posts/create.
When the submit button is clicked, the browser will send a POST request to the server, and this time, the store() method will be executed. This POST request will contain user input, and they can be accessed like this:
<?php
namespace App\Http\Controllers;
...classPostControllerextends Controller
{
.../**
* Store a newly created resource in storage.
*/publicfunctionstore(Request $request): RedirectResponse
{
// Get the data from the request
$title=$request->input('title');
$content=$request->input('content');
// Create a new Post instance and put the requested data to the corresponding column
$post=new Post;
$post->title=$title;
$post->content=$content;
// Save the data
$post->save();
return redirect()->route('posts.index');
}
...}
After the data has been stored, you will be redirected to the posts.index route.
Head back to your browser and type in a new title and content, and then click the submit button. The index view hasn’t been created yet, so an error message will be returned. However, if you check the database, a new record should be added.
Next, let’s work on the read operation (R). There are, in fact, two different types of read operations. The first one is the list action, which returns a listing of all posts to the user. This action corresponds to the index() method:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
classPostControllerextends Controller
{
/**
* Display a listing of the resource.
*/publicfunctionindex(): View
{
$posts= Post::all();
return view('posts.index', [
'posts'=>$posts,
]);
}
...}
Line 9, foreach iterates over all retrieved $posts, and assign each value to the variable $post.
Line 10, notice how the post id is passed to the posts.show route. The route will then pass this variable to the show() controller method, which you’ll see later.
Line 11, the Str::words() method is a PHP helper , and it will only take the first 100 words of the content.
There are a few things we must note here. First, notice how the Update button is a simple link, but the Delete button is a form. This is because a link would tell the browser to send a GET request to the server, but we need something else for the delete action.
Next, if you look inside the form, you will find that this form has method="POST", but we need a DELETE method for the delete action. This is because by default, HTML only supports GET and POST methods, and if you need something else, you must set method="POST", and use the method_field() method instead.
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
classPostControllerextends Controller
{
.../**
* Show the form for editing the specified resource.
*/publicfunctionedit(string $id): View
{
$post= Post::all()->find($id);
return view('posts.edit', [
'post'=>$post,
]);
}
/**
* Update the specified resource in storage.
*/publicfunctionupdate(Request $request, string $id): RedirectResponse
{
// Get the data from the request
$title=$request->input('title');
$content=$request->input('content');
// Find the requested post and put the requested data to the corresponding column
$post= Post::all()->find($id);
$post->title=$title;
$post->content=$content;
// Save the data
$post->save();
return redirect()->route('posts.show', ['post'=>$id]);
}
...}
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
classPostControllerextends Controller
{
.../**
* Remove the specified resource from storage.
*/publicfunctiondestroy(string $id): RedirectResponse
{
$post= Post::all()->find($id);
$post->delete();
return redirect()->route('posts.index');
}
}
This action does not require a view, since it just redirects you to posts.index after the action is completed.
If you think my articles are helpful, please consider making a donation to me. Your
support is greatly appreciated.