KodeInfo | Learning resources for web and mobile development

Laravel Admin Panel - Part 1 - CRUD Management

September 15th, 2014 23:13:08 by Imran Iqbal Comments(0) - Views(30652)

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 Iqbal
    Imran Iqbal

    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.

Related

WHY USE A FRAMEWORK OVER PLAIN PHP

WHY USE A FRAMEWORK OVER PLAIN PHP
read more

GETTING STARTED WITH LARAVEL

GETTING STARTED WITH LARAVEL
read more

UNDERSTANDING LARAVEL STRUCTURE

UNDERSTANDING LARAVEL STRUCTURE
read more

UNDERSTANDING LARAVEL ROUTES

UNDERSTANDING LARAVEL ROUTES
read more

comments powered by Disqus