Laravel Admin Panel - Part 1 - CRUD Management

By Imran Iqbal In Configuration, Controllers, Introduction, Query Builder, Schema Builder, Validation, Views - - Comments(0) - Views(29675)
Tags : laravel, create, update, delete, laravel admin panel, crud management, laravel tutorials, php laravel

Laravel Admin Panel

Every laravel project maybe large scale or small scale needs a dedicated laravel admin which can handle its own tasks uniquely like CRUD Management , User Management , Groups Management , Login System , Registration System , Forgot Password , User Analytics (Current loggedin users,page views by user , on which page user is browsing , which component is getting much attention by user ) ,Cron jobs and so on . But we wont be covering everything in one post lets make it a series we will start with our CRUD management Lets start with the plan . Ready Steady...GO .

 
 

 

 

 

CRUD Management

  • Schema .
  • Routing .
  • Templating and Controllers .

Schema
We need to keep track of table submitted for crud so we start with schema for crud_table
Create a new migration

Schema::create('crud_table',function($table){
            $table->increments('id');
            $table->string('crud_name');
            $table->string('table_name');
           $table->string('fontawesome_class');
            $table->string('needle');
            $table->string('slug');
            $table->boolean('creatable');
            $table->boolean('editable');
            $table->boolean('listable');
            $table->timestamps();
        });

We have an auto increment id , name of the crud operation , table name of which crud have to be created , fontawesome_class which will hold class name which can be applied to navigation  , needle will keep track of unique / primary key which will be used during editing and deletion ,  slug of table name , creatable will be boolean which says if user admin needs view from which Create Operation can be performed , editable is boolean and says if admin needs edit view , listable is boolean which says if admin needs listing view .
We got the schema for managing the table crud , next table will handle what each column of table will be . A column can be text,password,select box with options ,checkbox ,radiobutton etc . We will make use of below properties .


Text , Password , Number , Normal Textarea , Content Editor  , Gender Full (male/female) , Gender Short (m/f) , True/False , 1/0 , Range of values , File , Date , Datetime , Time ,  Colorpicker , Radio , Checkbox , Select . So our crud_table_rows will hold this data 

Create a new migration

Schema::create('crud_table_rows',function($table){
            $table->increments('id');
            $table->string('table_name');
            $table->string('column_name');
            $table->string('type');
            $table->string('create_rule');
            $table->string('edit_rule');
            $table->boolean('creatable');
            $table->boolean('editable');
            $table->boolean('listable');
            $table->timestamps();
        });
  • id - Auto Increment Id
  • table_name - Name of the table on which this crud operations need to be performed
  • column_name - Name of the Column
  • type - One of the type listed above 
  • create_rule - Validator Rule for Creation Ex : required|email 
  • edit_rule - Validatior Rule for editing
  • creatable - Should this column be shown in create view
  • editable - Should this column be shown in edit view 
  • listable - Should this column be listed

Everything good till now but we need to store the radio buttons, check box's , select options so lets create one more table to store key value pair linked to this table crud_table_rows
Create a new migration

Schema::create('crud_table_pairs',function($table){
            $table->increments('id');
            $table->integer('crud_table_id');
            $table->string('key');
            $table->string('value');
            $table->timestamps();
        });

Last migration will be settings table which will store the stuff which goes no where like uploads path for now
One more time create new migration

Schema::create('crud_settings',function($table){
            $table->string('upload_path');
});
DB::table('crud_settings')->insert(['upload_path'=>'/uploads/']);

Everyone angry seeing insert inside my migration  :^) . I recommend to use tinker to insert a new row with the upload path . We are done with the schema . Now create routes which will hold CRUD .

Routing
Lets create CRUD routes for our controller , 

routes.php

<?php

Route::get('/','CRUDController@index');
Route::get('/crud','CRUDController@index');
Route::get('/crud/create','CRUDController@create');
Route::post('/crud/create','CRUDController@store');
Route::get('/crud/delete/{id}','CRUDController@delete');
Route::get('/crud/edit/{id}','CRUDController@edit');
Route::post('/crud/update/{id}','CRUDController@update');
Route::get('/crud/all','CRUDController@index');

Route::get('/table/{table_name}/settings','SettingsController@settings');
Route::post('/table/{table_name}/settings','SettingsController@postSettings');

Route::get('/table/{table_name}/create','TablesController@create');
Route::post('/table/{table_name}/create','TablesController@store');
Route::get('/table/{table_name}/list','TablesController@all');
Route::get('/table/{table_name}/delete/{id}','TablesController@delete');
Route::get('/table/{table_name}/edit/{id}','TablesController@edit');
Route::post('/table/{table_name}/update/{id}','TablesController@update');

So we will have three controller which will handle respective logic . CRUDController will handle crud for crud_table , TablesController will handle New Entries for tables and SettingsController will handle Settings .


Templating and Controllers 
I found a good free bootstrap admin template(http://almsaeedstudio.com/preview/) which we can use for our admin panel .   Just download the template and paste the content into public folder . Copy index.html and paste it in app/views/ as index.blade.php . Now its time for separating the views . create three directories in views folder as crud,layouts,tables .
 I have removed unnecessary stuff from template and created our partials  .
 

layouts/master.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Laravel Admin Interface</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>

    {{HTML::style("/css/bootstrap.min.css")}}
    {{HTML::style("/css/font-awesome.min.css")}}
    {{HTML::style("/css/ionicons.min.css")}}
    {{HTML::style("/css/AdminLTE.css")}}

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    {{HTML::script("https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js")}}
    {{HTML::style("https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js")}}
    <![endif]-->

    @yield('styles')

</head>

<body class="skin-blue">
<!-- header logo: style can be found in header.less -->

@include('layouts.header')

<div class="wrapper row-offcanvas row-offcanvas-left">
    <!-- Left side column. contains the logo and sidebar -->

    @include('layouts.navigation')

    <!-- Right side column. Contains the navbar and content of the page -->
    <aside class="right-side">

        @yield('content')

    </aside><!-- /.right-side -->
</div><!-- ./wrapper -->

{{HTML::script("/js/jquery.2.0.3.js")}}
{{HTML::script("/js/jquery-ui-1.10.3.min.js")}}
{{HTML::script("/js/bootstrap.min.js")}}
{{HTML::script("js/plugins/iCheck/icheck.min.js")}}
{{HTML::script("js/AdminLTE/app.js")}}

@yield('scripts')

</body>
</html>

layouts/header.blade.php

<header class="header">
<a href="/" class="logo">
    <!-- Add the class icon to your logo image or logo icon to add the margining -->
    Laravel Admin Panel
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a href="#" class="navbar-btn sidebar-toggle" data-toggle="offcanvas" role="button">
    <span class="sr-only">Toggle navigation</span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
</a>
<div class="navbar-right">
<ul class="nav navbar-nav">

</ul>
</div>
</nav>
</header>

layouts/navigation.blade.php

<aside class="left-side sidebar-offcanvas">
    <!-- sidebar: style can be found in sidebar.less -->
    <section class="sidebar">
        <!-- Sidebar user panel -->
        <div class="user-panel">
            <div class="pull-left image">
                <img src="/img/avatar3.png" class="img-circle" alt="User Image" />
            </div>
            <div class="pull-left info">
                <p>Hello, Admin</p>

                <a href="#"><i class="fa fa-circle text-success"></i> Online</a>
            </div>
        </div>
        <!-- search form -->
        <form action="#" method="get" class="sidebar-form">
            <div class="input-group">
                <input type="text" name="q" class="form-control" placeholder="Search..."/>
                            <span class="input-group-btn">
                                <button type='submit' name='seach' id='search-btn' class="btn btn-flat"><i class="fa fa-search"></i></button>
                            </span>
            </div>
        </form>
        <!-- /.search form -->
        <!-- sidebar menu: : style can be found in sidebar.less -->
        <ul class="sidebar-menu">
            <li class="active">
                <a href="/">
                    <i class="fa fa-dashboard"></i> <span>Dashboard</span>
                </a>
            </li>
            @foreach($cruds as $crud)
            <li class="treeview">
                <a href="#">
                    <i class="{{$crud->fontawesome_class}}"></i>
                    <span>{{$crud->crud_name}}</span>
                    <i class="fa fa-angle-left pull-right"></i>
                </a>
                @if($crud->creatable==1||$crud->editable==1||$crud->listable==1)
                <ul class="treeview-menu">
                    @if($crud->creatable==1)
                        <li><a href="/table/{{$crud->slug}}/create"><i class="fa fa-angle-double-right"></i> Create</a></li>
                    @endif
                    @if($crud->listable==1)
                        <li><a href="/table/{{$crud->slug}}/list"><i class="fa fa-angle-double-right"></i> List</a></li>
                    @endif
                    <li><a href="/table/{{$crud->slug}}/settings"><i class="fa fa-angle-double-right"></i> Settings</a></li>
                </ul>
                @endif
            </li>
            @endforeach
        </ul>
    </section>
    <!-- /.sidebar -->
</aside>

layouts/notifications.blade.php

@if(Session::has('error_msg'))
<div class="alert alert-danger">
    <b>Error!</b> {{Session::get('error_msg')}}
</div>
@endif

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

As you see our cruds variable in layouts/navigation.blade.php since it is used in all views we will put that in BaseController  and define a data array 

protected $cruds;
protected $data;

In BaseController constructor

$this->data['cruds'] = DB::table('crud_table')->get();

Our CRUDController contains basic creation , editing , listing operations .

CRUDController.php

<?php

class CRUDController extends BaseController
{

    public function index()
    {
        $this->data['rows'] = DB::table("crud_table")->get();
        return View::make('index', $this->data);
    }

    public function create()
    {
        return View::make('crud.create',$this->data);
    }

    public function edit($id)
    {
        $this->data['crud'] = DB::table("crud_table")->where('id',$id)->first();
        return View::make('crud.edit',$this->data);
    }

    public function update($id)
    {
        $this->data['crud'] = DB::table("crud_table")->where('id',$id)->first();

        $v = Validator::make(['crud_name' => Input::get('crud_name'),
                'table_name' => Input::get('table_name'),
                'needle' => Input::get('needle'),
                'creatable' => Input::has('creatable'),
                'editable' => Input::has('editable'),
                'listable' => Input::has('listable')],
            ['crud_name'=>'required','table_name'=>'required','needle'=>'required'
                , 'creatable'=>'required','editable'=>'required', 'listable'=>'required']);

        if ($v->fails()) {
            Session::flash('error_msg',Utils::buildMessages($v->errors()->all()));
            return Redirect::to("/crud/edit/".$id)->withErrors($v)->withInput();
        } else {

            if(DB::table('crud_table')->where('table_name',Input::get('table_name'))->where('id','!=',$id)->count()>0){
                Session::flash('error_msg','Table name already exist');
                return Redirect::to("/crud/edit/".$id)->withInput();
            }

            DB::table('crud_table')->where('id',$id)->update(['crud_name' => Input::get('crud_name'),
                'table_name' => Input::get('table_name'),
                'slug' => Str::slug(Input::get('table_name')),
                'fontawesome_class' => Input::get('fontawesome_class','fa fa-ellipsis-v'),
                'needle' => Input::get('needle'),
                'creatable' => Input::has('creatable'),
                'editable' => Input::has('editable'),
                'listable' => Input::has('listable'),
                'created_at'=> Utils::timestamp(),
                'updated_at'=> Utils::timestamp()]);
        }

        Session::flash('success_msg','CRUD updated successfully');

        return Redirect::to("/crud/all");
    }

    public function store()
    {
        $v = Validator::make(['crud_name' => Input::get('crud_name'),
                'table_name' => Input::get('table_name'),
                'needle' => Input::get('needle'),
                'creatable' => Input::has('creatable'),
                'editable' => Input::has('editable'),
                'listable' => Input::has('listable')],
            ['crud_name'=>'required','table_name'=>'required|unique:crud_table,table_name','needle'=>'required'
                , 'creatable'=>'required','editable'=>'required', 'listable'=>'required']);

        if ($v->fails()) {
            Session::flash('error_msg',Utils::buildMessages($v->errors()->all()));
            return Redirect::to("/crud/create")->withErrors($v)->withInput();
        } else {
            DB::table('crud_table')->insert(['crud_name' => Input::get('crud_name'),
                    'table_name' => Input::get('table_name'),
                    'slug' => Str::slug(Input::get('table_name')),
                    'fontawesome_class' => Input::get('fontawesome_class','fa fa-ellipsis-v'),
                    'needle' => Input::get('needle'),
                    'creatable' => Input::has('creatable'),
                    'editable' => Input::has('editable'),
                    'listable' => Input::has('listable'),
                    'created_at'=> Utils::timestamp(),
                    'updated_at'=> Utils::timestamp()]);
        }

        Session::flash('success_msg','CRUD for table '+Input::get('table_name')+' created successfully');

        return Redirect::to("/crud/all");
    }

    public function delete($id){
        $crud_table = DB::table('crud_table')->where('id',$id)->first();

        DB::table('crud_table_rows')->where('table_name',$crud_table->table_name)->delete();

        DB::table('crud_table')->where('id',$id)->delete();
        Session::flash('success_msg','CRUD deleted successfully');
        return Redirect::to("/crud/all")->withInput();
    }
}

From TablesController I will show you snippets of code 

TablesController.php

$this->beforeFilter('table_settings');
 $this->beforeFilter('table_needle');

I have created filters because i dont want user to go create,list before creating settings .
Below foreach loop from create method checks the type of column and put it into respective array since when we render view we can initialize date , datetime ,time,colorpicker or ckeditor
 

  foreach ($columns as $column) {

            if ($column->type == "datetime") {
                $datetimepickers[] = $column->column_name;
            }

            if ($column->type == "time") {
                $timepickers[] = $column->column_name;
            }

            if ($column->type == "radio") {
                $radios = DB::table("crud_table_pairs")->where("crud_table_id", $column->id)->get();
                $column->radios = $radios;
            }

            if ($column->type == "checkbox") {
                $checkboxes = DB::table("crud_table_pairs")->where("crud_table_id", $column->id)->get();
                $column->checkboxes = $checkboxes;
            }

            if ($column->type == "range") {
                $range = DB::table("crud_table_pairs")->where("crud_table_id", $column->id)->first();
                $column->range_from = $range->key;
                $column->range_to = $range->value;
            }

            if ($column->type == "select") {
                $selects = DB::table("crud_table_pairs")->where("crud_table_id", $column->id)->get();
                $column->selects = $selects;
            }
        }

Whenever admin creates a new entry all validation rules have to be met for successful creation or else it will show errors . 

 $columns = DB::table('crud_table_rows')->where("table_name", $this->table->table_name)->get();
        $rules = [];
        $data = $inputs;

        for ($i = 0; $i < sizeOf($columns); $i++) {

            if (!empty($columns[$i]->create_rule) && isset($data[$columns[$i]->column_name]))
                $rules[$columns[$i]->column_name] = $columns[$i]->create_rule;
        }

        $v = Validator::make($data, $rules);

Our SettingsController will be responsible for creating type of columns and if column type is radio,checkbox etc which can generate key value pair then that will be saved in crud_table_pairs table .
Our tables CRUD views which goes in views/tables are used to create,update the column types , validation rules etc . 
Once you checkout code from github run migrations and follow the video in the starting of this tutorial .

Thanks
KodeInfo

Author

Imran is a web developer and consultant from India. He is the founder of KodeInfo, the PHP and Laravel Community . In the meantime he follows other projects, works as a freelance backend consultant for PHP applications and studies IT Engineering . He loves to learn new things, not only about PHP or development but everything.

comments powered by Disqus