Laravel 6 Crud app

Laravel 6 Crud app

Github repo

https://github.com/emuroiwa/guestbook-laravel-acl-crud

Lets start

Almost tempted to do a Vue SPA, but no K.I.S.S. This is CRUD laravel guestbook app, with 2 user roles 1) Admin and 2) the user (guest).

I will be using spaite package this package allows you to manage user permissions and roles in a database.

Lets Code

  1. Install Laravel

We are going to install a fresh new laravel project

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

2. Laravel Auth

 Make auth in your laravel project.

composer require laravel/ui
php artisan ui bootstrap --auth
npm install
npm run dev

3. Install spaite package

composer require spatie/laravel-permission

once done add the service provider and aliase in config/app.php

'providers' => [

	....

	Spatie\Permission\PermissionServiceProvider::class,

],

We need to take care of our permissions

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Run the migrations, after setting up you DB details in the .env file

php artisan migrate

3. Models and Migrations

Create a message model and migration

php artisan make:model Message -m
<?php

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

class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->id();
            $table->string('message_title');
            $table->longText('message_body');
            $table->unsignedBigInteger('user_id')->unsigned();
            $table->timestamps();
            $table->foreign('user_id')
                    ->references('id')
                    ->on('users')
                    ->onDelete('cascade');
        });
    }

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

once again run the migration 🙂

php artisan migrate

Message Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    //
    protected $fillable = [
        'message_title', 'message_body', 'user_id'
    ];
}

Edit the User Model

Middleware and Spaite

Controllers

php artisan make:controller RoleController --resource
php artisan make:controller UserController --resource
php artisan make:controller MessageController --resource
php artisan make:controller MessageReplyController --resource

MessageController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Message;

class MessageController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    function __construct()
    {
         $this->middleware('permission:message-list|message-create|message-edit|message-delete', ['only' => ['index','show']]);
         $this->middleware('permission:message-create', ['only' => ['create','store']]);
         $this->middleware('permission:message-edit', ['only' => ['edit','update']]);
         $this->middleware('permission:message-delete', ['only' => ['destroy']]);
    }
    public function index()
    {
        //
        $messages = Message::orderBy('id','DESC')->get();
        return view('messages.index',compact('messages'))
            ->with($messages);
    }

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

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //validate
        request()->validate([
            'message_title' => 'required',
            'message_body' => 'required',
            'user_id' => 'required',
        ]);


        Message::create($request->all());
        return redirect()->route('messages.index')
                        ->with('success','Message sent successfully.');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        return view('messages.show',compact('message'));

    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        return view('messages.edit',compact('message'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        request()->validate([
            'message_title' => 'required',
            'message_body' => 'required',
            'user_id' => 'required',
        ]);

        $message->update($request->all());
        return redirect()->route('messages.index')
                        ->with('success','Message updated successfully');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $message->delete();
        return redirect()->route('messages.index')
                        ->with('success','Message deleted successfully');
    }
}

Message Reply Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\MessageReply;
use Auth;

class MessageReplyController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    function __construct()
    {
         $this->middleware('permission:reply-list|reply-create|reply-edit|reply-delete', 
         ['only' => ['index','show']]);
         $this->middleware('permission:reply-create', ['only' => ['create','store']]);
         $this->middleware('permission:reply-edit', ['only' => ['edit','update']]);
         $this->middleware('permission:reply-delete', ['only' => ['destroy']]);
    }
    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)
    {
        //validate
         request()->validate([
            'message_body' => 'required',
        ]);

        // get logged user-.id
        $user = Auth::user();

        MessageReply::create([
            'reply_body' => $request['message_body'],
            'user_id' => $user->id,
            'message_id' => $request['message_id']
        ]);

        return redirect()->back()
                ->with('success','Reply sent successfully.');
    }

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

    /**
     * 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)
    {
        request()->validate([
            'reply_body' => 'required',
        ]);
        
        $reply = MessageReply::findOrFail($id);
        $reply->update($request->all());
        return redirect()->back()
                ->with('success','Reply updated successfully');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $reply = MessageReply::findOrFail($id);
        $reply->delete();

        return redirect()->back()
                ->with('success','Reply deleted successfully');
    }
}

Laravel Seeder

Create Seeder For Permissions and AdminUser

php artisan make:seeder PermissionTableSeeder
php artisan make:seeder UserPermissionSeeder
php artisan make:seeder CreateAdminSeeder
php artisan make:seeder CreateUserSeeder
<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;

class PermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $permissions = [
            'role-list',
            'role-create',
            'role-edit',
            'role-delete',
            'message-list',
            'message-create',
            'message-edit',
            'message-delete'
         ];
    
         foreach ($permissions as $permission) {
              Permission::create(['name' => $permission]);
         }
    }
}

CreateAdminSeeder

<?php
  
use Illuminate\Database\Seeder;
use App\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
  
class CreateAdminSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $user = User::create([
        	'name' => 'Payfast Admin', 
        	'email' => 'admin@payfast.co.za',
        	'password' => bcrypt('payfast')
        ]);
  
        $role = Role::create(['name' => 'Admin']);
        $permissions = Permission::pluck('id','id')->all();
        $role->syncPermissions($permissions);
        $user->assignRole([$role->id]);
    }
}

User Seeder

<?php
  
use Illuminate\Database\Seeder;
use App\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
  
class CreateUserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $user = User::create([
        	'name' => 'Payfast User', 
        	'email' => 'user@payfast.co.za',
        	'password' => bcrypt('payfast')
        ]);
  
        $role = Role::create(['name' => 'User']);
        $permissions = Permission::pluck('id','id')
                        ->where('name', 'LIKE', '%message%')
                        ->orWhere('name','=',"reply-list")
                        ->get();
        $role->syncPermissions($permissions);
        $user->assignRole([$role->id]);
    }
}

User Permission Seeder

<?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;

class UserPermissionSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {

      $role = Role::firstOrCreate(['name' => 'User']);

      $role->syncPermissions([
        'message-list',
        'message-create',
        'message-edit',
        'message-delete',
        'reply-list',
      ]);
    }
}

Add Seeders to Database Seeder

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(PermissionTableSeeder::class);
        $this->call(UserPermissionSeeder::class);
        $this->call(CreateAdminSeeder::class);
        $this->call(CreateUserSeeder::class);

    }
}

Run Seeder

php artisan db:seed --class=DatabaseSeeder

Blade templates

Messages Blades

In your resources/views folder create a messages folder, then create these blade templates

  1. index.blade.php
  2. edit.blade.php
  3. create.blade.php
  4. show.blade.php

index.blade.php

@extends('layouts.app')


@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2>Inbox</h2>
            </div>
        </div>
    </div>

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

    <div class="card">
        <div class="card-header">
            @can('message-create')
                <a class="btn btn-success" href="{{ route('inbox.create') }}"> Create New message</a>
            @endcan
        </div>

        <div class="card-body">
            <table class="table table-striped">
                <tr>
                <th>Name</th>
                <th>Title</th>
                    <th width="280px">Action</th>
                </tr>
                @foreach ($messages as $message)
                <tr>
                <td>{{ $message->name }}</td>
                <td>{{ $message->message_title }}</td>
                    <td>
                        <form action="{{ route('inbox.destroy',$message->id) }}" method="POST">
                            <a class="btn btn-info" href="{{ route('inbox.show',$message->id) }}">Show</a>
                            @can('message-edit')
                            <a class="btn btn-primary" href="{{ route('inbox.edit',$message->id) }}">Edit</a>
                            @endcan


                            @csrf
                            @method('DELETE')
                            @can('message-delete')
                            <button type="submit" class="btn btn-danger">Delete</button>
                            @endcan
                        </form>
                    </td>
                </tr>
                @endforeach
            </table>
            {{ $messages->links() }}
        </div>
    </div>


@endsection

edit.blade.php

@extends('layouts.app')


@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2>Edit Message</h2>
            </div>
        </div>
    </div>


    @if ($errors->any())
        <div class="alert alert-danger">
            <strong>Error !</strong>  Check your input.<br><br>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif
    <div class="card">
        <div class="card-header">
            <a class="btn btn-primary" href="{{ route('inbox.index') }}"> Back</a>
        </div>

        <div class="card-body">

            <form action="{{ route('inbox.update',$message->id) }}" method="POST">
                @csrf
                @method('PUT')


                <div class="row">
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Title:</strong>
                            <input type="text" name="message_title" value="{{ $message->message_title }}" class="form-control" >
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Message:</strong>
                            <textarea class="form-control" style="height:150px" name="message_body" placeholder="Detail">{{ $message->message_body }}</textarea>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                    <button type="submit" class="btn btn-primary">Update</button>
                    </div>
                </div>


            </form>
        </div>
    </div>

@endsection

create.blade.php

<!-- extend layout -->
@extends('layouts.app')
<!-- content -->
@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2>Create New Message</h2>
            </div>
        </div>
    </div>


    @if ($errors->any())
        <div class="alert alert-danger">
            <strong>Error!</strong> Check your input.<br><br>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif


    <form action="{{ route('inbox.store') }}" method="POST">
    	@csrf
    <div class="card">
        <div class="card-header">
            <a class="btn btn-primary" href="{{ route('inbox.index') }}"> Back</a>
        </div>

        <div class="card-body">

            <div class="row">
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Title:</strong>
                        <input type="text" name="message_title" class="form-control" placeholder="Title">
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Message:</strong>
                        <textarea class="form-control" style="height:150px" name="message_body" placeholder="Message"></textarea>
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                        <button type="submit" class="btn btn-primary">Send</button>
                </div>
            </div>
        </div>
    </div>

    </form>


@endsection

show.blade.php

@extends('layouts.app')


@section('content')
    <div class="row">
        <div class="col-lg-12 margin-tb">
            <div class="pull-left">
                <h2> Message</h2>
            </div>
        </div>
    </div>
    @if ($message = Session::get('success'))
        <div class="alert alert-success">
            <p>{{ $message }}</p>
        </div>
    @endif
    @if(isset($messages))

        <div class="card">
            @can('reply-create')
                <div class="card-header">
                    <a class="btn btn-primary" data-toggle="modal" data-target="#myModal"> Reply</a>
                </div>
            @endcan
            <div class="card-body">
                <div class="row">
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Title:</strong>
                            <p>{{ $messages->message_title }}</p>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Body:</strong>
                            <p>{{ $messages->message_body }}</p>
                        </div>
                        <small>{{ $messages->created_at }}</small>
                    </div>
                </div>
            </div>
        </div>
    @endif
    @if(isset($replies))
        @foreach($replies as $reply)
            <div class="card mt-2">
                <div class="card-body">
                    <label class="badge badge-info">{{ $reply->name }}</label>
                    <p> {{ $reply->reply_body }} </p>
                    <small>{{ $reply->created_at }}</small>

                </div>
                <div class="card-footer">
                    <form action="{{ route('reply.destroy',$reply->id) }}" method="POST">
                         @csrf
                        @method('DELETE')
                        @can('reply-delete')
                            <button type="submit" class="btn btn-danger">Delete</button>
                        @endcan
                    </form>
                </div>
            </div>
        @endforeach
    @endif
    <!-- Modal -->
    <div id="myModal" class="modal fade" role="dialog">
        <div class="modal-dialog modal-lg">

            <!-- Modal content-->
            <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Reply</h4>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
                
            </div>
            
            <form action="{{ route('reply.store') }}" method="POST">
                @csrf
                <div class="modal-body">
                    <div class="form-group">
                            <strong>Message:</strong>
                            <textarea class="form-control" style="height:150px" name="message_body" placeholder="Message" required></textarea>
                            {{ Form::hidden('message_id', $messages->id, array('id' => 'message_id')) }}
                        </div>
                </div>
                <div class="modal-footer">
                    <button type="submit" class="btn btn-primary">Send</button>
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                </div>
            </form>
            </div>

        </div>
    </div>
@endsection

Lets test it out for a minute

php artisan serve

Leave a Reply

Close Menu