This tutorial provides all the steps required to perform CRUD operations in applications developed using the Laravel Framework. It provides the complete steps following the MVC (Model View Controller) architecture for the most recent version of Laravel i.e. Laravel 8. Also, it provides the steps to Create, Read, Update, and Delete using the MySQL database. The steps should be the same for other databases including MariaDB and SQLite.
It provides the simple steps to perform the CRUD operations to manage the tasks by creating a task manager application using Laravel 8. In order to keep this tutorial simple, it does not cover the authentication of users using the login and registration process.
Prerequisites
This tutorial assumes that PHP, Apache Web Server, and MySQL are already installed on your system. You can follow How To Install XAMPP On Windows or How To Install WampServer on Windows to install all of them together. You may follow How To Install PHP 8 On Ubuntu 20.04 LTS, How To Install Apache 2 On Ubuntu 20.04 LTS, How To Install MySQL 8 on Ubuntu 20.04 LTS, How To Install PHP 8 On Windows 10, How To Install Apache 2 On Windows, and How To Install MySQL 8 With Workbench On Windows 10 to install them separately. You are also required to have Composer installed on your system. You can follow How To Install Composer On Ubuntu 20.04 and How To Install Composer As PHP Dependency Manager On Windows to install Composer.
Notes: I have used WampServer with PHP 7.4.9, Apache 2.4.46, and MySQL 8 installed on my system. Also, this tutorial installs Laravel using Composer and does not cover the Docker way of installing Laravel.
Install Laravel 8
In this step, we will create the project using the composer and install the most recent version of Laravel 8 using the command as shown below.
# Create the project by installing Laravel 8
composer create-project laravel/laravel=8.* mvccruddemo --prefer-dist
# Output
Creating a "laravel/laravel=8.*" project at "./mvccruddemox"
Installing laravel/laravel (v8.6.5)
- Downloading laravel/laravel (v8.6.5)
- Installing laravel/laravel (v8.6.5): Extracting archive
....
....
Package manifest generated successfully.
78 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi
No publishable resources for tag [laravel-assets].
Publishing complete.
> @php artisan key:generate --ansi
Application key set successfully.
The above command creates the default project structure of Laravel 8 and installs the dependencies in the vendor directory as shown in Fig 1.
Create Database
In this step, we will create the MySQL database required to perform the CRUD operations using the Laravel 8 application installed by us in the previous step. We will also configure the project environment to use the database created by us. You can create the database either using the console or MySQL Workbench by executing the command as shown below.
# Create Database
CREATE SCHEMA `mvccruddemo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Instead of using the root user, you may create a specific user to manage this database by providing appropriate privileges.
# Create User
CREATE USER 'mvccrud'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'password';
# Example
CREATE USER 'mvccrud'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'MvC12@ud';
# Grant Privileges
GRANT ALL PRIVILEGES ON mvccruddemo.* TO 'mvccrud'@'localhost';
Now, configure the Laravel 8 CRUD application to use the MySQL database and user by updating the .env file located at the root of the project as shown below.
....
....
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mvccruddemo
DB_USERNAME=mvccrud
DB_PASSWORD="MvC12@ud"
....
....
Notes: Avoid using # in your password, else enclose it in quotes.
Create Migrations
In this step, we will create the migration to create the database table to store the tasks. Open the console and navigate to the project root directory. Also, create the migration using the command as shown below.
# Create Migration
php artisan make:migration create_tasks_table --create=tasks
The above command will create the migration script in the database/migrations directory. You may notice few migration scripts are already there in the migrations directory. These were created by Laravel as part of the project installation step. The table fields required to manage the tasks are shown below.
Id | The unique id to identify the task. |
User Id | The user id to identify the corresponding user. |
Created By | The user id to identify the user who added the task. |
Updated By | The user id to identify the user who updated the task. |
Title | The title of the Task. |
Description | The description of the Task. |
Status | The status of the task can be New, In-Progress, or Completed. |
Hours Required | The total hours required by the Task. It can be filled while creating the task. |
Hours Consumed | The total hours consumed by the Task. It can be manually filled on task completion. |
Created At | It stores the date and time at which the task is created. |
Updated At | It stores the date and time at which the task is updated. |
Planned Start Date | It stores the date and time at which the task is planned to start. |
Planned End Date | It stores the date and time at which the task is planned to end. |
Actual Start Date | It stores the actual date and time at which the task started. |
Actual End Date | It stores the actual date and time at which the task is finished. |
Content | The column is used to store the task details. |
It uses the column status to track the status of the task. The status can be either New, In-Progress, or Completed. Also, the user can manually fill the hours required to complete the task.
Now, open the tasks migration script and update it as shown below.
public function up() {
Schema::create( 'tasks', function( Blueprint $table ) {
$table->id();
$table->string( 'title', 512 );
$table->string( 'description', 1024 )->nullable();
$table->smallInteger( 'status' );
$table->smallInteger( 'hoursRequired' )->default( 0 );
$table->smallInteger( 'hoursConsumed' )->default( 0 );
$table->dateTime( 'createdAt' );
$table->dateTime( 'updatedAt' )->nullable();
$table->date( 'plannedStartDate' )->nullable();
$table->date( 'plannedEndDate' )->nullable();
$table->date( 'actualStartDate' )->nullable();
$table->date( 'actualEndDate' )->nullable();
$table->text( 'content' )->nullable();
$table->foreignId( 'userId' )->nullable()->constrained( 'users' )->onUpdate( 'cascade' )->onDelete( 'cascade' );
$table->foreignId( 'createdBy' )->nullable()->constrained( 'users' )->onUpdate( 'cascade' )->onDelete( 'set null' );
$table->foreignId( 'updatedBy' )->nullable()->constrained( 'users' )->onUpdate( 'cascade' )->onDelete( 'set null' );
});
}
Use the below-mentioned command to run the migration scripts to create the database tables as shown below. Make sure to run this command from the project root directory.
# Create Migrations
php artisan migrate
# Output
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (106.22ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (107.32ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (78.76ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (134.28ms)
Migrating: 2021_10_31_134047_create_tasks_table
Migrated: 2021_10_31_134047_create_tasks_table (50.00ms)
This completes the migrations and creates the tables corresponding to the migration scripts.
Create Controller And Model
In this step, we will create the TaskController and the corresponding resource model as shown below.
# Create Controller and Resource Model
php artisan make:controller TaskController --resource --model=Task
# Output
A App\Models\Task model does not exist. Do you want to generate it? (yes/no) [yes]:
> yes
Model created successfully.
Controller created successfully.
The create controller command asks to create the model as shown above. Enter yes to create the Task model. The command will create the TaskController in the app/Http/Controllers directory with the default CRUD methods and Task model in the app/Models directory.
Add Resource Route
In this step, we will add a resource route to perform the CRUD operations using the TaskController created by us in the previous step. Update the routes/web.php to add the resource route as shown below.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController;
....
....
Route::resource( 'tasks', TaskController::class );
Update Controller And Model
In this step, we will update the controller and model to perform the CRUD operations. I have updated the default methods created by the create controller command as shown below.
<?php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
class TaskController extends Controller {
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index() {
$tasks = Task::latest()->paginate( 10 );
$statusMap = Task::$statusMap;
return view( 'tasks.index', compact( 'tasks', 'statusMap' ) )
->with( 'i', ( request()->input( 'page', 1 ) - 1 ) * 10 );
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create() {
$statusMap = Task::$statusMap;
return view( 'tasks.create', compact( 'statusMap' ) );
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store( Request $request ) {
$request->validate([
'title' => 'required',
'status' => 'required|numeric|min:0|not_in:0',
'hoursRequired' => 'numeric|nullable|min:0|not_in:0',
'hoursConsumed' => 'numeric|nullable|min:0|not_in:0',
'plannedStartDate' => 'nullable|date_format:Y-m-d',
'plannedEndDate' => 'nullable|date_format:Y-m-d',
'actualStartDate' => 'nullable|date_format:Y-m-d',
'actualEndDate' => 'nullable|date_format:Y-m-d'
]);
Task::create( $request->all() );
return redirect()->route( 'tasks.index' )
->with( 'success', 'Task created successfully.' );
}
/**
* Display the specified resource.
*
* @param \App\Models\Task $task
* @return \Illuminate\Http\Response
*/
public function show( Task $task ) {
return view( 'tasks.show', compact( 'task' ) );
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Task $task
* @return \Illuminate\Http\Response
*/
public function edit( Task $task ) {
$statusMap = Task::$statusMap;
return view( 'tasks.edit', compact( 'task', 'statusMap' ) );
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Task $task
* @return \Illuminate\Http\Response
*/
public function update( Request $request, Task $task ) {
$request->validate([
'title' => 'required',
'status' => 'required|numeric|min:0|not_in:0',
'hoursRequired' => 'numeric|nullable|min:0|not_in:0',
'hoursConsumed' => 'numeric|nullable|min:0|not_in:0',
'plannedStartDate' => 'nullable|date_format:Y-m-d',
'plannedEndDate' => 'nullable|date_format:Y-m-d',
'actualStartDate' => 'nullable|date_format:Y-m-d',
'actualEndDate' => 'nullable|date_format:Y-m-d'
]);
$task->update( $request->all() );
return redirect()->route( 'tasks.index' )
->with( 'success', 'Task updated successfully.' );
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Task $task
* @return \Illuminate\Http\Response
*/
public function destroy( Task $task ) {
$task->delete();
return redirect()->route( 'tasks.index' )
->with( 'success', 'Task deleted successfully.' );
}
}
I have also updated the model as shown below.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model {
const CREATED_AT = 'createdAt';
const UPDATED_AT = 'updatedAt';
const STATUS_NEW = 10;
const STATUS_PROGRESS = 20;
const STATUS_COMPLETED = 30;
public static $statusMap = [
self::STATUS_NEW => 'New',
self::STATUS_PROGRESS => 'In Progress',
self::STATUS_COMPLETED => 'Completed'
];
use HasFactory;
/**
* The storage format of the model's date columns.
*
* @var string
*/
protected $dateFormat = 'Y-m-d H:i:s';
protected $fillable = [
'title', 'description', 'status',
'hoursRequired', 'hoursConsumed',
'plannedStartDate', 'plannedEndDate',
'actualStartDate', 'actualEndDate',
'content'
];
public function getStatusStr() {
return self::$statusMap[ $this->status ];
}
}
Create Layout And Views
In this step, we will create the layout used for the CRUD operations. We will also create the views to perform the CRUD operations.
Task Layout - <project root>/resources/views/layouts/task.blade.php
<!DOCTYPE html>
<html>
<head>
<!-- Meta -->
<meta charset="utf-8">
<meta name="title" content="@yield( 'metaTitle' ) | Task Manager">
<meta name="description" content="@yield('metaDescription')" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Title -->
<title>@yield( 'metaTitle' ) | Task Manager</title>
<!-- Favicon -->
<link rel="shortcut icon" href="{{ url('/images/icons/favicon.ico') }}">
<link rel="apple-touch-icon-precomposed" href="{{ url('/images/icons/apple-icon-precomposed.png') }}">
<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker3.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<script type="text/javascript">
jQuery( document ).ready(function() {
jQuery( '.datepicker' ).datepicker();
jQuery( '.trigger-info' ).click( function() {
var info = jQuery( this ).closest( '.row-task-main' ).next( '.row-task-info' );
jQuery( '.row-task-info' ).not( info ).hide();
if( info.is( ':visible' ) ) {
info.slideUp( 'fast' );
}
else {
info.slideDown( 'slow' );
}
});
});
</script>
<style type="text/css">
.cursor-pointer {
cursor: pointer;
}
.row-task-info {
display: none;
}
</style>
</head>
<body>
<header>
<div class="p-5 text-center bg-light">
<h1 class="mb-3">Task Manager</h1>
<h4 class="mb-3">(Manage Tasks)</h4>
</div>
</header>
<div class="container">
@yield( 'content' )
</div>
</body>
</html>
Tasks Index - <project root>/resources/views/tasks/index.blade.php
@extends( 'layouts.task' )
@section( 'metaTitle', 'All Tasks' )
@section( 'metaDescription', 'Shows the list of all the tasks.' )
@section( 'content' )
<div class="clearfix pt-4 pb-2">
<div class="float-left">
<h2>Manage Tasks</h2>
</div>
<div class="float-right">
<a class="btn btn-success" href="{{ route( 'tasks.create' ) }}" title="Create Task">
<i class="fas fa-plus"></i>
</a>
</div>
</div>
@if( $message = Session::get( 'success' ) )
<div class="alert alert-success">
<p>{{ $message }}</p>
</div>
@endif
<div class="container">
<div class="row text-2xl">
<div class="col p-2 border border-right-0"><h5>Title</h5></div>
<div class="col-6 p-2 border border-right-0"><h5>Description</h5></div>
<div class="col p-2 border border-right-0"><h5>Status</h5></div>
<div class="col p-2 border"><h5>Actions</h5></div>
</div>
@foreach( $tasks as $task )
<div class="row row-task-main">
<div class="col p-2 border border-right-0">{{ $task->title }}</div>
<div class="col-6 p-2 border border-right-0">{{ $task->description }}</div>
<div class="col p-2 border border-right-0">{{ $task->getStatusStr() }}</div>
<div class="col p-2 border">
<form action="{{ route( 'tasks.destroy', $task->id ) }}" method="POST">
<span class="trigger-info cursor-pointer" title="Info">
<i class="fas fa-info text-success fa-lg"></i>
</span>
<a href="{{ route( 'tasks.show', $task->id ) }}" title="View">
<i class="fas fa-eye text-success fa-lg"></i>
</a>
<a href="{{ route( 'tasks.edit', $task->id) }}">
<i class="fas fa-edit fa-lg"></i>
</a>
@csrf
@method( 'DELETE' )
<button type="submit" title="Delete" style="border: none; background-color:transparent;">
<i class="fas fa-trash fa-lg text-danger"></i>
</button>
</form>
</div>
</div>
<div class="row row-task-info border border-top-0 p-3">
<div class="row">
<div class="col-md-6">
<span class="font-weight-bold">Hours Estimated:</span> {{ $task->hoursRequired }}<br/>
<span class="font-weight-bold">Hours Consumed:</span> {{ $task->hoursConsumed }}
</div>
<div class="col-md-6">
<span class="font-weight-bold">Planned Start Date:</span> {{ $task->plannedStartDate }}<br/>
<span class="font-weight-bold">Planned End Date:</span> {{ $task->plannedEndDate }}
</div>
<div class="col-md-6">
<span class="font-weight-bold">Actual Start Date:</span> {{ $task->actualStartDate }}<br/>
<span class="font-weight-bold">Actual End Date:</span> {{ $task->actualEndDate }}
</div>
</div>
</div>
@endforeach
</div>
{!! $tasks->links() !!}
@endsection
Create Task - <project root>/resources/views/tasks/create.blade.php
@extends( 'layouts.task' )
@section( 'metaTitle', 'Create Task' )
@section( 'metaDescription', 'Create a new task.' )
@section( 'content' )
<div class="clearfix pt-4 pb-2">
<div class="float-left">
<h2>Add Task</h2>
</div>
<div class="float-right">
<a class="btn btn-primary" href="{{ route( 'tasks.index' ) }}" title="All Tasks">
<i class="fa fa-arrow-left"></i>
</a>
</div>
</div>
@if($errors->any())
<div class="alert alert-danger">
Resolve the errors to create the task.<br/>
<ul>
@foreach( $errors->all() as $error )
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route( 'tasks.store' ) }}" method="POST">
@csrf
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Title *</label>
<input type="text" name="title" class="form-control" placeholder="Title" value="{{old('title')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Status *</label>
<select name="status" class="form-control">
<?php
foreach( $statusMap as $key => $value ) {
if( !empty( old( 'status' ) ) && $key == old( 'status' ) ) {
?>
<option value="<?= $key ?>" selected><?= $value ?></option>
<?php } else { ?>
<option value="<?= $key ?>"><?= $value ?></option>
<?php } } ?>
</select>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<div class="form-group">
<label>Description</label>
<textarea name="description" class="form-control" placeholder="Description">{{old('description')}}</textarea>
</div>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Hours Estimated</label>
<input type="text" name="hoursRequired" class="form-control" placeholder="Hours Estimated" value="{{old('hoursRequired')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Hours Consumed</label>
<input type="text" name="hoursConsumed" class="form-control" placeholder="Hours Consumed" value="{{old('hoursConsumed')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Planned Start Date</label>
<input type="text" name="plannedStartDate" class="form-control datepicker" placeholder="Planned Start Date" data-date-format="yyyy-mm-dd" value="{{old('plannedStartDate')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Planned End Date</label>
<input type="text" name="plannedEndDate" class="form-control datepicker" placeholder="Planned End Date" data-date-format="yyyy-mm-dd" value="{{old('plannedEndDate')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Actual Start Date</label>
<input type="text" name="actualStartDate" class="form-control datepicker" placeholder="Actual Start Date" data-date-format="yyyy-mm-dd" value="{{old('actualStartDate')}}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Actual End Date</label>
<input type="text" name="actualEndDate" class="form-control datepicker" placeholder="Actual End Date" data-date-format="yyyy-mm-dd" value="{{old('actualEndDate')}}">
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<div class="form-group">
<label>Notes</label>
<textarea name="content" class="form-control" placeholder="Notes">{{old('content')}}</textarea>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center p-3">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</div>
</form>
@endsection
Update Task - <project root>/resources/views/tasks/edit.blade.php
@extends( 'layouts.task' )
@section( 'metaTitle', 'Update Task' )
@section( 'metaDescription', 'Update an existing task.' )
@section( 'content' )
<div class="clearfix pt-4 pb-2">
<div class="float-left">
<h2>Update Task</h2>
</div>
<div class="float-right">
<a class="btn btn-primary" href="{{ route( 'tasks.index' ) }}" title="All Tasks">
<i class="fa fa-arrow-left"></i>
</a>
</div>
</div>
@if($errors->any())
<div class="alert alert-danger">
Resolve the errors to create the task.<br/>
<ul>
@foreach( $errors->all() as $error )
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route( 'tasks.update', $task->id ) }}" method="POST">
@csrf
@method('PUT')
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Title *</label>
<input type="text" name="title" class="form-control" placeholder="Title" value="{{ $task->title }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Status *</label>
<select name="status" class="form-control">
<?php
foreach( $statusMap as $key => $value ) {
if( $key == $task->status ) {
?>
<option value="<?= $key ?>" selected><?= $value ?></option>
<?php } else { ?>
<option value="<?= $key ?>"><?= $value ?></option>
<?php } } ?>
</select>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<div class="form-group">
<label>Description</label>
<textarea name="description" class="form-control" placeholder="Description">{{ $task->description }}</textarea>
</div>
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Hours Estimated</label>
<input type="text" name="hoursRequired" class="form-control" placeholder="Hours Estimated" value="{{ $task->hoursRequired }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Hours Consumed</label>
<input type="text" name="hoursConsumed" class="form-control" placeholder="Hours Consumed" value="{{ $task->hoursConsumed }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Planned Start Date</label>
<input type="text" name="plannedStartDate" class="form-control datepicker" placeholder="Planned Start Date" data-date-format="yyyy-mm-dd" value="{{ $task->plannedStartDate }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Planned End Date</label>
<input type="text" name="plannedEndDate" class="form-control datepicker" placeholder="Planned End Date" data-date-format="yyyy-mm-dd" value="{{ $task->plannedEndDate }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Actual Start Date</label>
<input type="text" name="actualStartDate" class="form-control datepicker" placeholder="Actual Start Date" data-date-format="yyyy-mm-dd" value="{{ $task->actualStartDate }}">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<label>Actual End Date</label>
<input type="text" name="actualEndDate" class="form-control datepicker" placeholder="Actual End Date" data-date-format="yyyy-mm-dd" value="{{ $task->actualEndDate }}">
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<div class="form-group">
<label>Notes</label>
<textarea name="content" class="form-control" placeholder="Notes">{{ $task->content }}</textarea>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center p-3">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</div>
</form>
@endsection
View Task - <project root>/resources/views/tasks/show.blade.php
@extends( 'layouts.task' )
@section( 'metaTitle', 'View Task' )
@section( 'metaDescription', 'View task details.' )
@section( 'content' )
<div class="clearfix pt-4 pb-2">
<div class="float-left">
<h2>Task Details ({{ $task->title }})</h2>
</div>
<div class="float-right">
<a class="btn btn-primary" href="{{ route( 'tasks.index' ) }}" title="All Tasks">
<i class="fa fa-arrow-left"></i>
</a>
</div>
</div>
<div class="row pt-3">
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Title - </strong> {{ $task->title }}
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Status - </strong> {{ $task->getStatusStr() }}
</div>
</div>
<div class="row pt-3">
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Description - </strong> {{ $task->description }}
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Notes - </strong> {{ $task->notes }}
</div>
</div>
<div class="row pt-3">
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Hours Estimated - </strong> {{ $task->hoursRequired }}
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Hours Consumed - </strong> {{ $task->hoursConsumed }}
</div>
</div>
<div class="row pt-3">
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Planned Start Date - </strong> {{ $task->plannedStartDate }}
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Planned End Date - </strong> {{ $task->plannedEndDate }}
</div>
</div>
<div class="row pt-3">
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Actual Start Date - </strong> {{ $task->actualStartDate }}
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<strong>Actual End Date - </strong> {{ $task->actualEndDate }}
</div>
</div>
@endsection
This completes the views section.
Configure The Application
Optionally, we can configure the application to use it as a sub-directory on localhost by updating the configuration files as shown below.
# Update <project root>/.env
....
....
APP_URL=http://localhost/mvccruddemo
....
....
# Update <project root>/public/.htaccess
....
....
RewriteEngine On
RewriteBase /mvccruddemo/
....
....
Run The Application
In the previous steps, we have created the application, database, migrations, controller, model, and views. We have also configured the environment to use the database created by us and updated the routes to include our resource route. In this step, we will launch the application using the development server as shown below.
# Launch Application
php artisan serve
# Output
Starting Laravel development server: http://127.0.0.1:8000
[Sun Oct 31 19:45:16 2021] PHP 7.4.9 Development Server (http://127.0.0.1:8000) started
We can also launch the application using the Apache Web Server by adding an alias pointing to the public directory located at the project root.
Now open the application using your preferred web browser using the URL http://127.0.0.1:8000. The default page on launching the application shows the Laravel details as shown in Fig 2.
Now open the tasks page using your preferred web browser using the URL http://127.0.0.1:8000/tasks. The tasks page shows the tasks created by us as shown in Fig 3. Initially, it won't show any task.
Summary
This tutorial provided all the steps required to perform the CRUD operations using Laravel 8 and MySQL. You can download the complete source code from GitHub.