Laravel 5 Blog System : CRUD on Admin Panel Backend Services

Laravel 5.3 Tutorial - Create a CRUD operations on Admin Panel / Backend services, this lesson will show you how to manage posts in dashbord admin, at the previews lessons, we have learn how to setup database, create migration and table, display post, create single page, adding comment systems, bootstrap templates.

This lessons, we will working on Backends services.

Create Admin Panel Using Bootstrap

We will using bootstrap for our dashbord, download admin dahsbord from gootstrap site http://getbootstrap.com/examples/dashboard/
here's video tutorial how to create panel admin in Laravel



Full source code :
make sure you have install bootstrap on our project, or just follow video tutorial above.

dashbord.blade.php (resources\views\layouts\dashbord.blade.php)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Admin Dashboard</title>
    <!-- Bootstrap core CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
      <!-- Bootstrap core CSS -->
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href="{{ asset('css/ie10-viewport-bug-workaround.css') }}" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link href="{{ asset('dashboard.css') }}" rel="stylesheet">
    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="{{ asset('js/ie-emulation-modes-warning.js')}}"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/admin/index">
            <img alt="Brand" src="{{ asset('img/logo.png') }}" width="60%">
          </a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
            @if (Auth::guest())
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                Members<span class="caret"></span>
              </a>
              <ul class="dropdown-menu" role="menu">
                <li><a href="{{ url('/login') }}">Login</a></li>
                <li><a href="{{ url('/register') }}">Register</a></li>
              </ul>
            </li>
            @else
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                {{ Auth::user()->name }} <span class="caret"></span>
              </a>
              <ul class="dropdown-menu" role="menu">
                <li>
                  <a href="{{ url('/logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                    Logout
                  </a>
                  <form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">
                      {{ csrf_field() }}
                  </form>
                </li>
              </ul>
            </li>
            @endif
          </ul>
        </div>
      </div>
    </nav>
    <div class="container-fluid">
      <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
          <ul class="nav nav-sidebar">
            <li class="active"><a href="/admin/">Overview <span class="sr-only">(current)</span></a></li>
            <li><a href="/admin/posts/allposts">All Posts</a></li>
            <li><a href="/admin/posts/alldrafts">Drafts</a></li>
            <li><a href="/admin/posts/allcomments">Comments</a></li>
            <li><a href="#">Label</a></li>
            <li><a href="#">User</a></li>
            <li><a href="#">Profile</a></li>
          </ul>
        </div>
        <div class="col-sm-9 main">
          @yield('content')
        </div>
      </div>
    </div>

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="{{ asset('vendor/jquery/jquery.min.js') }}"><\/script>')</script>
    <script src="{{ asset('js/bootstrap.min.js') }}"></script>
    <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
    <script src="{{ asset('js/vendor/holder.min.js') }}"></script>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="{{ asset('js/ie10-viewport-bug-workaround.js') }}"></script>

    <!-- show images thumbnail in create/edit page -->
    <script type="text/javascript">
      function readURL(input) {
        if (input.files && input.files[0]) {
          var reader = new FileReader();
          reader.onload = function (e) {
            $('#showimages').attr('src', e.target.result);
          }
          reader.readAsDataURL(input.files[0]);
        }
      }
      $("#inputimages").change(function () {
        readURL(this);
      });
    </script>
  </body>
</html>

Next, on the index.blade.php add this source code

index.blade.php (resources\views\admin\index.blade.php)

@extends('layouts.dashbord')
@section('content')
<div class="col-sm-9 main">
  <h2 class="sub-header">Dashbord</h2>
  <div class="table-responsive">
    <p>
      wellcome admin, you can add, update, delete any posts, comments, drafts, etc ..
    </p>
    <p>
      <a href="http://www.hc-kr.com/">www.hc-kr.com</a>
    </p>
  </div>
</div>
@endsection

CRUD Operations (on Posts Table)

Next, we will create crud operation for manage the posts,
this's video tutorial crud operation for manage the posts.


Full source code

PostsController (app\Http\Controllers\Auth\PostsController.php)

<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Posts;
use App\User;
use Redirect;
class PostsController extends Controller {
    // show all posts
    public function index(Request $request) {
        // i'll stored all posts and search function
        $search = $request->get('search');
        $posts = Posts::where('title','like','%'.$search.'%')->where('active',1)->orderBy('created_at')->paginate(3);
        return view('admin/posts/allposts')->withPosts($posts);
    }
    // show form create post
    public function create(Request $request) {
        // if user can post i.e. user is admin or author
        if ($request->user()->can_post()){
          return view('admin/posts/create');
        } else {
          return redirect('/')->withErrors('You have not sufficient permissions for writing post');
        }
    }
    // save posts data into database
    public function store(Request $request) {
        $post = new Posts();
        $post->title = $request->get('title');
        $post->description = $request->get('description');
        $post->body = $request->get('body');
        $post->slug = str_slug($post->title);
        $post->author_id = $request->user()->id;

        // thumbnail upload
        if ($request->file('images')) {
          $fileName = str_random(30);
          $request->file('images')->move("img/",$fileName);
        } else {
          $fileName = $post->images;
        }
        $post->images = $fileName;

        if ($request->has('save')){
          // for draft
          $post->active = 0;
          $message = 'Post saved successfully';
        } else {
          // for posts
          $post->active = 1;
          $message = 'Post published successfully';
        }
        $post->save();
        return redirect('admin/posts/editpost/'.$post->slug)->withMessage($message);
    }

    public function show($id)
    {
        // next lessons we will use this function
    }

    public function edit(Request $request, $slug) {
        $post = Posts::where('slug',$slug)->first();
        if($post && ($request->user()->id == $post->author_id || $request->user()->is_admin()))
        return view('admin/posts/edit')->with('post',$post);
        return redirect('/')->withErrors('you have not sufficient permissions');
    }
    // update data
    public function update(Request $request) {
        $post_id = $request->input('post_id');
        $post = Posts::find($post_id);
        if ($post && ($post->author_id == $request->user()->id || $request->user()->is_admin())){
          $title = $request->input('title');
          $slug = str_slug($title);
          $duplicate = Posts::where('slug',$slug)->first();
          if($duplicate){
            if($duplicate->id != $post_id){
              return redirect('admin/posts/editpost/'.$post->slug)->withErrors('Title already exists.')->withInput();
            } else {
              $post->slug = $slug;
            }
          }
          $post->title = $title;
          // thumbnail upload
          if ($request->file('images')) {
            $fileName = str_random(30);
            $request->file('images')->move("img/",$fileName);
          } else {
            $fileName = $post->images;
          }
          $post->images = $fileName;
          $post->body = $request->input('body');

          if($request->has('save')){
            $post->active = 0;
            $message = 'Post saved successfully';
            $goto = 'admin/posts/editpost/'.$post->slug;
          } else {
            $post->active = 1;
            $message = 'Post updated successfully';
            $goto = 'admin/posts/allposts';
          }
          $post->save();
          return redirect($goto)->withMessage($message);
        } else {
          return redirect('/')->withErrors('you have not sufficient permissions');
        }
    }

    public function destroy(Request $request, $id) {
        $post = Posts::find($id);
        if($post && ($post->author_id == $request->user()->id || $request->user()->is_admin())){
          $post->delete();
          $data['message'] = 'Post deleted Successfully';

        } else {
          $data['errors'] = 'Invalid Operation. You have not sufficient permissions';
        }
        return redirect('admin/posts/allposts')->with($data);
    }
}

User Model (app\User.php)

<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable {
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    public function can_post() {
      $role = $this->role;
      if($role == 'author' || $role == 'admin') {
        return true;
      }
      return false;
    }

    public function is_admin() {
      $role = $this->role;
      if($role == 'admin') {
        return true;
      }
      return false;
    }

    public function posts() {
      return $this->hasMany('App\Posts','author_id');
    }

    public function comments() {
      return $this->hasMany('App\Comments','from_user');
    }
}

Routes

<?php
Auth::routes();
Route::get('/','PostsController@index');
Route::get('/home', 'PostsController@index');
Route::get('/{slug}', 'PostsController@show')->where('slug', '[A-Za-z0-9-_]+');
Route::group(['middleware' => ['auth']], function() {
  Route::post('comment/add','CommentsController@store');
  Route::get('admin/index', 'PostsController@indexDashbord');
  Route::resource('admin/posts/','Auth\PostsController');
  Route::get('admin/posts/allposts','Auth\PostsController@index');
  // show new post form
  Route::get('admin/posts/new-post','Auth\PostsController@create');
  // save new post
  Route::post('admin/posts/createpost','Auth\PostsController@store');
  // edit form
  Route::get('admin/posts/editpost/{slug}','Auth\PostsController@edit');
  // update data
  Route::post('admin/posts/updatepost','Auth\PostsController@update');
  // delete post
  Route::get('admin/posts/deletepost/{id}','Auth\PostsController@destroy');
});

allposts.blade.php (resources\views\admin\posts\allposts.blade.php)

@extends('layouts.dashbord')
@section('content')
  <h2 class="sub-header">All Posts</h2>
  <div class="row">
    <div class="col-md-9">
      <a href="{{ url('admin/posts/new-post')}}" class="btn btn-primary btn-sm">Add New Post</a>
    </div>
    <div class="col-md-3">
      {!! Form::open(['method'=>'GET','url'=>'admin/posts/','class'=>'navbar-form navbar-left','role'=>'search']) !!}
        <div class="input-group custom-search-form">
          <input type="text" name="search" class="form-control" placeholder="Search ....">
          <span class="input-group-btn">
            <button type="submit" class="btn btn-default-sm">
              <i class="fa fa-search"></i>
            </button>
          </span>
        </div>
      {!! Form::close() !!}
    </div>
  </div>

  <div class="table-responsive">
    <table class="table table-striped">
      <thead>
      <tr>
        <th>#</th>
        <th>Title</th>
        <th>Description</th>
        <th>Post</th>
        <th>Url's</th>
        <th>Image</th>
        <th>Created</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <?php $no=1; ?>
      @foreach($posts as $post)
        <tr>
          <td>{{$no++}}</td>
          <td>{{$post->title}}</td>
          <td>{{$post->description}}</td>
          <td>
            {{ str_limit($post->body, $limit = 120, $end = '.......') }}
          </td>
          <td>
            {!! ('<a href='.url("/".$post->slug).'>'.$post->slug.'</a>') !!}
          </td>
          <td>
            <img src="{{ url('img/'.$post->images)}}" id ="showimages" style="max-width:100px;max-height:50px;float:left;">
          </td>
          <td>{{$post->created_at}}</td>
          <td>
            <form class="" action="" method="post">
              <input type="hidden" name="_method" value="delete">
              <input type="hidden" name="_token" value="{{ csrf_token() }}">
              <a href="{{ url('admin/posts/editpost/'.$post->slug)}}" class="btn btn-primary btn-xs">
                <span class="glyphicon glyphicon-edit"></span>
              </a>

              <a href="{{ url('admin/posts/deletepost/'.$post->id.'?_token='.csrf_token()) }}" onclick="return confirm('Are you sure to delete this data');" class="btn btn-danger btn-xs">
                <span class="glyphicon glyphicon-trash"></span>
              </a>
            </form>
          </td>
        </tr>
      @endforeach
    </tbody>
    </table>
    <!-- pagination -->
    {!! $posts->links() !!}
  </div>
@endsection

create.blade.php (resources\views\admin\posts\create.blade.php)

@extends('layouts.dashbord')
@section('content')
<h2>Create New Post</h2>
<script type="text/javascript" src="//cdn.tinymce.com/4/tinymce.min.js"></script>
<script type="text/javascript">
tinymce.init({
  selector : "textarea",
  plugins : ["advlist autolink lists link image charmap print preview anchor", "searchreplace visualblocks code fullscreen", "insertdatetime media table contextmenu paste"],
  toolbar : "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
});
</script>

<form action="{{ url('/admin/posts/createpost') }}" method="post" enctype="multipart/form-data">
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <div class="form-group">
    <p>Title</p>
    <input type="text" name="title" value="{{ old('title') }}" required="required" placeholder="Enter title here" class="form-control">
    <br>
    <p>Description</p>
    <input type="text" name="description" value="{{ old('description') }}" required="required" placeholder="Enter description here" class="form-control">
    <br>
    <!-- thumbnail upload -->
    <p>Thumbnail</p>
    <img src="http://placehold.it/100x100" id ="showimages" style="max-width:200px;max-height:200px;float:left;"/>
    <div class="row">
      <div class="col-md-12">
        <input type="file" id="inputimages" name="images">
      </div>
    </div>
  </div>

  <div class="form-group">
    <textarea name='body'class="form-control" rows="20"></textarea>
  </div>
  <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  <input type="submit" name='save' class="btn btn-default" value = "Save Draft" />
</form>
@endsection

edit.blade.php (resources\views\admin\posts\edit.blade.php)

@extends('layouts.dashbord')
@section('content')
<h2>Update Post</h2>
<script type="text/javascript" src="//cdn.tinymce.com/4/tinymce.min.js"></script>
<script type="text/javascript">
tinymce.init({
  selector : "textarea",
  plugins : ["advlist autolink lists link image charmap print preview anchor", "searchreplace visualblocks code fullscreen", "insertdatetime media table contextmenu paste"],
  toolbar : "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
});
</script>

<form action="{{ url('/admin/posts/updatepost') }}" method="post" enctype="multipart/form-data">
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <input type="hidden" name="post_id" value="{{ $post->id }}{{ old('post_id') }}">
  <input type="hidden" name="post_descriptions" value="{{ $post->id }}{{ old('post_id') }}">
  <div class="form-group">
    <p>Title</p>
    <input type="text" name="title" value="@if(!old('title')){{$post->title}}@endif{{ old('title') }}" required="required" placeholder="Enter title here" class="form-control">
    <br>
    <p>Description</p>
    <input type="text" name="description" value="@if(!old('description')){{$post->description}}@endif{{ old('description') }}" required="required" placeholder="Enter description here" class="form-control">
    <br>
    <!-- thumbnail upload -->
    <p>Thumbnail</p>
    <img src="{{ url('img/'.$post->images) }}" id ="showimages" style="max-width:200px;max-height:200px;float:left;"/>
    <div class="row">
      <div class="col-md-12">
        <input type="file" id="inputimages" name="images">
      </div>
    </div>
  </div>

  <div class="form-group">
    <textarea name='body'class="form-control" rows="20">
      @if(!old('body'))
        {!! $post->body !!}
      @endif
        {!! old('body') !!}
    </textarea>
  </div>
  @if($post->active == '1')
    <input type="submit" name='publish' class="btn btn-success" value = "Update"/>
  @else
    <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  @endif
  <input type="submit" name='save' class="btn btn-default" value = "Save As Draft" />
  <a href="{{ url('admin/posts/deletepost/'.$post->id.'?_token='.csrf_token()) }}" onclick="return confirm('Are you sure to delete this data');" class="btn btn-danger">Delete</a>
</form>
@endsection

Note :
Please watch video tutorial before do anything,

for full tutorial is here

CRUD on Admin Panel Backend Services



See you next lessons

Recomended Tutorials

  1. Hi,
    I would like to develop dynamic website with admin dashboard. Using admin dashboard admin could be changed the website content and images like what we do in wordpress. Guide me to crack this.

    ReplyDelete
  2. Hi,
    Nice tutorial, thanks for your service.
    I would like to create a laravel website with admin dashboard.
    I want to edit and update pages(content, images) like what you did in this tutorial through admin dashboard.

    ReplyDelete