Build Laravel CRUD Web App

22 minutes, 58 seconds

We will build a simple CRUD application with Laravel 5 (5.3.16) with MySQL database. CRUD is stand for Create, Read, Update and Delete. It’s a core and important feature on a web app.

We will also learn about instlalling LaravelColective using Composer and setup with Laravel. It’s a component to generate form.

laravel-logo-white.png

Get Prepared

This guide will not be exhaustive and cover every detail of Laravel and it does expect a few prerequisites.

Install Composer

Composer is a PHP dependancy manager. We will using Composer to install Laravel. Download it from here.

Install MySQL

By using app like MAMP or XAMPP there is a feature to start MySQL too. You may choose your own solution like install manually MySQL to your machine.

Start Developing

Let’s start to create our new Laravel web app.

Creating the Project

Run this command to create a Laravel app named mytasks.


$ composer create-project laravel/laravel mytasks

Yahoo! We just create our new Laravel web app. Sometime we just want to checking our Laravel Version. cd to the created mytasks folder and run this command to check your Laravel version. In this case is version 5.3.24.


$ php artisan --version

Configure ENV for Database

Before we should run the server we may need to configure our database at .env file. Edit these configuration suit your MySQL configuration.


DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=mytasks
DB_USERNAME=root
DB_PASSWORD=root

Run Server

Laravel is equipped with its own server. You may need to run the server at a long time for testing and debugging, it’s advisable to open a new tab or window to run this command.


$ php artisan serve

By having the command running, you may open the web app by visiting localhost:8000 with your browser.

Laravel Routing

Routing is a core element for Laravel. It defines all the routes that possible to your web app.

For this version of Laravel (5.3.x), look up for routes folder, there are 3 files:

  • api.php can register API routes for your application.
  • console.php is where you may define all of your Closure based console commands.
  • web.php tell Laravel the URIs it should respond to using a Closure or controller method.

We only interested to web.php. Now open the file and replace the entire content with these lines of codes.


<?php

Route::get('/', [
    'as' => 'home',
    'uses' => 'PagesController@home'
]);

Route::resource('tasks', 'TasksController');

We just added 2 routes; ‘/’ and to ‘tasks’.

‘/’ the the main route when you open the website, it routing to a controller called PageController for a home() function. And the route being refered as ‘home‘ for for future reference or linking. It’s a GET method route.

‘task’ route is a resource route which assigned to typical ‘CRUD’ controller (TasksController) which predefined with loads of functions. We will discuss further on TasksController later.

We don’t have those controller yet on the app, it might show error if you try to access it. So let’s create those controllers with command line.

Create the Controllers

As discussed earliear we have 2 controllers; PageController is a normal controller and TasksController is a resource controller (which is used for CRUD operation).

Now with the power of command line let’s create a normal controller called PageController.


$ php artisan make:controller PagesController

Then make a resource (CRUD-specialty) controller named TasksController.


$ php artisan make:controller TasksController --resource

Now, by typing those commands, Laravel will generate new files inside app/Http/Controllers/. We have PagesController.php and TasksController.php.

Open the PagesController, and add a new function called home().


public function home()
{
    return 'Welcome home!';
}

Now you may test run the app, and you will see a simple text ‘Welcome home!’.

If you open TasksController file, it’s packed with many predefined method, because we use special option (–resource) to create the controller. We will discuss on this later.

Views & Basic Templating with Blade

Laravel use Blade templating system, that’s why the view file have filename ended with .blade.php. With Blade there is rules to make the content more dynamic as we will learn by reading the codes.

The views are inside resouces/views folder. Use subfolder to better manage the views system.

Let’s create subfolder named ‘layouts‘, inside it create a new view file named ‘master.blade.php‘. This will be our master template which has the navigation bar and the place to yield the content.


<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tasks</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" href="#">Tasks</a>
    </div>
    <div class="nav navbar-nav navbar-right">
        <li><a href="{{ route('home') }}">Home</a></li>
        <li><a href="{{ route('tasks.index') }}">Tasks</a></li>
    </div>
  </div>
</nav>

<main>
    <div class="container">
        @yield('content')
    </div>
</main>

</body>
</html>

Lets create another subdirectory inside views folder named pages, create a new file named home.blade.php.


@extends('layouts.master')

@section('content')

<h1>Welcome Home</h1>
<p class="lead">This app will do CRUD for Task model with Laravel.</p>
<hr>

<a href="{{ route('tasks.index') }}" class="btn btn-info">View Tasks</a>
<a href="{{ route('tasks.create') }}" class="btn btn-primary">Add New Task</a>

@stop

Back to PagesController, we have to do some modification to show our newly created view file. Replace home() function to like this one.


public function home()
{
  return view('pages.home');
}

Now try to refresh the page, and you will see something nicer like this.

S

Tasks Controller

Let’s open TaksController.php and see all the functions that is precreated. All is used for displaying the page with GET and also other HTTP vers to handle the functions.

  • GET index the main page of for /tasks route.
  • GET create will be the method we use to generate a page where we can create new tasks.
  • POST store will be the method we use to handle POST data from the task creation, and store it in the database.
  • GET show will be the method used to show a single task.
  • GET edit will be the method used to allow us to edit an existing task.
  • PUT/PATCH update will be the method that gets called for updating an existing task.
  • DELETE destroy delete the task.

Now we just need to edit inside the created functions within TasksController. For starter let’s create the index() fuction to show the main route for that, then create the coresponding view.


public function index()
{
    return view('tasks.index');
}

Create a subfolder inside view called ‘tasks‘, and create a new blade file called ‘index.blade.php‘.


@extends('layouts.master')

@section('content')

<h1>Task List</h1>
<p class="lead">All your tasks. <a href="{{ route('tasks.create') }}">Add a new one?</a></p>
<hr>

@stop

Now you may take a look at ‘tasks’ page by click from navigation.

Database Migration

In Laravel to define the database we have to create migration file so that Laravel can create database tables with its field for us, by defining inside the file.

First we just use this command to generate a migration file that states we want to create a table named ‘tasks‘.


$ php artisan make:migration create_tasks_table --create=tasks

This will create a file inside database/migration. Open the migration file name ended with create_task_table.php.

We actually need to add more fields, so we have to edit the file to be like this inside the up() function.


public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->text('description');
        $table->timestamps();
    });
}

Run the migration with this command.


$ php artisan migrate

Notes: you might need to change the .env file and also refresh the cache.


$ php artisan cache:clear

If something bad happened we can rollback.


$ php artisan migrate:rollback

Models & Eloquent ORM

In Laravel model using Eloquent ORM that is used to communicate with database. Run this command to create a new model called Task.


$ php artisan make:model Task

This create a Task.php file inside app folder.

By default, we don’t need to specify which table we want to interact with because it’s automatically link to ‘tasks’ table. Notice that the model name is singular and start with capital letter, while table name is plural and all at lowercase. This is a name convention that we need to follow the ‘Laravel’ way.

In case you need to use custom table use this protected $table = 'custom_tasks';.

Creating a Record

We will edit the TasksController to support create() and store() functions.

But before doing that, we need to install a dependancy called LaravelCollective to effectivly display a form for our views.

Installing LaravelCollective Dependancy

Begin by installing this package through Composer. Run the command.


$ composer require "laravelcollective/html":"^5.2.0"

Next, add your new provider to the providers array of config/app.php.


  'providers' => [
    // ...
    Collective\Html\HtmlServiceProvider::class,
    // ...
  ],

Finally, add two class aliases to the aliases array of config/app.php.


  'aliases' => [
    // ...
      'Form' => Collective\Html\FormFacade::class,
      'Html' => Collective\Html\HtmlFacade::class,
    // ...
  ],

Create a Task

Let’s create edit create() function inside TasksController.


public function create()
{
    return view('tasks.create');
}

Create a view file ‘tasks/create.blade.php‘.


@extends('layouts.master')

@section('content')

<h1>Add a New Task</h1>
<p class="lead">Add to your task list below.</p>
<hr>

{!! Form::open([
    'route' => 'tasks.store'
]) !!}

<div class="form-group">
    {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!}
    {!! Form::text('title', null, ['class' => 'form-control']) !!}
</div>

<div class="form-group">
    {!! Form::label('description', 'Description:', ['class' => 'control-label']) !!}
    {!! Form::textarea('description', null, ['class' => 'form-control']) !!}
</div>

{!! Form::submit('Create New Task', ['class' => 'btn btn-primary']) !!}

{!! Form::close() !!}

@stop

AA

At TasksController header. Add this lines of declaration.


use App\Task;
use Session;

To save the task into the database to work, we have to edit store() function. This will take all the input from the form and mapping into Task model automatically.


public function store(Request $request)
{

  $input = $request->all();
  Task::create($input);
  return redirect()->back();
  
}

We are almost ready to save into the database, but still have some issue to do that because of MassAssignmentException.
We have to edit our Task model with $fillable array like this.


class Task extends Model {

    protected $fillable = [
        'title',
        'description'
    ];

}

Optionally we also can do this as well to be guarded from nothing. This is preferrable if you don’t remember all the fields.


class Task extends Model {

    protected $guarded = [];
    
}

This will allows these fillable items to be saved into the database. Now you may try the app to some tasks. You may open the MySQL editing app like Sequel Pro or PHPMyAdmin to verify if the database is properly stored.

Validate, showing Error and Success

We will validate the request that come from the form with specified rules. For example, if we want the field always being filled, we set it as ‘required’. So it will show an error if we left it blank, and it will not be saved on the database.

We have to alert to the user, so that they can fill up the field. We will update the view for tasks/create.blade.php. We add this code before the form code.


@if(Session::has('flash_message'))
  <div class="alert alert-success">
    {{ Session::get('flash_message') }}
  </div>
@endif

@if($errors->any())
  <div class="alert alert-danger">
    @foreach($errors->all() as $error)
      <p>{{ $error }}</p>
    @endforeach
  </div>
@endif

Now at TasksController, we will edit the store() function to be like this.


public function store(Request $request)
{
  $this->validate($request, [
    'title' => 'required',
    'description' => 'required'
  ]);

  $input = $request->all();

  Task::create($input);

  Session::flash('flash_message', 'Task successfully added!');

  return redirect()->back();
}

Reading Records

Inside TasksController let’s edit the index() function.


public function index()
{
   $tasks = Task::all();
   return view('tasks.index')->with('tasks',$tasks);
}

Now edit tasks/index.blade.php. Add this line of code after <hr> mark.


@foreach($tasks as $task)
    <h3>{{ $task->title }}</h3>
    <p>{{ $task->description}}</p>
    <p>
        <a href="{{ route('tasks.show', $task->id) }}" class="btn btn-info">View Task</a>
        <a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a>
    </p>
    <hr>
@endforeach

Show Single Item

Update TasksController for show() function.


public function show($id)
{
    $task = Task::find($id);
    return view('tasks.show')->with('task',$task);
}

Create a view tasks/show.blade.php.


@extends('layouts.master')

@section('content')

<h1>{{ $task->title }}</h1>
<p class="lead">{{ $task->description }}</p>
<hr>

<a href="{{ route('tasks.index') }}" class="btn btn-info">Back to all tasks</a>
<a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a>

<div class="pull-right">
    <a href="#" class="btn btn-danger">Delete this task</a>
</div>

@stop

S

Updating A Record

Update the TasksController edit() function.


public function edit($id)
{
    $task = Task::find($id);
    return view('tasks.edit')->with('task',$task);

}

Create a new view file at tasks/edit.blade.php.


@extends('layouts.master')

@section('content')

<h1>Edit Task - {{ $task->title }} </h1>
<p class="lead">Edit this task below. <a href="{{ route('tasks.index') }}">Go back to all tasks.</a></p>
<hr>

@if(Session::has('flash_message'))
  <div class="alert alert-success">
    {{ Session::get('flash_message') }}
  </div>
@endif

@if($errors->any())
  <div class="alert alert-danger">
    @foreach($errors->all() as $error)
      <p>{{ $error }}</p>
    @endforeach
  </div>
@endif

{!! Form::model($task, [
    'method' => 'PATCH',
    'route' => ['tasks.update', $task->id]
]) !!}

<div class="form-group">
    {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!}
    {!! Form::text('title', null, ['class' => 'form-control']) !!}
</div>

<div class="form-group">
    {!! Form::label('description', 'Description:', ['class' => 'control-label']) !!}
    {!! Form::textarea('description', null, ['class' => 'form-control']) !!}
</div>

{!! Form::submit('Update Task', ['class' => 'btn btn-primary']) !!}

{!! Form::close() !!}

@stop

To make the function edit possible let’s edit update() function at TasksController.


public function update(Request $request, $id)
{
    $task = Task::find($id);

    $this->validate($request, [
        'title' => 'required',
        'description' => 'required'
    ]);

    $input = $request->all();

    $task->fill($input)->save();

    Session::flash('flash_message', 'Task successfully updated!');

    return redirect()->back();
}

Screen Shot 2016-11-30 at 3.34.46 AM.png

Deleting A Record

Let’s update TasksController to support destroy() function.


public function destroy($id)
{
    $task = Task::find($id);

    $task->delete();

    Session::flash('flash_message', 'Task successfully deleted!');

    return redirect()->route('tasks.index');
    
}

Now we can call the destroy() route when we show the single task at tasks/show.blade.php. We have to make adjustment to the button like this.


@extends('layouts.master')

@section('content')

<h1>{{ $task->title }}</h1>
<p class="lead">{{ $task->description }}</p>
<hr>

<a href="{{ route('tasks.index') }}" class="btn btn-info">Back to all tasks</a>
<a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a>

<div class="pull-right">
    {!! Form::open([
            'method' => 'DELETE',
            'route' => ['tasks.destroy', $task->id]
        ]) !!}
            {!! Form::submit('Delete this task', ['class' => 'btn btn-danger']) !!}
        {!! Form::close() !!}
</div>

@stop

When the task is successfully deleted, we have sent a flash to the session but we cannot show it yet because there is no session flash at ‘tasks.index.blade.php’. So we have to add this after <hr> code at tasks/index.blade.php.


@if(Session::has('flash_message'))
    <div class="alert alert-success">
        {{ Session::get('flash_message') }}
    </div>
@endif

By doing this we will have a flash message ‘Task Successfully Deleted’ at the task list.

Summary

We just have created the super common feature on web app which is CRUDing data. We can use this for many kind of data, but for this example we use Task which has title and description. It’s looked simple, but can be daunting if you are new and stuck somewhere, possibly at integrating with database.

There is more to learn for field data verifiction. We can have other type of validation as well like checking how many character, is valid email etc.

So far we have been covered the simplest yet sweetest way to create CRUD web app with Laravel.

If you have any question or concern, please don’t hesistate to leave comment below.

Resources

Laravel Official
Laravel Collective
Laravel Tutorial

  • SM Nadim Uddin

    Thank you so much :) it’s like learning in person for beginner. Keep it up!

  • Ahsanul Muktadir Ahsan

    just loved what have you done….
    seriously after this work when the web page is running as i saw is fabulous. thank you so much and plz keep this up

  • Sanchit Mahajan

    Excellent… Thank You..Boss…