WordPress-Like Blog Laravel 5.7 and AdminLTE 3 (15) – Managing Categories
In this fifteenth part of creating WordPress-Like Blog using Laravel 5.7 and AdminLTE 3, we will :
- Add Category Menu
- Display Categories
- Add Category
- Edit Category
- Delete Category
Add Category Menu
To add category menu on the sidebar, let’s modify sidebar.blade.php inside resources/views/layouts/backend directory.
1 2 3 4 5 6 7 8 |
<li class="nav-item has-treeview"> <a href="{{ route('backend.category.index') }}" class="nav-link"> <i class="nav-icon fa fa-pencil"></i> <p> Category </p> </a> </li> |
Since the route doesn’t exist, let’s create it inside routes/web.php :
1 |
Route::resource('/backend/category', 'Backend\CategoryController',['as'=>'backend']); |
Display Categories
Create CategoryController from terminal :
1 |
php artisan make:controller Backend/CategoryController --resource |
Change the parent class to BackendController
1 |
class CategoryController extends BackendController |
Add these lines to the index() method :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use App\Category; class CategoryController extends BackendController { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $categories = Category::with('posts')->orderBy('title')->paginate($this->limit); $categoriesCount = Category::count(); return view ("backend.category.index", compact('categories','categoriesCount')); } |
Go to resources/views/backend directory, add a new folder ‘category’. Add index.blade.php inside this category.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
@extends('layouts.backend.main') @section('title', 'Laravel 5 Blog | Category Index') @section('content') <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Category <small style="font-size: 15px">Display All Categories</small></h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="{{ url('/home') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url('/blog.index') }}">Category</a></li> <li class="breadcrumb-item active">All Categories</li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <section class="content"> <div class="container-fluid"> <div class="row"> <div class="col-md-12"> <div class="card"> <!-- /.card-header --> <div class="card-header"> <h3 class="card-title">Categories</h3> <div class="card-tools"> <div class="input-group input-group-sm" style="width: 150px;"> <input type="text" name="table_search" class="form-control float-right" placeholder="Search"> <div class="input-group-append"> <button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button> </div> </div> </div> </div> <!-- /.card-header --> <div class="card-body p-1"> <div class="row"> <div class="col-md-6" style="padding-left: 10px; padding-right: 30px; padding-top: 10px; padding-bottom: 10px; "> <a href="{{ route('backend.category.create') }}" class="btn btn-info float-left"> <span> <i class="fa fa-plus-circle"></i> <span> Add Post </span> </span> </a> </div> <div class="col-md-6" style="padding-left: 10px; padding-right: 30px; padding-top: 10px; padding-bottom: 10px; "> <div class="pull-right"> </div> </div> </div> @include('backend.partials.message') @if(!$categories->count()) <div class="alert alert-danger"> <strong>No Record Found</strong> </div> @else @include('backend.category.table') @endif </div> <div class="card-footer clearfix"> <div class="pull-left" id="pagination"> {{ $categories->appends( Request::query() )->render() }} </div> <div class="pull-right"> <?php $categoriesCount = $categories->count();?> <small style="padding-right: 25px;">{{ $categoriesCount }} out of {{ $categoriesCount }} {{ str_plural('Items', $categoriesCount) }}</small> </div> </div> </div> <!-- /.card --> </div> </div> <!-- /.row --> </div><!-- /.container-fluid --> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> @endsection @section('script') <script type="text/javascript"> $('#pagination').addClass('no-margin pagination-sm'); </script> @endsection |
Inside resources/views/backend, create another directory, ‘partials’, to put all files used by all the views so we don’t need to duplicate it everytime we create another view.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@if(session('message')) <div class="alert alert-info"> {{ session('message') }} </div> @elseif(session('trash-message')) <?php list($message, $postId) = session('trash-message') ?> {!! Form::open(['method'=>'PUT', 'route' => ['backend.blog.restore', $postId]]) !!} <div class="alert alert-info"> {{ $message }} <button type="submit" class="btn btn-sm btn-warning"><i class="fa fa-undo"></i>Undo</button> </div> {!! Form::close() !!} @endif |
Add table.blade.php inside category folder :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<table class="table table-striped"> <tr> <th width="10%">Action </th> <th>Category Name</th> <th width="20%" class="text-center">Post Count</th> </tr> @foreach($categories as $category) <tr> <td> {!! Form::open(['method' => 'DELETE', 'route' => ['backend.category.destroy', $category->id] ]) !!} <a href="{{ route('backend.category.edit', $category->id) }}" class="btn btn-sm btn-default"><i class="fa fa-edit"></i></a> <button onclick="return confirm('Are you sure to delete?')" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-times"></i></button> {!! Form::close() !!} </td> <td>{{ $category->title }}</td> <td align="center">{{ $category->posts->count() }}</td> </tr> @endforeach </table> |
Open the category index page :
Add Category
Add new method create() to CategoryController.php
1 2 3 4 5 6 |
public function create() { $category = new Category(); return view("backend.category.create", compact('category')); } |
Add create.blade.php inside resources/views/backend/category folder :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
@extends('layouts.backend.main') @section('title', 'Laravel 5 Blog | Add New Category') @section('content') <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Category <small style="font-size: 15px">Add New Category</small></h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="{{ url('/home') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url('/blog.index') }}">Category</a></li> <li class="breadcrumb-item active">Add new</li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <section class="content"> <div class="container-fluid"> <div class="row"> <div class="col-md-9"> {!! Form::model($category, [ 'method' => 'POST', 'route' => 'backend.category.store', 'id' => 'category-form' ])!!} @include('backend.category.form') {!! Form::close() !!} </div> </div> <!-- /.row --> </div><!-- /.container-fluid --> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> @endsection @include('backend.category.script') |
Inside the same directory, add form.blade.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<div class="card"> <!-- /.card-header --> <div class="card-header"> <h3 class="card-title">Add New Category</h3> <div class="card-tools"> <div class="input-group input-group-sm" style="width: 150px;"> <input type="text" name="table_search" class="form-control float-right" placeholder="Search"> <div class="input-group-append"> <button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button> </div> </div> </div> </div> <!-- /.card-header --> <div class="card-body p-1"> <div class="form-group {{ $errors->has('title') ? 'has-error' : ''}}"> {!! Form::label('title') !!} {!! Form::text('title', null, ['class'=>'form-control']) !!} @if($errors->has('title')) <span class="badge badge-danger">{{ $errors->first('title') }}</span> @endif </div> <div class="form-group {{ $errors->has('slug') ? 'has-error' : ''}}"> {!! Form::label('slug') !!} {!! Form::text('slug', null, ['class'=>'form-control']) !!} @if($errors->has('slug')) <span class="badge badge-danger">{{ $errors->first('slug') }}</span> @endif </div> <div class="pull-left"> <button type="submit" class="btn btn-primary">{{ $category->exists ? 'Update' : 'Save'}}</button> <a href="{{ route('backend.category.index') }}" class="btn btn-default">Back</a> </div> </div> </div> <!-- /.card --> </div> |
Also add script.blade.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@section('script') <script type="text/javascript"> $('#pagination').addClass('no-margin pagination-sm'); $('#title').on('blur',function(){ var theTitle = this.value.toLowerCase().trim(), slugInput = $('#slug'); theSlug = theTitle.replace(/&/g, '-and') .replace(/[^a-z0-9-]+/g,'-') .replace(/\-\-+/g, '-') .replace(/^-+|-+$/g, ''); slugInput.val(theSlug); }); </script> @endsection |
Next, we need to deal with store() method inside CategoryController.php, previously, let’s create another request, CategoryStoreRequest and CategoryUpdateRequest :
1 2 |
php artisan make:request CategoryStoreRequest php artisan make:request CategoryUpdateRequest |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CategoryStoreRequest 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|unique:categories|max:255', 'slug' => 'required|unique:categories|max:255', ]; } } |
Store method on CategoryController :
1 2 3 4 5 6 |
public function store(Requests\CategoryStoreRequest $request) { Category::create($request->all()); return redirect("/backend/category")->with('message', 'A New Category has been added'); } |
Don’t forget to add namespace :
1 |
use App\Http\Requests; |
Add fillable and table property inside App\Category model :
1 2 3 |
protected $fillable = ['title','slug']; protected $table = 'categories'; |
Now you can add a new category :
Add a new Category, ‘Uncategorized’. This category will be useful later when we delete any category.
Edit Category
To edit the category, modify edit() method inside CategoryController :
1 2 3 4 5 6 |
public function edit($id) { $category = Category::findOrFail($id); return view("backend.category.edit", compact('category')); } |
Modify CategoryUpdateRequest :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CategoryUpdateRequest 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|unique:categories|max:255,title,' . $this->route('category'), 'slug' => 'required|unique:categories|max:255,slug,' . $this->route('category'), ]; } } |
Add some modification to update() method inside CategoryController :
1 2 3 4 5 6 |
public function update(Requests\CategoryUpdateRequest $request, $id) { Category::findOrFail($id)->update($request->all()); return redirect("/backend/category")->with('message', 'The Category has been updated'); } |
Add edit.blade.php inside backend/category folder :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
@extends('layouts.backend.main') @section('title', 'Laravel 5 Blog | Edit New Category') @section('content') <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0 text-dark">Blog <small style="font-size: 15px">Edit Category</small></h1> </div><!-- /.col --> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="{{ url('/home') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url('/blog.index') }}">Blog</a></li> <li class="breadcrumb-item active">Edit Category</li> </ol> </div><!-- /.col --> </div><!-- /.row --> </div><!-- /.container-fluid --> </div> <!-- /.content-header --> <!-- Main content --> <section class="content"> <div class="container-fluid"> <div class="row"> <div class="col-md-9"> {!! Form::model($category, [ 'method' => 'PUT', 'route' => ['backend.category.update', $category->id], 'id' => 'category-form' ])!!} @include('backend.category.form') {!! Form::close() !!} </div> </div> <!-- /.row --> </div><!-- /.container-fluid --> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> @endsection @include('backend.category.script') |
Now you will be able to edit the category name :
Delete Category
To delete a category, modify the destroy() method inside CategoryController :
1 2 3 4 5 6 |
public function destroy($id) { Category::findOrFail($id)->delete(); return redirect("/backend/category")->with('message', 'The Category has been deleted'); } |
However, we wouldn’t be able to delete this category since it’s restricted. That’s why we made one ‘uncategorized’ category before, so the post that have the same category with the deleted category will be automatically under uncategorized category.
Let’s protect the uncategorized category on the client and server side so it couldn’t be deleted.
In my categories table, uncategorized has an id ‘7’. Open config/cms.php
1 |
'default_category_id' => 7, |
Next, create a new request, CategoryDestroyRequest
1 |
php artisan make:request CategoryDestroyRequest |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CategoryDestroyRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return !($this->route('category') == config('cms.default_category_id')); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ // ]; } } |
We have set the client side. Now for the client side, we will create a disabled button for the uncategorized category. Open table.blade.php inside category folder and modify this lines :
1 2 3 4 5 |
@if($category->id == config('cms.default_category_id')) <button onclick="return false" type="submit" class="btn btn-sm btn-danger disabled"><i class="fa fa-times"></i></button> @else <button onclick="return confirm('Are you sure to delete?')" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-times"></i></button> @endif |
Modify destroy method inside CategoryController :
1 2 3 4 5 6 7 8 9 10 |
public function destroy(Requests\CategoryDestroyRequest $request, $id) { $category = Category::findOrFail($id); Post::withTrashed()->where('category_id', $id)->update(['category_id' => config('cms.default_category_id')]) $category->delete(); return redirect("/backend/category")->with('message', 'The Category has been deleted'); } |
And add post model namespace :
1 |
use App\Post; |
Github commit.