Laravel 5.8 Todo CRUD Example Step By Step

laravel-5.8-todo-crud

In this tutorial i will show you how to create simple CRUD example in laravel. In the laravel CRUD example, we will create one Todo module for registered users. So users can create, read, update and delete there daily todos.

Lets install fresh laravel using this command. If you don’t know how to install laravel on your server, you can check this tutorial for laravel installation in windows xampp and database configuration.

composer create-project --prefer-dist laravel/laravel laravel-crud

Now lets create a new lara-crud database and configure database credentials in .env file

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=lara-crud
DB_USERNAME=root
DB_PASSWORD=

So lets get started creating our todo app step by step,

First of all we are going to make login, registration module for users using laravel authentication.  For authentication we will use inbuilt authentication module of laravel. Laravel makes implementing authentication very simple. In fact, almost everything is configured for you out of the box. Lets make laravel authentication using below artisan command.

php artisan make:auth

This command will install a layout view, registration and login views, as well as routes for all authentication end-points. It will also generate HomeController to handle post-login requests to your application’s dashboard.

We are not going to use HomeController and its default route here, instead we will use our TodoController and set its route for post-login requests. For that we need to do little customization in LoginController and RegistrationController. We will do it later.

Now run artisan migration command which will create required authentication tables in database.

php artisan migrate

Now lets start laravel application server to check authentication functionality using this artisan command.

php artisan serve

This command will start server on 8000 port. After successfully start server, now navigate this url http://127.0.0.1:8000 in browser. It will show laravel welcome page. You can see login and registration links on welcome page.

Login : http://127.0.0.1:8000/login

Register : http://127.0.0.1:8000/register

I have made some customization on welcome page so my welcome page won’t similar to your welcome page. You can check out my welcome.blade.php page here.

Users can register and login into system and create, read, update and delete their todos respectively.

First of all we need to make a database migration table. Lets make it using below artisan command

php artisan make:migration create_todos_table

This command will generate migration file with default code. Open and update it as below.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTodosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
            $table->string('description');
            $table->integer('user_id');
            $table->enum('status', ['pending','completed']);
            $table->timestamps();
        });
    }

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

Now run below migration command to generate todo database table.

php artisan migrate

Now lets create a todo model file using this artisan command.

php artisan make:model Todo

This will generate Todo.php model under app/ directory. We are going to use model file for database logic. Here we have used fillable property for mass assignable fields.

<?php
# app/Todo.php
namespace App;
use Illuminate\Database\Eloquent\Model;

class Todo extends Model
{
    protected $fillable = ['title', 'description', 'user_id', 'status'];
}

Now its time to create TodoController file. Controller contain all the CRUD business logic as we need. Lets make it using this artisan command.

php artisan make:controller TodoController --resource

Above artisan command will generate TodoController.php file  under app/Http/Controllers directory. The controller will contain a method for each of the available resource operations. Check below controller default code generated by above artisan controller command.

<?php

# app/Http/Controllers/TodoController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TodoController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

Next we need to register resourceful route to access all resource methods from TodoController. Open routes/web.php file and add route code.

<?php

# routes/web.php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::group(['middleware' => 'auth'], function () {
  Route::resource('/todo', 'TodoController');
});

This single route declaration Route::resource('/todo', 'TodoController'); creates multiple routes to handle a variety of actions on the resource. Here is a list of route which resource route generate.

VerbURIActionRoute Name
GET/todoindextodo.index
GET/todo/createcreatetodo.create
POST/todostoretodo.store
GET/todo/{todo}showtodo.show
GET/todo/{todo}/editedittodo.edit
PUT/PATCH/todo/{todo}updatetodo.update
DELETE/todo/{todo}destroytodo.destroy

Now its time to write code for listing all todos.

Todo List

Let’s create a todo list for user. To display todo list we will use index method in TodoController.

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $userId = Auth::user()->id;
    $todos = Todo::where(['user_id' => $userId])->get();
    return view('todo.list', ['todos' => $todos]);
}

Here we use where condition on user_id so we can get all todos for particular user. Now lets create list.blade.php layout file under resources/views/todo folder. Add the layout code shown as below.

# resources/views/todo/list.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
  <br>
    <div class="row justify-content-center">
    	<div class="col-md-6">
    		<h2>Todos List</h2>
    	</div>
    	<div class="col-md-6">
    		<div class="float-right">
    			<a href="{{ route('todo.create') }}" class="btn btn-primary"><i class="fa fa-plus"></i> Add new todo</a>
    		</div>
    	</div>
    	<br>
        <div class="col-md-12">
        	@if (session('success'))
                <div class="alert alert-success" role="alert">
                    {{ session('success') }}
                </div>
            @endif
            @if (session('error'))
                <div class="alert alert-danger" role="alert">
                    {{ session('error') }}
                </div>
            @endif
        	<table class="table table-bordered">
        <thead class="thead-light">
          <tr>
            <th width="5%">#</th>
            <th>Task Name</th>
            <th width="10%"><center>Task Status</center></th>
            <th width="14%"><center>Action</center></th>
          </tr>
        </thead>
        <tbody>
          @forelse ($todos as $todo)
              <tr>
              <th>{{ $todo->id }}</th>
              <td>{{ $todo->title }}</td>
              <td><center>{{ $todo->status }}</center></td>
              <td>
                <div class="action_btn">
                  <div class="action_btn">
                    <a href="{{ route('todo.edit', $todo->id)}}" class="btn btn-warning">Edit</a>
                  </div>
                  <div class="action_btn margin-left-10">
                    <form action="{{ route('todo.destroy', $todo->id)}}" method="post">
                      @csrf
                      @method('DELETE')
                      <button class="btn btn-danger" type="submit">Delete</button>
                    </form>
                  </div>
                </div>
              </td>
            </tr>
          @empty
              <tr>
              <td colspan="4"><center>No data found</center></td>
            </tr>
          @endforelse
        </tbody>
      </table>
        </div>
    </div>
</div>
@endsection

To view all todos listing navigate http://127.0.0.1:8000/todo url and it will show you screen as shown below.

Add/Create Todo

Next we will write code for add todo. We will use two method create() and store() from TodoController for that. Lets code it. Add the following code in create() and store() method.

# app/Http/Controllers/TodoController.php

/**
 * Show the form for creating a new resource.
 *
 * @return \Illuminate\Http\Response
 */
public function create()
{
    return view('todo.add');
}

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    $userId = Auth::user()->id;
    $input = $request->input();
    $input['user_id'] = $userId;
    $todoStatus = Todo::create($input);
    if ($todoStatus) {
        $request->session()->flash('success', 'Todo successfully added');
    } else {
        $request->session()->flash('error', 'Oops something went wrong, Todo not saved');
    }
    return redirect('todo');
}
Add  layout file [resources/views/todo/add.blade.php]

We need to create add.blade.php layout file under resources/views/todo folder for rendering add todo form. Add the following code in add.blade.php

# resources/views/todo/add.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
  <br>
    <div class="row justify-content-center">
    	<div class="col-md-6">
    		<h2>Add Todo</h2>
    	</div>
    	<div class="col-md-6">
    		<div class="float-right">
    			<a href="{{ route('todo.index') }}" class="btn btn-primary">Back</a>
    		</div>
    	</div>
    	<br>
        <div class="col-md-12">
        	@if (session('success'))
                <div class="alert alert-success" role="alert">
                    {{ session('success') }}
                </div>
            @endif
            @if (session('error'))
                <div class="alert alert-error" role="alert">
                    {{ session('error') }}
                </div>
            @endif
      <form action="{{ route('todo.store') }}" method="POST">
        @csrf
        <div class="form-group">
          <label for="title">Title:</label>
          <input type="text" class="form-control" id="title" name="title">
        </div>
        <div class="form-group">
          <label for="description">Description:</label>
          <textarea name="description" class="form-control" id="description" rows="5"></textarea>
        </div>
        <div class="form-group">
        <label for="status">Select todo status</label>
        <select class="form-control" id="status" name="status">
          <option value="pending">Pending</option>
          <option value="completed">Completed</option>
        </select>
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
        </div>
    </div>
</div>
@endsection

You can add new todo using this url http://127.0.0.1/todo/create.

Edit/Update Todo

Next we will implement edit/update functionality for existing todo. For that we will use edit() and update() controller methods. We will also make edit.blade.php view/layout file which contain todo edit form. Open TodoController.php and add update edit() and update() method as below.

# app/Http/Controllers/TodoController.php

/**
 * Show the form for editing the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function edit($id)
{
    $userId = Auth::user()->id;
    $todo = Todo::where(['user_id' => $userId, 'id' => $id])->first();
    if ($todo) {
        return view('todo.edit', [ 'todo' => $todo ]);
    } else {
        return redirect('todo')->with('error', 'Todo not found');
    }
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    $userId = Auth::user()->id;
    $todo = Todo::find($id);
    if (!$todo) {
        return redirect('todo')->with('error', 'Todo not found.');
    }
    $input = $request->input();
    $input['user_id'] = $userId;
    $todoStatus = $todo->update($input);
    if ($todoStatus) {
        return redirect('todo')->with('success', 'Todo successfully updated.');
    } else {
        return redirect('todo')->with('error', 'Oops something went wrong. Todo not updated');
    }
}

Here edit method will render edit todo form and update method will process form submit data.

Edit layout file [resources/views/todo/edit.blade.php]
# resources/views/todo/edit.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
  <br>
    <div class="row justify-content-center">
    	<div class="col-md-6">
    		<h2>Edit Todo</h2>
    	</div>
    	<div class="col-md-6">
    		<div class="float-right">
    			<a href="{{ route('todo.index') }}" class="btn btn-primary">Back</a>
    		</div>
    	</div>
    	<br>
        <div class="col-md-12">
        	@if (session('success'))
                <div class="alert alert-success" role="alert">
                    {{ session('success') }}
                </div>
            @endif
            @if (session('error'))
                <div class="alert alert-error" role="alert">
                    {{ session('error') }}
                </div>
            @endif
      <form action="{{ route('todo.update', ['id' => $todo->id]) }}" method="POST">
        @csrf
                @method('PUT')
        <div class="form-group">
          <label for="title">Title:</label>
          <input type="text" class="form-control" id="title" name="title" value="{{ $todo->title }}">
        </div>
        <div class="form-group">
          <label for="description">Description:</label>
          <textarea name="description" class="form-control" id="description" rows="5">{{ $todo->description }}</textarea>
        </div>
        <div class="form-group">
        <label for="status">Select todo status</label>
        <select class="form-control" id="status" name="status">
          <option value="pending" @if ($todo->status == 'pending') selected @endif>Pending</option>
          <option value="completed" @if ($todo->status == 'completed') selected @endif>Completed</option>
        </select>
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
        </div>
    </div>
</div>
@endsection

View/Display Todo

To display todo, we will use show() method of TodoController.php controller file. Update show() method code as shown below.

# app/Http/Controllers/TodoController.php

/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function show($id)
{
    $userId = Auth::user()->id;
    $todo = Todo::where(['user_id' => $userId, 'id' => $id])->first();
    if (!$todo) {
        return redirect('todo')->with('error', 'Todo not found');
    }
    return view('todo.view', ['todo' => $todo]);
}

Now create a view.blade.php layout for display todo.

Display todo layout file [resources/views/todo/view.blade.php]
# resources/views/todo/view.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
  <br>
    <div class="row justify-content-center">
    	<div class="col-md-6">
    		<h2>Todo Details</h2>
    	</div>
    	<div class="col-md-6">
    		<div class="float-right">
    			<a href="{{ route('todo.index') }}" class="btn btn-primary">Back</a>
    		</div>
    	</div>
    	<br>
        <div class="col-md-12">
            <br><br>
        	<div class="todo-title">
                <strong>Title: </strong> {{ $todo->title }}
            </div>
            <br>
            <div class="todo-description">
                <strong>Description: </strong> {{ $todo->description }}
            </div>
            <br>
            <div class="todo-description">
                <strong>Status: </strong> {{ $todo->status }}
            </div>
        </div>
    </div>
</div>
@endsection

Destroy Todo

We will use destroy() method of TodoController file to destroy/delete todo. We don’t need any layout file for destroy operation. Here we will only notify user about destroy operation and redirect to todo list route.

# app/Http/Controllers/TodoController.php

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function destroy($id)
{
    $userId = Auth::user()->id;
    $todo = Todo::where(['user_id' => $userId, 'id' => $id])->first();
    $respStatus = $respMsg = '';
    if (!$todo) {
        $respStatus = 'error';
        $respMsg = 'Todo not found';
    }
    $todoDelStatus = $todo->delete();
    if ($todoDelStatus) {
        $respStatus = 'success';
        $respMsg = 'Todo deleted successfully';
    } else {
        $respStatus = 'error';
        $respMsg = 'Oops something went wrong. Todo not deleted successfully';
    }
    return redirect('todo')->with($respStatus, $respMsg);
}

Now we will update post-login request route  in LoginController and RegistrationController as below.

LoginController.php [app/Http/Controllers/Auth]

/**
 * Where to redirect users after login.
 *
 * @var string
 */
protected $redirectTo = '/todo';

RegisterController.php [app/Http/Controllers/Auth]

/**
 * Where to redirect users after registration.
 *
 * @var string
 */
protected $redirectTo = '/todo';

The reason behind updating this route is that it will redirect to todo list page after successful registration and login of user. Normally it redirect to dashboard page of HomeController but we dont need it so we use different route.

So finally out todo CRUD in laravel 5.8 is completed. You can download code from github repository and setup it in your system. There is project setup guide available in README.md file. If you need any help, please write it in comment. I will try to help you. Please share this tutorial with your friends too. Thank you.

About Chintan Panchal

I am web developer having 4+ year of experience of web development. I have worked on codeigniter, symfony, laravel and wordpress.

View all posts by Chintan Panchal →

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.