distilleries / expendable
Expendable is an admin panel base on laravel 5.*. This package give you some implementation do add a content management system of your application. You can override everything. This Cms give view few tools to develop your content management easily and properly.
Installs: 8 759
Dependents: 2
Suggesters: 0
Security: 0
Stars: 29
Watchers: 9
Forks: 8
Open Issues: 0
Language:CSS
Requires
- php: >=7.1.3
- distilleries/datatable-builder: 2.14.*
- distilleries/form-builder: 2.13.*
- distilleries/permission-util: 1.10.*
- distilleries/security: 1.1.*
- forxer/gravatar: ~1.2
- illuminate/database: 5.8.*
- illuminate/support: 5.8.*
- illuminate/translation: 5.8.*
- maatwebsite/excel: 3.1.*
Requires (Dev)
- fzaninotto/faker: ~1.4
- mockery/mockery: ~1.0
- orchestra/database: 3.8.*
- orchestra/testbench: 3.8.*
- orchestra/testbench-browser-kit: 3.8.*
- phpunit/phpunit: ~7.0
- 2.24.1
- 2.24.0
- 2.23.0
- 2.22.1
- 2.22.0
- 2.21.3
- 2.21.2
- 2.21.1
- 2.21.0
- 2.20.1
- 2.20.0
- v2.19.2
- 2.19.1
- 2.19.0
- v2.18.9
- 2.18.8
- 2.18.7
- 2.18.6
- 2.18.5
- 2.18.4
- 2.18.3
- 2.18.2
- 2.18.1
- 2.18.0
- 2.17.0
- 2.16.7
- 2.16.6
- 2.16.5
- 2.16.4
- 2.16.3
- 2.16.2
- 2.16.1
- 2.16.0
- 2.15.0
- 2.14
- 2.13.3
- 2.13.2
- 2.13.1
- 2.13.0
- 2.12.0
- 2.11.0
- 2.10.0
- 2.9.5
- 2.9.4
- 2.9.3
- 2.9.2
- 2.9.1
- 2.9.0
- 2.8.1
- 2.8.0
- 2.7.3
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.14
- 2.6.13
- 2.6.12
- 2.6.11
- 2.6.10
- 2.6.9
- 2.6.8.x-dev
- 2.6.8
- 2.6.7
- 2.6.6
- 2.6.5
- 2.6.4
- 2.6.3
- 2.6.2
- 2.6.1
- 2.6.0
- 2.5.5
- 2.5.4
- 2.5.3
- 2.5.2
- 2.5.1
- 2.5.0
- 2.4.5
- 2.4.4
- 2.4.3
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.0
- 2.2.6
- 2.2.5
- 2.2.4
- 2.2.3
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.6.x-dev
- 2.1.6
- 2.1.5.x-dev
- 2.1.5
- 2.1.4
- 2.1.3
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.2
- 2.0.1
- 2.0.0
- 1.3.2
- 1.3.1
- 1.2.0
- dev-scrutinizer-patch-1
- dev-scrutinizer-patch-2
- dev-release/2.19.2
- dev-release/2.18.9
- dev-release/2.18.8
- dev-release/2.20.1
- dev-release/2.19.1
- dev-release/2.18.7
- dev-develop
- dev-dev
- dev-laravel-4
This package is auto-updated.
Last update: 2024-12-11 03:11:54 UTC
README
Expendable
Expendable is an admin panel base on laravel 5.6.* This package give you some implementation do add a content management system of your application. You can override everything. This Cms give view few tools to develop your content management easily and properly.
Table of contents
- Require
- Installation
- Configurations
- Menu
- State
- Component
- Model
- Global scope
- Permissions
- Views
- Assets (CSS and Javascript)
- Create a new backend module
- Case studies
Require
To use this project you have to install:
- Php 7.1.3 or more
- Active mpcrypt
- Composer https://getcomposer.org/download/
- Sass (
gem install sass
) - NodeJs version v9.4.0
Installation
Add on your composer.json
"require": { "distilleries/expendable": "2.*", }
run composer update
.
Add Application override to bootstrap/app.php
:
$app = new \Distilleries\Expendable\Fondation\Application( realpath(__DIR__ . '/../') ); $app->bind('path.storage', function ($app) { $path = env('STORAGE_PATH', base_path() . DIRECTORY_SEPARATOR . 'storage'); return $path; });
I add a bind event to override the storage path. If you want overrid it juste add STORAGE_PATH=
on your .env. If you don't want override it juste to put it on your .env.
Add Service provider to config/app.php
:
'providers' => [ /* * Package Service Providers... */ Laravel\Tinker\TinkerServiceProvider::class, Distilleries\FormBuilder\FormBuilderServiceProvider::class, Distilleries\DatatableBuilder\DatatableBuilderServiceProvider::class, Distilleries\PermissionUtil\PermissionUtilServiceProvider::class, Maatwebsite\Excel\ExcelServiceProvider::class, Distilleries\Expendable\ExpendableServiceProvider::class, Distilleries\Expendable\ExpendableRouteServiceProvider::class, ]
And Facade (also in config/app.php
) replace the laravel facade Mail
'aliases' => [ /** * Vendor facade * */ 'FormBuilder' => \Distilleries\FormBuilder\Facades\FormBuilder::class, 'Form' => Collective\Html\FormFacade::class, 'HTML' => Collective\Html\HtmlFacade::class, 'Datatable' => \Distilleries\DatatableBuilder\Facades\DatatableBuilder::class, 'PermissionUtil' => \Distilleries\PermissionUtil\Facades\PermissionUtil::class, 'Excel' => \Maatwebsite\Excel\Facades\Excel::class, ]
Replace the service old facade Mail by the new one.
Publish the configuration:
php artisan vendor:publish --provider="Distilleries\Expendable\ExpendableServiceProvider"
Configurations
return [ 'login_uri' => 'admin/login', 'logout_action' => 'Distilleries\Expendable\Http\Controllers\Admin\LoginController@getLogout', 'admin_base_uri' => 'admin', 'config_file_assets' => base_path().'/package.json', 'folder_whitelist' => [ 'moximanager' ], 'listener' => [ '\Distilleries\Expendable\Listeners\UserListener' ], 'mail' => [ 'actions' => [ 'emails.password' ] ], 'menu' => \Distilleries\Expendable\Config\MenuConfig::menu([], 'beginning'), 'menu_left_collapsed' => false, 'state' => [ 'Distilleries\DatatableBuilder\Contracts\DatatableStateContract' => [ 'color' => 'bg-green-haze', 'icon' => 'th-list', 'libelle' => 'expendable::menu.datatable', 'position' => 0, 'action' => 'getIndex' ], 'Distilleries\Expendable\Contracts\OrderStateContract' => [ 'color' => 'bg-grey-cascade', 'icon' => 'resize-vertical', 'libelle' => 'expendable::menu.order_state', 'position' => 1, 'action' => 'getOrder' ], 'Distilleries\Expendable\Contracts\ExportStateContract' => [ 'color' => 'bg-blue-hoki', 'icon' => 'save-file', 'libelle' => 'expendable::menu.export', 'position' => 2, 'action' => 'getExport' ], 'Distilleries\Expendable\Contracts\ImportStateContract' => [ 'color' => 'bg-red-sunglo', 'icon' => 'open-file', 'libelle' => 'expendable::menu.import', 'position' => 3, 'action' => 'getImport' ], 'Distilleries\FormBuilder\Contracts\FormStateContract' => [ 'color' => 'bg-yellow', 'icon' => 'pencil', 'libelle' => 'expendable::menu.add_state', 'position' => 4, 'action' => 'getEdit' ], ] ];
Menu
I use a function to easily merge the default component with the component of the application.
By default you can find on the menu left:
- Email
- List of email
- Add email
- User
- List of user
- Add user
- Role
- List of role
- Add role
- Permission
- Associate permission
- List of service
- Add service
- Synchronize all services
- Language
- List of language
- Add language
By default you can find on the menu task:
- Generate a new component
- Synchronize all services
To add a new item it's easy
'menu' => \Distilleries\Expendable\Config\MenuConfig::menu([ 'left' => [ [ 'icon' => 'send', 'action' => 'Admin\ContactController@getIndex', 'libelle' => 'Contact', 'submenu' => [ [ 'icon' => 'th-list', 'libelle' => 'List of Contact', 'action' => 'Admin\ContactController@getIndex', ], [ 'icon' => 'pencil', 'libelle' => 'Add Contact', 'action' => 'Admin\ContactController@getEdit', ] ] ], ], 'tasks' => [ [ 'icon' => 'console', 'action' => 'Admin\TestController@getIndex', 'libelle' => 'Test', ], ] ], 'beginning'),
The method \Distilleries\Expendable\Config\MenuConfig::menu
tak two parameters
- An array with the content of the meny
['left'=>[],'tasks'=>[]]
- The second one is a string
beginning
orend
to define the direction of the merge.
Example of menu left:
Example of menu task:
State
A state is a part of your controller where you define a list of actions. By default I implemented four states:
- Datatable
- Order
- Export
- Import
- Form
To display the menu of state I provide a class for the interface Distilleries\Expendable\Contracts\StateDisplayerContract
.
$this->app->singleton('Distilleries\Expendable\Contracts\StateDisplayerContract', function ($app) { return new StateDisplayer($app['view'],$app['config']); });
This class check the interface use on your controller and with the config exependable::state
display the logo and the name of the state.
If you want change the state display, just provide a new class for the contract Distilleries\Expendable\Contracts\StateDisplayerContract
.
To display all the element I use a layout manager. you can override it to display what you want.
$this->app->singleton('Distilleries\Expendable\Contracts\LayoutManagerContract', function ($app) { return new LayoutManager($app['config']->get('expendable'), $app['view'], $app['files'], app('Distilleries\Expendable\Contracts\StateDisplayerContract')); });
1. Datatable
A datatable state it's use to display a list of content with filter if you need it.
To use it you have to implement the interface Distilleries\DatatableBuilder\Contracts\DatatableStateContract
.
public function getIndexDatatable(); public function getDatatable();
getIndexDatatable
it's form initilize the datatable.getDatatable
it's for get the data in json.
You can use the trait :
use \Distilleries\Expendable\States\DatatableStateTrait;
On this trait you have a generic implementation to display the datatable and the data. This trait need to use two attributes of your controller:
$datatable
, it's an instance ofEloquentDatatable
(come from DatatableBuilder).model
, it's and instance ofModel
(come from laravel).
Inject them on your constructor:
public function __construct(\Address $model, AddressDatatable $datatable) { $this->datatable = $datatable; $this->model = $model; }
2. Order
Add basic order feature to a component .
Handle Controller
- must implements
\Distilleries\Expendable\Contracts\OrderStateContract
- methods are implemented in
\Distilleries\Expendable\States\OrderStateTrait
Handle Model
- must implements
\Distilleries\Expendable\Contracts\OrderContract
Methods
orderLabel()
must return a string displayed in order page (by using model attributes)orderFieldName()
must return the name of the field where the model persist the order
3. Export
An export state it's to export the data from your model between two dates.
To use it you have to implement the interface Distilleries\Expendable\Contracts\ExportStateContract
.
public function getExport(); public function postExport();
getExport
it's to display the form to select the dates and the type of export.postExport
proceed the export and return the file.
You can use the trait :
use \Distilleries\Expendable\States\ExportStateTrait;
On this trait you have a generic implementation to export your data. This trait need to use on attribute of your controller:
model
, it's and instance ofEloquant
(come from laravel).
Inject them on your constructor:
public function __construct(\Address $model) { $this->model = $model; }
You can change the class provide to export the data. Just add those methods on your service provider and change the class instantiated.
$this->app->singleton('Distilleries\Expendable\Contracts\CsvExporterContract', function ($app) { return new CsvExporter; }); $this->app->singleton('Distilleries\Expendable\Contracts\ExcelExporterContract', function ($app) { return new ExcelExporter; }); $this->app->singleton('Distilleries\Expendable\Contracts\PdfExporterContract', function ($app) { return new PdfExporter; });
4. Import
An import state it's to import the data from a file to your model.
To use it you have to implement the interface Distilleries\Expendable\Contracts\ImportStateContract
.
public function getImport(); public function postImport();
getImport
it's to display the form give the file.postImport
proceed the import and return back.
You can use the trait :
use \Distilleries\Expendable\States\ImportStateTrait;
On this trait you have a generic implementation to export your data. This trait need to use on attribute of your controller:
model
, it's and instance ofEloquant
(come from laravel).
Inject them on your constructor:
public function __construct(\Address $model) { $this->model = $model; }
You can change the class provide to import the data. Just add those methods on your service provider and change the class instantiated.
$this->app->singleton('CsvImporterContract', function ($app) { return new CsvImporter; }); $this->app->singleton('XlsImporterContract', function ($app) { return new XlsImporter; }); $this->app->singleton('XlsxImporterContract', function ($app) { return new XlsImporter; });
5. Form
The form state give you a part to add or edit an element and a part to view the element without edit.
To use it you have to implement the interface Distilleries\FormBuilder\Contracts\FormStateContract
.
public function getEdit($id); public function postEdit(); public function getView($id);
getEdit
it's to display the form to edit or add new item.postEdit
proceed the save or update.getView
Display the form in not editable.
You can use the trait :
use \Distilleries\Expendable\States\FormStateTrait;
On this trait you have a generic implementation to display form, save and display view. This trait need to use two attributes of your controller:
model
, it's and instance ofEloquant
(come from laravel).form
, it's and instance ofForm
(come from FormBuilder).
Inject them on your constructor:
public function __construct(\Address $model, AddressForm $form) { $this->form = $form; $this->model = $model; }
Component
A component is just a composition of controller, form, datatable, model.
To create a new component you can go in /admin/component/edit
and fill the form, or use the command line:
php artisan expendable:component.make app/controllers/Admin/TestController
You can check the options with the help.
In the backend you have all this options:
To know all the types of fields you can have look the documentation.
Admin BaseComponent
By default if you check all the state that generate a controller inheritance from Distilleries\Expendable\Http\Controllers\Admin\Base\BaseComponent
.
This controller implement all the states interfaces.
use Distilleries\DatatableBuilder\Contracts\DatatableStateContract; use Distilleries\Expendable\Contracts\ExportStateContract; use Distilleries\Expendable\Contracts\ImportStateContract; use Distilleries\Expendable\States\DatatableStateTrait; use Distilleries\Expendable\States\ExportStateTrait; use Distilleries\Expendable\States\FormStateTrait; use Distilleries\Expendable\States\ImportStateTrait; use Distilleries\FormBuilder\Contracts\FormStateContract; class BaseComponent extends ModelBaseController implements FormStateContract, DatatableStateContract, ExportStateContract, ImportStateContract { use FormStateTrait; use ExportStateTrait; use DatatableStateTrait; use ImportStateTrait; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ public function getIndex() { return $this->getIndexDatatable(); } }
Admin ModelBaseController
If you don't want use all the state and you use a model just extend Distilleries\Expendable\Http\Controllers\Admin\Base\ModelBaseController
.
Example:
use Distilleries\Expendable\Contracts\LayoutManagerContract; use Distilleries\Expendable\Models\BaseModel; use Illuminate\Http\Request; class ModelBaseController extends BaseController { /** * @var \Distilleries\Expendable\Models\BaseModel $model * Injected by the constructor */ protected $model; // ------------------------------------------------------------------------------------------------ public function __construct(BaseModel $model, LayoutManagerContract $layoutManager) { parent::__construct($layoutManager); $this->model = $model; } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ public function putDestroy(Request $request) { $validation = \Validator::make($request->all(), [ 'id' => 'required' ]); if ($validation->fails()) { return redirect()->back()->withErrors($validation)->withInput($request->all()); } $data = $this->model->find($request->get('id')); $data->delete(); return redirect()->to(action('\\'.get_class($this) . '@getIndex')); } }
Admin BaseController
If you don't want use all the state and you don't use a model just extend Distilleries\Expendable\Http\Controllers\Admin\Base\BaseController
.
You just have to inject the LayoutManagerContract
Example:
use Distilleries\Expendable\Contracts\LayoutManagerContract; use Distilleries\Expendable\Http\Controllers\Controller; class BaseController extends Controller { protected $layoutManager; protected $layout = 'expendable::admin.layout.default'; // ------------------------------------------------------------------------------------------------ public function __construct(LayoutManagerContract $layoutManager) { $this->layoutManager = $layoutManager; $this->setupLayout(); } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ protected function setupLayout() { $this->layoutManager->setupLayout($this->layout); $this->setupStateProvider(); $this->initStaticPart(); } // ------------------------------------------------------------------------------------------------ protected function setupStateProvider() { $interfaces = class_implements($this); $this->layoutManager->initInterfaces($interfaces, get_class($this)); } // ------------------------------------------------------------------------------------------------ protected function initStaticPart() { $this->layoutManager->initStaticPart(function ($layoutManager) { $menu_top = $layoutManager->getView()->make('expendable::admin.menu.top'); $menu_left = $layoutManager->getView()->make('expendable::admin.menu.left'); $layoutManager->add([ 'state.menu' => $layoutManager->getState()->getRenderStateMenu(), 'menu_top' => $menu_top, 'menu_left' => $menu_left ]); }); } }
Model
By default you can extend \Distilleries\Expendable\Models\BaseModel
, this one extend \Illuminate\Database\Eloquent\Model
.
On it you have some method you can use:
public static function getChoice(); public function scopeSearch($query, $searchQuery); public function getAllColumnsNames(); public function scopeBetweenCreate($query, $start, $end); public function scopeBetweenupdate($query, $start, $end);
Global scope
I provide some global scope usable on the model.
Status
If you want display an element only if your are connected use this scope. The model check if the user is not connected and if the status equal online (1).
To use it add the trait on your model use \Distilleries\Expendable\Models\StatusTrait;
Permissions
The system of permission is base on the public method of all your controller.
To generate the list of all services use the Synchronize all services
(/admin/service/synchronize
).
That use all the controller and get the public actions.
If you go on Associate Permission
you have the list of controller with all methods:
On this page you can allow a role to the method.
By default Expendable use Distilleries\PermissionUtil
package and add the good middleware in his kernel.
You don't have to configure something.
If the role is not allowed the application dispatch an error 403:
if(!PermissionUtil::hasAccess('Controller@action')){ App::abort(403, Lang::get('expendable::errors.unthorized')); }
Views
To override the view publish them with command line:
php artisan vendor:publish --provider="Distilleries\Expendable\ExpendableServiceProvider" --tag="views"
Assets (CSS and Javascript)
All the assets are one the folder resources/assets
.
Sass
To use the sass file just add bootstrap and application.admin.scss
on your admin file scss.
If you check the repo Xyz you have a folder assets.
I use the same structure.
// // Third-parties // @import "../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; @import "../../../../node_modules/font-awesome/scss/font-awesome"; // // Expendable // @import "../../../../vendor/distilleries/expendable/src/resources/assets/backend/sass/application.admin"; @import "../../../../vendor/distilleries/expendable/src/resources/assets/backend/sass/admin/layout/themes/grey";
Images
The images are copy by mix script when they are found in sass file
Javascript
The javascript is compiled by the mix
Composer
I update my composer json to add the npm install and gulp generation when I update my libraries.
"post-update-cmd": [ "php artisan clear-compiled", "php artisan optimize", "php artisan down", "npm install", "php artisan migrate --force", "npm run production", "php artisan up" ],
Create a new backend module
- Generate your migration.
- Generate your model.
- Generate you component.
- Add your controller in the routes file.
Case studies
Try to create a blog post component. I use a fresh install of Xyz
1. Generate your migration
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function(Blueprint $table) { $table->increments('id'); $table->string('libelle'); $table->text('content')->nullable(); $table->tinyInteger('status'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('posts'); } }
php artisan migrate
2. Generate your model
use Distilleries\Expendable\Models\BaseModel; class Post extends BaseModel { use \Distilleries\Expendable\Models\StatusTrait; protected $fillable = [ 'id', 'libelle', 'content', 'status', ]; }
3 Generate you component
I use the backend generator /admin/component/edit
.
Datatable:
<?php namespace App\Datatables; use Distilleries\DatatableBuilder\EloquentDatatable; class PostDatatable extends EloquentDatatable { public function build() { $this ->add('id',null,trans('datatable.id')) ->add('libelle',null,trans('datatable.libelle')); $this->addDefaultAction(); } }
Form:
This file is generated:
<?php namespace App\Forms; use Distilleries\FormBuilder\FormValidator; class PostForm extends FormValidator { public static $rules = []; public static $rules_update = null; public function buildForm() { $this ->add('id', 'hidden') ->add('libelle', 'text') ->add('content', 'tinymce') ->add('status', 'choice'); $this->addDefaultActions(); } }
You have to update it for give a value for the choice and give the rules for the validation:
<?php namespace App\Forms; use Distilleries\Expendable\Helpers\StaticLabel; use Distilleries\FormBuilder\FormValidator; class PostForm extends FormValidator { public static $rules = [ 'libelle' => 'required', 'status' => 'required|integer' ]; public static $rules_update = null; public function buildForm() { $this ->add('id', 'hidden') ->add('libelle', 'text') ->add('status', 'choice', [ 'choices' => StaticLabel::status(), 'empty_value' => '-', 'validation' => 'required', 'label' => 'Status' ]); $this->addDefaultActions(); } }
Controller:
<?php namespace App\Http\Controllers\Admin; use Distilleries\Expendable\Contracts\LayoutManagerContract; use Distilleries\Expendable\Http\Controllers\Admin\Base\BaseComponent; use App\Forms\PostForm; use App\Datatables\PostDatatable; class PostController extends BaseComponent { // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ public function __construct(PostDatatable $datatable, PostForm $form, \App\Post $model, LayoutManagerContract $layoutManager) { parent::__construct($model, $layoutManager); $this->datatable = $datatable; $this->form = $form; } }
4 Add your controller in the routes file
I add Route::controller('post', 'Admin\PostController');
on the route file:
<?php use \Route; Route::get('/', 'HomeController@index'); Route::group(array('middleware' => 'auth'), function () { Route::group(array('before' => 'permission', 'prefix' => config('expendable::admin_base_uri')), function () { Route::controller('post', 'Admin\PostController'); }); });
5 Add to the menu
On config/expendable.php
id add the Post entry:
'menu' => \Distilleries\Expendable\Config\MenuConfig::menu([ 'left' => [ [ 'icon' => 'pushpin', 'action' => 'Admin\PostController@getIndex', 'libelle' => 'Post', 'submenu' => [ [ 'icon' => 'th-list', 'libelle' => 'List of Post', 'action' => 'Admin\PostController@getIndex', ], [ 'icon' => 'pencil', 'libelle' => 'Add Post', 'action' => 'Admin\PostController@getEdit', ] ] ], ] ], 'beginning'),