Final code

Here is the final code of this second lab: todos.zip

Table des matières

1. Install a fresh copy

  1. Start a DOS prompt
  2. Make sure Laravel is correctly install: type Laravel -V, if you see the Laravel Installer’s version number (f.i. 2.0.1), it’s fine.
  3. Install a fresh Laravel website: go to your public_html folder (i.e. where you wish create your website, can be c:\development\my_sites too if configured like this on your machine).
  4. At the prompt level, run laravel new a_folder_name (f.i. laravel new app_todos)
  5. Once the installation is done, go in that folder: type cd app_todos (i.e. your folder)
  6. Grab a copy of todos.zip and unzip the file (under DOS "C:\Program Files\7-Zip\7z.exe" x todos.zip -aoa i.e. extract with full path and overwrite existing files)
  7. Retrieve the name of the database to create:
    1. Open with a text editor the file called .env present in the root of app_todos
    2. Look to the DB_DATABASE variable, you’ll find the name of the database for the application (by default, it’ll be todos)
  8. Create a database with that name (todos) in your mySQL
    1. At the prompt level, type mysql -u root -p (where root is the username)
    2. When asking the password, just press enter (there is no password)
    3. In the MySQL prompt, type CREATE DATABASE todos; and press Enter
    4. Type quit for leaving MySQL
  9. Start the migration: php artisan migrate:install
  10. Create tables: php artisan migrate:fresh
  11. Add the authentication layer: php artisan make:auth
  12. Add fake data: php artisan todos:populate
  13. Run composer require "laravelcollective/html"
  14. This done, start artisan by starting php artisan serve on the DOS prompt.
  15. With your browser go to http://127.0.0.1:8000/login and make a login with christophe@todos.com for the email while admin is the password.
  16. Then we can use the application: http://127.0.0.1:8000

2. Code source

2.1. env

Our environment file.

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:SS+CZVwEkBRyzZQTxpogAroaFSbazMf+DtE9J0vftA8=
APP_DEBUG=true
APP_URL=http://127.0.0.1

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todos
DB_USERNAME=root
DB_PASSWORD=

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

2.2. app

2.2.1. app/Todo.php

Our model, layer for our todos table.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Todo extends Model
{
    // Name of our table in the database
    protected $table = 'todos';

    // Only if $table->timestamps() was mentioned in the
    // CreateTodosTable class; set to False if not mentioned
    public $timestamps = true;

    // List of columns that we can update
    public $fillable = ['title', 'completed', 'description'];

    /**
     * Extend the model and offer a convenient way to retrieve the
     * user linked to our todo (thanks the user_id foreign key and
     * his link with the users table)
     *
     * @return void
     */
    public function user()
    {
        return $this->belongsTo('App\User', 'user_id', 'id');
    }
}

2.2.2. app/Console/Commands/clear.php

Our artisan command for clear tables

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class clear extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'todos:clear';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Clear todos app tables';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        \DB::table('todos')->truncate();

        \DB::table('users')->where('email', 'like', '%@todos.com')->delete();

        $this->info('>> Todos - Clear done <<');
    }
}

2.2.3. app/Console/Commands/populate.php

Our artisan command for populating tables

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Faker;

class populate extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'todos:populate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Add fake data\'s to tables';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // First clear previous content
        $this->call('todos:clear');

        // Ask how many items to create, default 20
        $wNbr = $this->ask('How many todos do you want to create?', 20);

        // Create user Christophe
        \DB::table('users')->insert([[
            'name' => 'Christophe',
            'email' => 'christophe@todos.com',
            'password' => bcrypt('admin')
        ]]);

        // Getting the ID of the user Christophe
        $user_id = \DB::table('users')->where('name', 'Christophe')->take(1)->value('id');

        // Use faker to get french dummy text
        // If needed, just run "composer require fzaninotto/faker" in
        // a DOS prompt
        $faker = Faker\Factory::create('fr_FR');

        // Insert a few items for him
        for ($i = 0; $i < $wNbr; $i++) {
            \DB::table('todos')->insert([
                [
                    'title' => $faker->sentence($nbWords = 6, $variableNbWords = true) .
                        ' (todo #' . ($i + 1) . ')',
                    'description' => $faker->realText($maxNbChars = 1000),
                    'user_id' => $user_id,
                    'completed' => $faker->boolean(),
                    'created_at' => now(),
                    'updated_at' => now()
                ]
            ]);
        }

        $this->info('>> Todos - Populating done <<');
    }
}

2.2.4. app/Http/Controllers/TodoController.php

Implements the code to run for each route.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests\TodoRequest;
use App\Repositories\TodoRepositoryInterface;
use App\Repositories\TodoRepository;
use Response;

class TodoController extends Controller
{
    protected $todoRepository;

    protected $nbrPerPage = 5;

    public function __construct(TodoRepositoryInterface $todoRepository)
    {
        $this->todoRepository = $todoRepository;

        // Methods in the controller needs to be logged in
        // Except getting the list of todos (method index) and showing
        // one of them (method show).
        // All other methods requires a valid login.
        $this->middleware('auth', ['except' => ['index', 'show']]);
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        // Retrieve all todos
        $datas = $this->todoRepository->getPaginate($this->nbrPerPage);
        $links = $datas->render();

        // call the index.blade.php view and pass the data
        return view('index', compact('datas', 'links'));
    }

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

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(TodoRequest $request)
    {
        $todo = $this->todoRepository->store($request->all());

        return redirect()->route('todos.show', ['id' => $todo->id])->withOk('Todo has been successfully created');
    }

    /**
     * Display the specified resource.
     *
     * @param  int                       $id
     * @return \Illuminate\Http\Response
     */
    public function show(int $id)
    {
        $data = $this->todoRepository->getById($id);

        return view('show', compact('data'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int                       $id
     * @param  TodoRepositoryInterface   $todoRepository
     * @return \Illuminate\Http\Response
     */
    public function edit(int $id)
    {
        $data = $this->todoRepository->getById($id);

        return view('edit', compact('data'));
    }

    /**
     * Update the specified resource in storage.
     * This function answer to the PUT method (updating an existing record)
     *
     * @param  TodoRequest               $request
     * @param  int                       $id
     * @return \Illuminate\Http\Response
     */
    public function update(TodoRequest $request, int $id)
    {
        $this->todoRepository->update($id, $request->all());

        // Redirect to the edit form
        return redirect()->route('todos.edit', ['id' => $id])->withOk('Successfully updated');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int                       $id
     * @param  TodoRepositoryInterface   $todoRepository
     * @return \Illuminate\Http\Response
     */
    public function destroy(Request $request, int $id)
    {
        $this->todoRepository->destroy($id);

        if (!$request->ajax()) {
            return redirect()->route('todos.index')->withOk('Successfully removed');
        } else {
            // Return a JSON string that will be used in an Ajax request
            return Response::json([
                'status' => true,
                'message' => 'Successfully deleted'
            ]);
        }
    }
}

2.2.5. app/Http/Requests/TodoRequest.php

Define who can use our Todo records and specify which rules should be applied to records.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TodoRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|string|max:100',
            'completed' => 'boolean'
        ];
    }
}

2.2.6. app/Providers/AppServiceProvider.php

Since we need to bind our interface and our repository.

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Schema::defaultStringLength(191);
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'App\Repositories\TodoRepositoryInterface',
            'App\Repositories\TodoRepository'
        );
    }
}

2.2.7. app/Repositories/TodoRepositoryInterface.php

Defines the functions that are supported by the TodoRepository i.e. the data logic.

<?php

namespace App\Repositories;

use App\Todo;
use Illuminate\Database\Eloquent\Collection;
use App\Http\Requests\TodoRequest;

/**
 * Define the function that should be available through the
 * TodoRepository
 */
interface TodoRepositoryInterface
{
    /**
     * Get a pagination
     *
     * @param  int        $n
     * @return Collection
     */
    public function getPaginate(int $n);

    /**
     * Get the list of records of the table
     *
     * @return Collection
     */
    public function index() : Collection;

    /**
     * Get a specific todo; identified by his ID
     *
     * @param  int  $id
     * @return Todo
     */
    public function getById(int $id) : Todo;

    /**
     * Save the todo
     *
     * @param  array $inputs Submitted datas
     * @return void
     */
    //public function store(TodoRequest $todo) : Todo;
    public function store(array $inputs) : Todo;

    /**
     * Remove the specified todo
     *
     * @param  int  $id
     * @return void
     */
    public function destroy(int $id);

    /**
     * Update the specified todo, update columns
     *
     * @param  int   $id
     * @param  array $inputs Submitted datas
     * @return void
     */
    public function update(int $id, array $inputs) : Todo;
}

2.2.8. app/Repositories/TodoRepository.php

Implements functions for our data logic.

<?php

namespace App\Repositories;

use Illuminate\Database\Eloquent\Collection;
use App\Todo;
use Auth;

/**
 * Implements functions for working with the todos table
 */
class TodoRepository implements TodoRepositoryInterface
{
    protected $todo;

    public function __construct(Todo $todo)
    {
        $this->todo = $todo;
    }

    public function getPaginate(int $n)
    {
        return $this->todo->paginate($n);
    }

    /**
     * Get the list of records of the table
     *
     * @return Collection
     */
    public function index() : Collection
    {
        return $this->todo->all();
    }

    /**
     * Get a specific todo; identified by his ID
     *
     * @param  int  $id
     * @return Todo
     */
    public function getById(int $id) : Todo
    {
        return $this->todo->where('id', $id)->firstOrFail();
    }

    private function save(Todo $todo, array $inputs) : Todo
    {
        $todo->title = $inputs['title'];
        $todo->description = $inputs['description'];

        $todo->save();

        return $todo;
    }

    /**
     * Save the todo
     *
     * @param  array $inputs List of fields present in the creation form
     * @return Todo
     */
    public function store(array $inputs) : Todo
    {
        $todo = new $this->todo();

        $todo->completed = false;
        $todo->user_id = Auth::user()->id;

        return $this->save($todo, $inputs); // Save the submitted data
    }

    /**
     * Remove the specified todo
     *
     * @param  int  $id
     * @return void
     */
    public function destroy(int $id)
    {
        $this->todo->destroy($id);
    }

    /**
     * Update the specified todo, update columns
     *
     * @param  int   $id
     * @param  array $inputs Submitted datas
     * @return void
     */
    public function update(int $id, array $inputs) : Todo
    {
        return $this->save($this->getById($id), $inputs);
    }
}

2.3. database

2.3.1. database/migrations/create_todos_table.php

Define the structure of the todos table.

<?php

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

class CreateTodosTable extends Migration
{
    /**
     * The migration is running
     * Define a foreign key between user_id and the users table
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            // Our primary key
            $table->increments('id');

            // Allow Eloquent to add two fields and managed them:
            // created_at and updated_at
            $table->timestamps();

            $table->string('title', 100);
            $table->boolean('completed')->default(0);
            $table->text('description', 1000)->nullable();

            // The author of the record
            $table->integer('user_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
    }

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

2.4. resources

2.4.1. resources/views/create.blade.php

The form for adding a new todo.

@extends('master')

@section('content')

  {{-- Display the name of the connected user --}}
  <div class="panel-heading">Hi {{ Auth::user()->name }}, please add your new Todo below</div>  

  <div class="panel-body">   

    {{-- 
      Messages sent by the controller like in
        return view('...')->with('message', '<div class="...">success</div>'))
      will be displayed here
    --}}
    @if(Session::has('message'))
      {!! Session::get('message') !!}
    @endif

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

    <div class="form-group {!! $errors->has('todo') ? 'has-error' : '' !!}"> 
        {{--
          Output the "Title" entry.
        --}}
        {!! Form::text('title', '', array('size' => '100', 'class' => 'form-control', 'placeholder' => 'Enter Todo\'s title')) !!}
        {!! $errors->first('title', '<div class="alert alert-danger">:message</div>') !!}
      </div>

      <div class="form-group">
        {{--
          Output the "Completed" flag.
        --}}      
        {!! Form::checkbox('completed', 1, 0)  !!}
        {!! Form::label('completed', 'Completed'); !!}
      </div>

      <div class="form-group {!! $errors->has('todo') ? 'has-error' : '' !!}">
        {{--
          Output the "Description" textarea.
        --}}
        {!! Form::label('description', 'Description (optional)'); !!}
        {!! Form::textarea('description', '', array('class' => 'form-control')) !!}
      </div>

      {{--
        Output the submit button
      --}}
      {!! Form::submit('Submit !', array('class' => 'btn btn-sm btn-primary')) !!}

      <a href="javascript:location.href='{{ route('todos.index') }}'" class="btn btn-sm btn-success">
        <span class="glyphicon glyphicon-circle-arrow-left"></span> Back
      </a> 
    {!! Form::close() !!}
  </div>
@endsection

@section('navigation')
@endsection

2.4.2. resources/views/edit.blade.php

The form for the edition of an existing todo.

@extends('master')

@section('content')

  <div class="panel-heading">Need to edit a Todo?</div>  

  <div class="panel-body">   

    {{-- 
      Messages sent by the controller like in
        return view('...')->with('message', '<div class="...">success</div>'))
      will be displayed here
    --}}
    @if(Session::has('message'))
      {!! Session::get('message') !!}
    @endif

    {!! Form::model($data, ['route' => ['todos.update', $data->id], 'method' => 'PUT']) !!}

      <div class="form-group {!! $errors->has('todo') ? 'has-error' : '' !!}"> 
        {{--
          Output the "Title" entry.
        --}}
        {!! Form::text('title', null, array('size' => '100', 'class' => 'form-control', 'placeholder' => 'Enter Todo\'s title')) !!}
        {!! $errors->first('title', '<div class="alert alert-danger">:message</div>') !!}
      </div>

      <div class="form-group">
        {{--
          Output the "Completed" flag.
        --}}      
        {!! Form::checkbox('completed', 1, null)  !!}
        {!! Form::label('completed', 'Completed'); !!}
      </div>

      <div class="form-group {!! $errors->has('todo') ? 'has-error' : '' !!}">
        {{--
          Output the "Description" textarea.
        --}}      
        {!! Form::label('description', 'Description (optional)'); !!}
        {!! Form::textarea('description', null, array('class' => 'form-control')) !!}
      </div>

      {{--
        Output the submit button
      --}}      
      {!! Form::submit('Submit !', array('class'=> 'btn btn-sm btn-primary')) !!}

      @include('buttons.back')

      {!! Form::close() !!}
  </div>
@endsection

@section('navigation')  
@endsection

2.4.3. resources/views/index.blade.php

Display the list of todos, like in a blog.

@extends('master')

@section('content')

    @if(session()->has('ok'))
    <div class="alert alert-success alert-dismissible">{!! session('ok') !!}</div>
  @endif

  {{--
    $data is a collection of records
  --}}
  @isset($datas)
    {{--
      For each todo, we'll show his title in a H3
      Clicking on the title will display the todo's details
      We'll also show: todo's description and his author.
    --}}
    @foreach($datas as $data)
      <h3><a href="{{ route('todos.show', ['id' => $data->id]) }}">#{{ $loop->iteration. " - " . $data->title }}</a></h3>

      <div style="padding:5px 0 15px 0;">
        @include('buttons.show')
        @auth 
          @include('buttons.edit')
          @include('buttons.delete')
        @endauth 
      </div>
      <p>{{ $data->description }}</p>
      <small>Author: {{ $data->user->name }}</small>
      <hr/>
    @endforeach
    <small>Number of todos: {{ count($datas) }}</small>
  @endisset
@endsection

@section('navigation')
  {{ $links }}
  <hr/>
  @include('buttons.add')

@endsection

2.4.4. resources/views/master.blade.php

Our page’s master. Defines how pages should looks like.

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  {{--
    Important for our Ajax requests: we need to protect our server's requests
    with the generated session token
  --}}
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Some stupid Todos application</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  <style>
    textarea { max-height:150px; }
  </style>
</head>
<body>
  @include('navbar') 
  <main role="main">  
    <div class="jumbotron">
      <div class="container">
        <h1 class="display-3">Some stupid Todos application</h1>
        <small>A simple Laravel application, learning purposes</small>
      </div>
    </div>
  </main>
  <div class="container">
    @yield('content')
    <hr/>
    @yield('navigation')
  </div>
  @yield('script')
</body>
</html>

2.4.5. resources/views/show.blade.php

Show the detail of a todo. If a valid user is logged-in, display action’s buttons.

@extends('master')

@section('content')

  <div class="msg hide alert alert-success alert-dismissible">&nbsp;</div>

  {{-- 
    Display the detail of a todo; make sure we've one
  --}}
  @isset($data)
    {{-- 
      Show information's like title, description and timestamps
    --}}
    <h3>{{ $data->title }}</h3>
    <p>{{ $data->description }}</p>
    <small>
      Created at: {{ $data->created_at }}
      <br/>
      Last updated: {{ $data->updated_at }}
      <br/>

      {{--
        $data->user isn't a column but, in our model, the user() function
        returns an object which represent a record of the users table.
        So, through $data->user we can access to the user's name, email, ...
      --}}
      Author: {{ $data->user->name }}
    </small>

  @endisset

@endsection

@section('navigation')
  @include('buttons.back')
  @auth 
    <span class="buttons">
      @include('buttons.edit')
      @include('buttons.delete')
    </span>
  @endauth
@endsection

@section('script')
{{--
  Add our script for our buttons
--}}
<script defer="defer">
  $('.delete, .edit').click(function(){

    if ($(this).hasClass('delete')) {

      // Add the csrf-token protection but only when the request is 
      // made on the same site (no cross-domain). 
      // Don't share the token outside
      $.ajaxSetup({
        beforeSend: function(xhr, type) {
          if (!type.crossDomain) {
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
          }
        }
      });

      // By clicking on the delete button, make an Ajax request
      $.ajax({
        url: '{{ route('todos.destroy', $data->id) }}',
        type: 'DELETE',
        contentType: 'application/json',
        success: function (data) {
          if (data.hasOwnProperty("message")) {
            // Show the message
            $('.msg').html(data.message).removeClass('hide');
            // And remove buttons.
            $('.buttons').remove();
            // The back button should refresh the page so don't 
            // use history.back() anymore
            $('.back').attr("href", "{{ route('todos.index') }}");
          }
        },
        error: function (data, textStatus, errorThrown) {
          console.log(data);
        }
      });
    } else {
      // The user has clicked on the edit button, redirect the browser
      // to the edit page
      window.location.replace('{{ route('todos.edit', ['id' => $data->id]) }}');
    }
});
</script>
@endsection

2.4.6. resources/views/buttons/add.blade.php

Add a new item button.

<button type="submit" class="btn btn-sm btn-primary" 
  onclick="location.href='{{ route('todos.create') }}'">
  <i class="far fa-plus-square"></i> Add new item
</button>

2.4.7. resources/views/buttons/back.blade.php

Go back button.

<a href="javascript:history.back()" class="back btn btn-sm btn-success">
    <i class="far fa-hand-point-left"></i> Back
</a>

2.4.8. resources/views/buttons/delete.blade.php

Delete an item button.

{!! Form::open(['method' => 'DELETE', 'route' => ['todos.destroy', $data->id], 'style' => 'display:inline']) !!}

<button type="submit" class="btn btn-sm btn-danger delete" onclick="return confirm('Remove this record?')">
    <i class="far fa-trash-alt"></i> Delete
</button>

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

2.4.9. resources/views/buttons/edit.blade.php

Edit an existing item button.

<button type="submit" class="btn btn-sm btn-primary" 
    onclick="location.href='{{ route('todos.edit', ['id' => $data->id]) }}'">
    <i class="far fa-edit"></i> Edit
</button>

2.4.10. resources/views/buttons/show.blade.php

Show button, go to the detail page.

<button type="submit" class="btn btn-sm btn-success" 
    onclick="location.href='{{ route('todos.show', ['id' => $data->id]) }}'">
    <i class="far fa-eye"></i> Show
</button>

2.5. routes

2.5.1. routes/web.php

Definition of our routes and how to manage them.

<?php

// Enable authentication routes
Auth::routes();

// Register http://127.0.0.1:8000/login and display the login screen
Route::get('home', 'HomeController@index')->name('home');

// Register the http://127.0.0.1:8000/logout and logout
Route::get('logout', ['as' => 'logout', 'uses' => 'Auth\LoginController@logout']);

// -------------------

// Attach the controller to every action(GET, HEAD, POST, DELETE, ...)
// for the "todos" resource. Use "php artisan route:list" to retrieve all
// routes created by this Route::resource() statement.
Route::resource('todos', 'TodoController');

// Default homepage : show the list of todos
Route::get('/', 'TodoController@index');

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');