PHP/Mysql Admin generator
Nice and easy admin for generate backend and implement frontend side also., (*1)
$sudo a2enmod rewrite
, (*2)
$sudo service apache2 restart
, (*3)
assets/admin/
$npm install -g grunt-cli
, (*4)
$npm install
, (*5)
$grunt uglify
, (*6)
$grunt cssmin
, (*7)
see http://gruntjs.com/ for more details, (*8)
Copy files and folders inside some directory, for example "project1"., (*9)
Edit config/constants.php
#8, (*10)
define('BASE_URL', $protocol . ':' . '//' . $_SERVER['SERVER_NAME'] . $port . 'project1');
, (*11)
Edit Database config config/database.php
, (*12)
Load in browser depending your environment maybe:, (*13)
http://localhost/project1
, (*14)
http://localhost/project1/admin
, (*15)
Admin Credentials, (*16)
admin@gmail.com
, (*17)
admin
, (*18)
** Basic concepts and structure for tables recomended ** - Database Table names. must be in lowercase and singular, for example:, (*19)
city, company, gym.
- Table column names, must be in lowercase you need a status of type ENUM if you may bot want delete permanently, see examples below., (*20)
Table referenced names must be of format: tablename_id, ending with "_id" after table name in lowercase., (*21)
CREATE TABLE IF NOT EXISTS `city` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(250) NOT NULL, `status` enum('active','deleted') DEFAULT 'active', PRIMARY KEY (`id`), KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `company` ( `id` int(11) NOT NULL AUTO_INCREMENT, `style` enum('pfc','mfc','psf','nn') DEFAULT 'nn', `name` varchar(250) NOT NULL, `logo` varchar(500) NOT NULL, `logo_small` varchar(500) NOT NULL, `logo_big` varchar(500) NOT NULL, `marker` varchar(500) NOT NULL, `permalink` varchar(500) NOT NULL, `status` enum('active','deleted') DEFAULT 'active', `date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE IF NOT EXISTS `gym` ( `id` int(11) NOT NULL AUTO_INCREMENT, `city_id` int(11) NOT NULL, `company_id` int(11) NOT NULL, `name` varchar(250) NOT NULL, `short_name` varchar(100) NOT NULL, `latitude` varchar(20) NOT NULL, `longitude` varchar(20) NOT NULL, `phone` varchar(20) NOT NULL, `email` varchar(200) NOT NULL, `address` varchar(500) NOT NULL, `permalink` varchar(500) NOT NULL, `status` enum('active','incoming','deleted') DEFAULT 'active', `date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `company_id` (`company_id`), KEY `city_id` (`city_id`), KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
** Import that tables into database and build three next models inside models
folder **
city_model.php
, (*22)
<?php require_once(BASE_PATH . 'system/module.php'); require_once(BASE_PATH . 'system/auth.php'); class CityModel extends Module { function __construct() { // Calls to parent constructor with table name and table_id column name parent::__construct('city', 'id'); // You can comment this line if no prefix are used $this->phisical_table = DB_PREFIX . 'city'; // key is exactly column name, lang function uses laguage/en.php by default you can make change the logic $this->addField( array( "key" => "id", "comparator" => 'IN', "name" => lang("ID"), "type" => "hidden" ) ); // Validation format is like jqyer validation but in php format $this->addField( array( "key" => "name", // Default comparator when making background search yout can specify, see filters line "comparator" => "LIKE", "name" => lang("Name"), "type" => "text", // supported types are: /* text textarea password upload editor date daterange hidden email dropdown -> for ENUM types or external table composite -> composite table(C) starting from (A) like: A -> C <- B (You cannot add more C rows) inline -> simple composite table A, B then A -> C <- B (You can only add or edit C rows) */ 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Name is required') ) ), // You can customize attributtes for each here 'attr' => array( array('name' => 'placeholder', 'value' => lang('Name') ) ) ) ); // Added status column for persist data after deleting (deleted row = deleted value for this column) $this->addField( array( "key" => "status", "comparator" => '!=' ) ); // This compares with != like described above in comparator field parameter $this->filters = array( 'status' => 'deleted' ); // You can access to current user authenticated as below, roles are specified in user table, you can add more roles there also in user_model.php if(Auth::$user['role'] != 'sadmin') { $this->filters = array( 'status' => 'deleted' ); } // Defines what be showed in form and grid alt is set the order $this->addGroup('form', array('id', 'name')); $this->addGroup('grid', array('id', 'name')); } }
company_model.php
, (*23)
<?php require_once(BASE_PATH . 'system/module.php'); require_once(BASE_PATH . 'system/auth.php'); class CompanyModel extends Module { function __construct() { parent::__construct('company', 'id'); $this->phisical_table = DB_PREFIX . 'company'; $this->addField( array( "key" => "id", "name" => lang("ID"), "type" => "hidden" ) ); $this->addField( array( "key" => "name", "comparator" => "=", "name" => lang("Name"), "type" => "text", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Name is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Name') ) ), 'group' => 'column1' ) ); $this->addField( array( "key" => "style", "comparator" => "LIKE", "name" => lang("Style"), "type" => "dropdown", // Setting up values for each enum type supported un table structure "values" => array( array('id' => '', 'name' => lang('Select Style')), array('id' => 'pfc', 'name' => lang('PFC')), array('id' => 'mfc', 'name' => lang('MFC')), array('id' => 'psf', 'name' => lang('PSF')) ), 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Style is required') ) ), // You can organize fields in columns put any name here or leave empty for no columns 'group' => 'column1' ) ); $this->addField( array( "key" => "logo", "name" => lang("Logo"), "type" => "upload", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Logo is required') ) ), // settings are required for upload/editor type field 'settings' => array( // url for uploader admin/[controller_name]/upload 'url' => BASE_URL . 'admin/company/upload', // absolute path for upload dir 'path' => BASE_PATH . 'uploads' . DIRECTORY_SEPARATOR . 'logos' . DIRECTORY_SEPARATOR, // Download path by default are using timthumb for thumbnails in framework.js 'download' => BASE_URL . 'uploads/logos/' ), 'group' => 'column2' ) ); $this->addField( array( "key" => "logo_small", "name" => lang("Logo Small"), "type" => "upload", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Logo Small is required') ) ), 'settings' => array( 'url' => BASE_URL . 'admin/company/upload', 'path' => BASE_PATH . 'uploads' . DIRECTORY_SEPARATOR . 'logos' . DIRECTORY_SEPARATOR, 'download' => BASE_URL . 'uploads/logos/' ), 'group' => 'column1' ) ); /* Inline example, you can add/edit referenced external table and external columns specified en model parameter $this->addField( array( "key" => "images", "type" => "inline", "model" => new Company_imagesModel(), 'field_group' => lang('Add Image'), "group" => "column1", ) ); */ $this->addField( array( "key" => "logo_big", "name" => lang("Logo Big"), "type" => "upload", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Logo Big is required') ) ), 'settings' => array( 'url' => BASE_URL . 'admin/company/upload', 'path' => BASE_PATH . 'uploads' . DIRECTORY_SEPARATOR . 'logos' . DIRECTORY_SEPARATOR, 'download' => BASE_URL . 'uploads/logos/' ), 'group' => 'column2' ) ); $this->addField( array( "key" => "marker", "name" => lang("Marker for map"), "type" => "upload", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Marker is required') ) ), 'settings' => array( 'url' => BASE_URL . 'admin/company/upload', 'path' => BASE_PATH . 'uploads' . DIRECTORY_SEPARATOR . 'markers' . DIRECTORY_SEPARATOR, 'download' => BASE_URL . 'uploads/markers/' ), 'group' => 'column2' ) ); $this->addField( array( "key" => "status" ) ); $this->filters = array( DB_PREFIX . 'company.status!=' => 'deleted' ); $this->pagination = array( 'start' => 0, 'per_page' => 10 ); $this->addGroup('form', array('id', 'style', 'name', 'logo_small', 'images', 'logo', 'logo_big', 'marker')); $this->addGroup('grid', array('id', 'style', 'name', 'logo_small', 'logo', 'marker')); } /* Callback before saving data (add/edit), for example sets permalink */ function callback_before_save($id, $data) { $data['permalink'] = toAscii($data['name']); return $data; } }
<?php require_once(BASE_PATH . 'system/module.php'); require_once(BASE_PATH . 'system/auth.php'); require_once(BASE_PATH . 'models/city_model.php'); require_once(BASE_PATH . 'models/company_model.php'); class GymModel extends Module { function __construct() { parent::__construct('gym', 'id'); $this->phisical_table = DB_PREFIX . 'gym'; /* customizing for dropdown when referencing this model on a dropdown field */ $this->field_custom_name_dropdown = "CONCAT(company.name, ' - ', gym.name)"; $this->addField( array( "key" => "id", "name" => lang("ID"), "type" => "hidden" ) ); $this->addField( array( "key" => "name", "comparator" => "=", "name" => lang("Name"), "type" => "text", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Name is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Name') ) ), 'group' => 'column1', 'filtrable' => true, ) ); $this->addField( array( "key" => "city_id", "comparator" => "=", "name" => lang("City"), "type" => "dropdown", // instead on ENUM here are using a external table for reference city_id column "model" => new CityModel(), "default" => array( array('id' => '', 'name' => lang('Select City')) ), 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('City is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('City') ) ), 'group' => 'column1', // This paramter creates a searc bar and enable this column for make searches 'filtrable' => true, // You cam agrupate by tabs, then tab bar appears on top of form 'tab' => lang('Main Settings') ) ); $this->addField( array( "key" => "company_id", "comparator" => "=", "name" => lang("Company"), "type" => "dropdown", "model" => new CompanyModel(), "default" => array( array('id' => '', 'name' => lang('Select Company')) ), 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Company is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Company') ) ), 'group' => 'column1', 'filtrable' => true ) ); $this->addField( array( "key" => "short_name", "comparator" => "=", "name" => lang("Short name"), "type" => "text", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Short name is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Short name') ) ), 'group' => 'column1' ) ); $this->addField( array( "key" => "phone", "comparator" => "=", "name" => lang("Phone"), "type" => "text", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Phone is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Phone') ) ), 'group' => 'column1' ) ); $this->addField( array( "key" => "email", "comparator" => "=", "name" => lang("Email"), "type" => "text", 'validation' => array( 'rules' => array( 'required' => true, 'email' => true ), 'messages' => array( 'required' => lang('Email is required'), 'email' => lang('Invalid email address') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Email') ) ), 'group' => 'column1' ) ); $this->addField( array( "key" => "address", "comparator" => "=", "name" => lang("Address"), "type" => "textarea", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Address is required') ) ), 'attr' => array( array('name' => 'placeholder', 'value' => lang('Address') ), array('name' => 'class', 'value' => 'full-width' ) ), 'group' => 'column1' ) ); $this->addField( array( "key" => "location", "comparator" => "=", "name" => lang("Location"), // This type will show a google map "type" => "map", 'validation' => array( 'rules' => array( 'required' => true ), 'messages' => array( 'required' => lang('Location is required') ) ), // Settings are required for map type in this format : "settings" => array( 'type' => 'selector', 'lat_key' => 'latitude', 'lon_key' => 'longitude', 'center' => array( 'latitude' => -17.130948, 'longitude' => -64.753418 ), 'zoom' => 18, 'map_type' => 'google.maps.MapTypeId.ROADMAP' ), 'group' => 'column2' ) ); $this->addField( array( "key" => "status", 'name' => lang('Status'), 'type' => 'dropdown', 'values' => array( array('id' => '', 'name' => lang('Select Status')), array('id' => 'active', 'name' => lang('Active')), array('id' => 'incoming', 'name' => lang('Incoming')) ), 'filtrable' => true, "group" => "column1" ) ); // this for show company name in grid $this->addField( array( "grid_key" => "company", "name" => lang('Company'), "field" => 'company_id' ) ); $this->filters = array( DB_PREFIX . 'gym.status!=' => 'deleted' ); // This setup pagination for grid $this->pagination = array( 'start' => 0, 'per_page' => 10 ); $this->addGroup('form', array('id', 'city_id', 'company_id', 'name', 'short_name', 'email', 'phone', 'address', 'status', 'location')); $this->addGroup('grid', array('id', 'company', 'name', 'short_name', 'status')); } function callback_before_save($id, $data) { $data['permalink'] = toAscii($data['name']); return $data; } // Example for custom methods for frontend function getFor($company_id) { $sql = " SELECT gym.id, gym.permalink, gym.name, gym.short_name, gym.status, company.permalink as company_permalink, company.id as company_id, company.name as company, city.name as city FROM gym INNER JOIN company ON(company.id=gym.company_id) INNER JOIN city ON(city.id=gym.city_id) WHERE gym.company_id='$company_id' AND gym.status != 'deleted' ORDER BY gym.short_name ASC"; return $this->fetch_result($sql); } }
** now create three controllers inside admin
folder **
controllers must be each one in a separated file, name must be "table name" in lower case, (*24)
<?php require_once(BASE_PATH . 'controllers/admin/backend.php'); require_once(BASE_PATH . 'models/city_model.php'); class City extends Backend { function __construct() { parent::__construct(); $this->model = new CityModel(); } }
<?php require_once(BASE_PATH . 'controllers/admin/backend.php'); require_once(BASE_PATH . 'models/company_model.php'); class Company extends Backend { function __construct() { parent::__construct(); $this->model = new CompanyModel(); } }
<?php require_once(BASE_PATH . 'controllers/admin/backend.php'); require_once(BASE_PATH . 'models/gym_model.php'); class Gym extends Backend { function __construct() { parent::__construct(); $this->model = new GymModel(); } }
yes it is very simple, extending of Backend you have session control redirects to login if are not logged, chek home controller for no session control, (*25)
** Now you are ready to add modules in the admin panel ** 1. Open dashboard controller and add something like next code:, (*26)
<?php require_once(BASE_PATH . 'controllers/admin/backend.php'); //require_once(BASE_PATH . 'models/dashboard.php'); require_once(BASE_PATH . 'models/user_model.php'); require_once(BASE_PATH . 'models/gym_model.php'); require_once(BASE_PATH . 'models/city_model.php'); require_once(BASE_PATH . 'models/company_model.php'); class Dashboard extends Backend { function __construct() { parent::__construct(); //$this->model = new DashboardModel(); } function index() { global $lang; $userModule = new UserModel(); $cityModule = new CityModel(); $gymModule = new GymModel(); $companyModel = new CompanyModel(); $data['title'] = lang("Admin Panel"); if (Auth::$user['role'] == 'sadmin') $data['modules']['user'] = array( 'model' => $userModule->purgeModel(), 'settings' => array( 'container' => '#page', 'class' => 'page' ), 'containers' => array( 'modal' => array( 'settings' => array() ), 'form' => array( 'settings' => array() ), 'grid' => array( 'settings' => array( 'actions' => array( array('type' => 'edit', 'text' => lang('Edit'), 'icon' => ''), array('type' => 'delete', 'text' => lang('Delete'), 'icon' => '') ) ) ), 'toolbar' => array( 'settings' => array( 'actions' => array( array( 'type' => 'text', 'title' => lang('Users Manager'), 'template' => 'h2' ), array( 'type' => 'text', 'title' => lang('Manage users and manage roles'), 'template' => 'h5' ), array( 'type' => 'break' ), array( 'type' => 'button', 'title' => lang('New User'), 'action' => 'new' ) ) ) ) ) ); if (Auth::$user['role'] == 'sadmin') $data['modules']['city'] = array( 'model' => $cityModule->purgeModel(), 'settings' => array( 'container' => '#page', 'class' => 'page' ), 'containers' => array( 'modal' => array( 'settings' => array() ), 'form' => array( 'settings' => array() ), 'grid' => array( 'settings' => array( 'actions' => array( array('type' => 'edit', 'text' => lang('Edit'), 'icon' => ''), array('type' => 'delete', 'text' => lang('Delete'), 'icon' => '') ) ) ), 'toolbar' => array( 'settings' => array( 'actions' => array( array( 'type' => 'text', 'title' => lang('City Manager'), 'template' => 'h2' ), array( 'type' => 'text', 'title' => lang('Manage cities'), 'template' => 'h5' ), array( 'type' => 'break' ), array( 'type' => 'button', 'title' => lang('New City'), 'action' => 'new' ) ) ) ) ) ); ); if (Auth::$user['role'] == 'sadmin') $data['modules']['gym'] = array( 'model' => $gymModule->purgeModel(), 'settings' => array( 'container' => '#page', 'class' => 'page' ), 'containers' => array( 'modal' => array( 'settings' => array() ), 'form' => array( 'settings' => array() ), 'grid' => array( 'settings' => array( 'actions' => array( array('type' => 'edit', 'text' => lang('Edit'), 'icon' => ''), array('type' => 'delete', 'text' => lang('Delete'), 'icon' => '') ) ) ), 'toolbar' => array( 'settings' => array( 'actions' => array( array( 'type' => 'text', 'title' => lang('Gym manager'), 'template' => 'h2' ), array( 'type' => 'text', 'title' => lang('Manage gym'), 'template' => 'h5' ), array( 'type' => 'break' ), array( 'type' => 'button', 'title' => lang('New Gym'), 'action' => 'new' ) ) ) ) ) ); if (Auth::$user['role'] == 'sadmin') $data['modules']['company'] = array( 'model' => $companyModel->purgeModel(), 'settings' => array( 'container' => '#page', 'class' => 'page' ), 'containers' => array( 'modal' => array( 'settings' => array() ), 'form' => array( 'settings' => array() ), 'grid' => array( 'settings' => array( 'actions' => array( array('type' => 'edit', 'text' => lang('Edit'), 'icon' => ''), array('type' => 'delete', 'text' => lang('Delete'), 'icon' => '') ) ) ), 'toolbar' => array( 'settings' => array( 'actions' => array( array( 'type' => 'text', 'title' => lang('Company manager'), 'template' => 'h2' ), array( 'type' => 'text', 'title' => lang('Manage companies'), 'template' => 'h5' ), array( 'type' => 'break' ), array( 'type' => 'button', 'title' => lang('New Company'), 'action' => 'new' ) ) ) ) ) ); // View obect for render content $this->view = new View(); $data['modules'] = $this->view->putDefaults($data['modules']); $data['sidebar'] = array(); // Adds user module to sidebar if (isset($data['modules']['user'])) $data['sidebar'][] = array( 'title' => lang('Manage Users'), 'attr' => array( 'onclick' => 'javascript: ;' ), 'icon' => '', // this must be table name 'module' => 'user' ); if (isset($data['modules']['company'])) $data['sidebar'][] = array( 'title' => lang('Manage Companies'), 'attr' => array( 'onclick' => 'javascript: ;' ), 'icon' => '', 'module' => 'company' ); $data['language'] = $lang; // Sets layout for page inside views folder, check depending css/js $this->view->add('admin/layouts/backend', $data); $this->view->render(); } }
** Now enjoy refresh admin, you can setup several interfaces like next screens: **, (*27)
, (*28)
, (*29)
, (*30)
, (*31)