File: README.md
Sponsor
Requirements
- PHP >= 7.0.0
- Laravel >= 5.5.0
- Fileinfo PHP Extension
Installation
This package requires PHP 7+ and Laravel 5.5, for old versions please refer to 1.4
First, install laravel 5.5, and make sure that the database connection settings are correct.
composer require encore/laravel-adminThen run these commands to publish assets and config:
php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider"After run command you can find config file in config/admin.php, in this file you can change the install directory,db connection or table names.
At last run following command to finish install.
php artisan admin:installOpen http://localhost/admin/ in browser,use username admin and password admin to login.
Configurations
The file config/admin.php contains an array of configurations, you can find the default configurations in there.
Right to left support
just go to this path <YOUR_PROJECT_PATH>\vendor\encore\laravel-admin\src\Traits\HasAssets.php and modify $baseCss array for loading right to left (rtl) version of bootstap and AdminLTE css files.
bootstrap.min.css change it to bootstrap.rtl.min.css
AdminLTE.min.css change it to AdminLTE.rtl.min.css
Extensions
| Extension | Description | laravel-admin |
|---|---|---|
| helpers | Several tools to help you in development | ~1.5 |
| media-manager | Provides a web interface to manage local files | ~1.5 |
| api-tester | Help you to test the local laravel APIs | ~1.5 |
| scheduling | Scheduling task manager for laravel-admin | ~1.5 |
| redis-manager | Redis manager for laravel-admin | ~1.5 |
| backup | An admin interface for managing backups | ~1.5 |
| log-viewer | Log viewer for laravel | ~1.5 |
| config | Config manager for laravel-admin | ~1.5 |
| reporter | Provides a developer-friendly web interface to view the exception | ~1.5 |
| wangEditor | A rich text editor based on wangeditor | ~1.6 |
| summernote | A rich text editor based on summernote | ~1.6 |
| china-distpicker | 一个基于distpicker的中国省市区选择器 | ~1.6 |
| simplemde | A markdown editor based on simplemde | ~1.6 |
| phpinfo | Integrate the phpinfo page into laravel-admin |
~1.6 |
| php-editor python-editor js-editor css-editor clike-editor | Several programing language editor extensions based on code-mirror | ~1.6 |
| star-rating | Star Rating extension for laravel-admin | ~1.6 |
| json-editor | JSON Editor for Laravel-admin | ~1.6 |
| grid-lightbox | Turn your grid into a lightbox & gallery | ~1.6 |
| daterangepicker | Integrates daterangepicker into laravel-admin | ~1.6 |
| material-ui | Material-UI extension for laravel-admin | ~1.6 |
| sparkline | Integrates jQuery sparkline into laravel-admin | ~1.6 |
| chartjs | Use Chartjs in laravel-admin | ~1.6 |
| echarts | Use Echarts in laravel-admin | ~1.6 |
| simditor | Integrates simditor full-rich editor into laravel-admin | ~1.6 |
| cropper | A simple jQuery image cropping plugin. | ~1.6 |
| composer-viewer | A web interface of composer packages in laravel. | ~1.6 |
| data-table | Advanced table widget for laravel-admin | ~1.6 |
| watermark | Text watermark for laravel-admin | ~1.6 |
| google-authenticator | Google authenticator | ~1.6 |
Contributors
This project exists thanks to all the people who contribute. [Contribute].
Backers
Thank you to all our backers! 🙏 [Become a backer]
Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]
Other
laravel-admin based on following plugins or services:
- Laravel
- AdminLTE
- Datetimepicker
- font-awesome
- moment
- Google map
- Tencent map
- bootstrap-fileinput
- jquery-pjax
- Nestable
- toastr
- X-editable
- bootstrap-number-input
- fontawesome-iconpicker
- sweetalert2
License
laravel-admin is licensed under The MIT License (MIT).
File: CHANGELOG.md
Changelog
See https://laravel-admin.org/docs/
File: composer.json
{
"name": "encore/laravel-admin",
"description": "laravel admin",
"type": "library",
"keywords": ["laravel", "admin", "grid", "form"],
"homepage": "https://github.com/z-song/laravel-admin",
"license": "MIT",
"authors": [
{
"name": "zsong",
"email": "[email protected]"
}
],
"require": {
"php": ">=7.0.0",
"symfony/dom-crawler": "~3.1|~4.0|~5.0",
"laravel/framework": ">=5.5",
"doctrine/dbal": "2.|3."
},
"require-dev": {
"laravel/laravel": ">=5.5",
"fzaninotto/faker": "~1.4",
"intervention/image": "~2.3",
"laravel/browser-kit-testing": "^6.0",
"spatie/phpunit-watcher": "^1.22.0"
},
"autoload": {
"psr-4": {
"Encore\Admin\": "src/"
},
"files": [
"src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Tests\Models\": "tests/models",
"Tests\Controllers\": "tests/controllers"
},
"classmap": [
"tests/TestCase.php"
]
},
"scripts": {
"test": "./vendor/bin/phpunit"
},
"suggest": {
"intervention/image": "Required to handling and manipulation upload images (~2.3).",
"spatie/eloquent-sortable": "Required to built orderable gird."
},
"extra": {
"laravel": {
"providers": [
"Encore\Admin\AdminServiceProvider"
],
"aliases": {
"Admin": "Encore\Admin\Facades\Admin"
}
}
}
}
File: CONTRIBUTING.md
Contribute
Introduction
First, thank you for considering contributing to laravel-admin! It's people like you that make the open source community such a great community! 😊
We welcome any type of contribution, not only code. You can help with
- QA: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- Marketing: writing blog posts, howto's, printing stickers, ...
- Community: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- Code: take a look at the open issues. Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
- Money: we welcome financial contributions in full transparency on our open collective.
Your First Contribution
Working on your first Pull Request? You can learn how from this free series, How to Contribute to an Open Source Project on GitHub.
Submitting code
Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
Code review process
The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
Financial contributions
We also welcome financial contributions in full transparency on our open collective.
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
Questions
If you have any questions, create an issue (protip: do a quick search first to see if someone else didn't ask the same question before!).
You can also reach us at [email protected].
Credits
Contributors
Thank you to all the people who have already contributed to laravel-admin!
Backers
Thank you to all our backers! [Become a backer]
Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor)
File: FUNDING.yml
These are supported funding model platforms
open_collective: laravel-admin
custom: http://laravel-admin.org/donate
File: .travis.yml
language: php
php:
- 7.2
- 7.3
- 7.4
- 8.0
matrix:
fast_finish: true
allow_failures:
- php: hhvm
sudo: false
services:
- mysql
before_script:
- mysql -e 'create database if not exists laravel_admin_test;'
- travis_retry composer self-update
- travis_retry composer install --no-interaction
script:
- composer test
File: docs/issue_template.md
- Laravel Version: #.#.#
- PHP Version:
- Laravel-admin: #.#.#
Steps To Reproduce:
File: docs/en/README.md
laravel-admin
laravel-admin is administrative interface builder for laravel which can help you build CRUD backends just with few lines of code.
Demo use username/password:admin/admin
Inspired by SleepingOwlAdmin and rapyd-laravel.
Screenshots
Installation
This package requires PHP 7+ and Laravel 5.5, for old versions please refer to 1.4
First, install laravel 5.5, and make sure that the database connection settings are correct.
composer require encore/laravel-admin 1.5.*Then run these commands to publish assets and config:
php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider"After run command you can find config file in config/admin.php, in this file you can change the install directory,db connection or table names.
At last run following command to finish install.
php artisan admin:installOpen http://localhost/admin/ in browser,use username admin and password admin to login.
Default Settings
The file in config/admin.php contains an array of settings, you can find the default settings in there.
Other
laravel-admin based on following plugins or services:
- Laravel
- AdminLTE
- Datetimepicker
- font-awesome
- moment
- Google map
- Tencent map
- bootstrap-fileinput
- jquery-pjax
- Nestable
- toastr
- X-editable
- bootstrap-number-input
- fontawesome-iconpicker
License
laravel-admin is licensed under The MIT License (MIT).
File: docs/en/_sidebar.md
- Getting started
- Model grid
- Model form
- Model-tree
- Admin extensions
- Widgets
- Permissions
- Custom authentication
- Custom Navbar
- Custom chart
- Helpers
- Upgrade precautions
- Change log
File: docs/en/change-log.md
Change log
v1.2.9、v1.3.3、v1.4.1
- Add user settings and modify avatar function
- Embedded form support
- Support for customize navigation bar (upper right corner)
- Add scaffolding, database command line tool, web artisan help tool
- Support for customize login page and login logic
- The form supports setting the width and setting the action
- Optimize table filters
- Fix bugs, optimize code and logic
File: docs/en/content-layout.md
Page content
The layout usage of laravel-admin can be found in the index() method of the home page's layout file HomeController.php.
The Encore\Admin\Layout\Content class is used to implement the layout of the content area. The Content::body ($element) method is used to add page content:
The page code for an unfilled content is as follows:
public function index()
{
return Admin::content(function (Content $content) {
// optional
$content->header('page header');
// optional
$content->description('page description');
// add breadcrumb since v1.5.7
$content->breadcrumb(
['text' => 'Dashboard', 'url' => '/admin'],
['text' => 'User management', 'url' => '/admin/users'],
['text' => 'Edit user']
);
// Fill the page body part, you can put any renderable objects here
$content->body('hello world');
});
}Method $content->body(); can accepts any renderable objects, like string, number, class that has method __toString, or implements Renderable、Htmlable interface , include Laravel View objects.
Layout
laravel-admin use grid system of bootstrap,The length of each line is 12, the following is a few simple examples:
Add a line of content:
$content->row('hello')
---------------------------------
|hello |
| |
| |
| |
| |
| |
---------------------------------Add multiple columns within the line:
$content->row(function(Row $row) {
$row->column(4, 'foo');
$row->column(4, 'bar');
$row->column(4, 'baz');
});
----------------------------------
|foo |bar |baz |
| | | |
| | | |
| | | |
| | | |
| | | |
----------------------------------
$content->row(function(Row $row) {
$row->column(4, 'foo');
$row->column(8, 'bar');
});
----------------------------------
|foo |bar |
| | |
| | |
| | |
| | |
| | |
----------------------------------Column in the column:
$content->row(function (Row $row) {
$row->column(4, 'xxx');
$row->column(8, function (Column $column) {
$column->row('111');
$column->row('222');
$column->row('333');
});
});
----------------------------------
|xxx |111 |
| |---------------------|
| |222 |
| |---------------------|
| |333 |
| | |
----------------------------------Add rows in rows and add columns:
$content->row(function (Row $row) {
$row->column(4, 'xxx');
$row->column(8, function (Column $column) {
$column->row('111');
$column->row('222');
$column->row(function(Row $row) {
$row->column(6, '444');
$row->column(6, '555');
});
});
});
----------------------------------
|xxx |111 |
| |---------------------|
| |222 |
| |---------------------|
| |444 |555 |
| | | |
----------------------------------Add body into a page:
Create a blade view file inside /project/resources/views/admin/custom.blade.php
public function customPage($id)
{
$content = new Content();
$content->header('View');
$content->description('Description...');
$content->body('admin.custom',['id' => $id]);
return $content;
}File: docs/en/custom-authentication.md
Custom authentication
If you do not use the laravel-admin built-in authentication login logic, you can refer to the following way to customize the login authentication logic.
First of all, you need define a User provider, used to obtain the user identity, such as app/Providers/CustomUserProvider.php:
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class CustomUserProvider implements UserProvider
{
public function retrieveById($identifier)
{}
public function retrieveByToken($identifier, $token)
{}
public function updateRememberToken(Authenticatable $user, $token)
{}
public function retrieveByCredentials(array $credentials)
{
// Use $credentials to get the user data, and then return an object implements interface `Illuminate\Contracts\Auth\Authenticatable`
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Verify the user with the username password in $ credentials, return `true` or `false`
}
}In the methods retrieveByCredentials and validateCredentials the parameter $credentials is the user name and password array submitted on the login page, you can use $credentials to implement your own login logic.
The definition of interface Illuminate\Contracts\Auth\Authenticatable:
<?php
namespace Illuminate\Contracts\Auth;
interface Authenticatable {
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}For more details about custom authentication please refer to adding-custom-user-providers.
After you created cusom user provider, you will need to extend Laravel with it:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('custom', function ($app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new CustomUserProvider();
});
}
}Finally modify the configuration, open config/admin.php, find the auth part:
'auth' => [
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'admin',
]
],
// Modify the following
'providers' => [
'admin' => [
'driver' => 'custom',
]
],
],This completes the logic of custom authentication.
File: docs/en/custom-chart.md
Custom chart
laravel-admin 1.5 has removed all the chart components. If you want to add chart components to the page, you can refer to the following process
Use chartjs for example, first download chartjs, put it under the public directory, such as in the public/vendor/chartjs directory
Then import the component in app/Admin/bootstrap.php:
use Encore\Admin\Facades\Admin;
Admin::js('/vendor/chartjs/dist/Chart.min.js');Create a new view file resources/views/admin/charts/bar.blade.php
<canvas id="myChart" width="400" height="400"></canvas>
<script>
$(function () {
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
});
</script>And then you can introduce this chart view anywhere on the page:
public function index()
{
return Admin::content(function (Content $content) {
$content->header('chart');
$content->description('.....');
$content->body(view('admin.charts.bar'));
});
}In the above way you can introduce any chart library. multi-chart page layout, refer to [view layout] (/en/layout.md)
File: docs/en/custom-navbar.md
Customize the head navigation bar
Since version 1.5.6, you can add the html element to the top navigation bar, open app/Admin/bootstrap.php:
use Encore\Admin\Facades\Admin;
Admin::navbar(function (\Encore\Admin\Widgets\Navbar $navbar) {
$navbar->left('html...');
$navbar->right('html...');
});Methods left and right are used to add content to the left and right sides of the head, the method parameters can be any object that can be rendered (objects which impletements Htmlable, Renderable, or has method __toString()) or strings.
Add elements to the left
For example, add a search bar on the left, first create a view resources/views/search-bar.blade.php:
<style>
.search-form {
width: 250px;
margin: 10px 0 0 20px;
border-radius: 3px;
float: left;
}
.search-form input[type="text"] {
color: #666;
border: 0;
}
.search-form .btn {
color: #999;
background-color: #fff;
border: 0;
}
</style>
<form action="/admin/posts" method="get" class="search-form" pjax-container>
<div class="input-group input-group-sm ">
<input type="text" name="title" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i></button>
</span>
</div>
</form>Then add it to the head navigation bar:
$navbar->left(view('search-bar'));Add elements to the right
You can only add the <li> tag on the right side of the navigation, such as adding some prompt icons, creating a new rendering class app/Admin/Extensions/Nav/Links.php
<?php
namespace App\Admin\Extensions\Nav;
class Links
{
public function __toString()
{
return <<<HTML
<li>
<a href="#">
<i class="fa fa-envelope-o"></i>
<span class="label label-success">4</span>
</a>
</li>
<li>
<a href="#">
<i class="fa fa-bell-o"></i>
<span class="label label-warning">7</span>
</a>
</li>
<li>
<a href="#">
<i class="fa fa-flag-o"></i>
<span class="label label-danger">9</span>
</a>
</li>
HTML;
}
}Then add it to the head navigation bar:
$navbar->right(new \App\Admin\Extensions\Nav\Links());Or use the following html to add a drop-down menu:
<li class="dropdown notifications-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i class="fa fa-bell-o"></i>
<span class="label label-warning">10</span>
</a>
<ul class="dropdown-menu">
<li class="header">You have 10 notifications</li>
<li>
<!-- inner menu: contains the actual data -->
<ul class="menu">
<li>
<a href="#">
<i class="fa fa-users text-aqua"></i> 5 new members joined today
</a>
</li>
<li>
<a href="#">
<i class="fa fa-warning text-yellow"></i> Very long description here that may not fit into the
page and may cause design problems
</a>
</li>
<li>
<a href="#">
<i class="fa fa-users text-red"></i> 5 new members joined
</a>
</li>
<li>
<a href="#">
<i class="fa fa-shopping-cart text-green"></i> 25 sales made
</a>
</li>
<li>
<a href="#">
<i class="fa fa-user text-red"></i> You changed your username
</a>
</li>
</ul>
</li>
<li class="footer"><a href="#">View all</a></li>
</ul>
</li>More components can be found here Bootstrap
File: docs/en/extension-api-tester.md
Laravel API tester
api-tester is an API testing tool developed for laravel that helps you test your laravel API like postman.
Installation
$ composer require laravel-admin-ext/api-tester -vvv
$ php artisan vendor:publish --tag=api-testerAnd then run the following command to import menus and permissions (which can also be added manually)
$ php artisan admin:import api-testerThen you can find the entry link in the admin menu, http://localhost/admin/api-tester.
Usage
Open routes/api.php try to add an api:
Route::get('test', function () {
return 'hello world';
});Open the api-tester page, you can see api/test on the left, select it and click the Send button to send request to the api
Login as
Login as Fill in the user id you want to log in, you can log in as the user to request the API, add the following API:
use Illuminate\Http\Request;
Route::middleware('auth:api')->get('user', function (Request $request) {
return $request->user();
});Fill in the user ID in Login as input , then request the api and will respond with the user's model
Parameters
Used to set the request parameters for api , the type can be a string or file, add the following API:
use Illuminate\Http\Request;
Route::get('parameters', function (Request $request) {
return $request->all();
});Fill in the parameters send request and you can see the results
File: docs/en/extension-config.md
Configuration management
This tool will store the configuration data in the database
Installation
$ composer require laravel-admin-ext/config
$ php artisan migrateOpen app/Providers/AppServiceProvider.php, and call the Config::load() method within the boot method:
<?php
namespace App\Providers;
use Encore\Admin\Config\Config;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Config::load(); // Add this
}
}Then run the command to import menus and permissions (which can also be added manually)
$ php artisan admin:import configOpen http://localhost/admin/config.
Usage
After add config in the panel, use config($key) to get value you configured.
File: docs/en/extension-helpers.md
Helpers
Added support for developers, available in development to help improve efficiency, currently providing scaffolding, database command line and artisan command line three tools, if there are better other utilities The idea of welcome to provide advice.
Installation:
composer require laravel-admin-ext/helpers
php artisan admin:import helpersPart of the function of the tool will create or delete files in the project, there may be some file or directory permissions errors, the problem needs to be resolved.
Another part of the database and artisan command can not be used in the web environment.
Scaffold
This Tool can help you build controller, model, migrate files, and run migration files.
access by visit http://localhost/admin/helpers/scaffold.
Which set the migration table structure, the primary key field is automatically generated do not need to fill out.
Database command line
Database command line tool for web integration,Currently supports mysql, mongodb and redis,access by visit http://localhost/admin/helpers/terminal/database.
Change the database connection in the upper right corner, and then in the bottom of the input box to enter the corresponding database query and then enter, you can get the query results:
The use of the database and the operation of the database is consistent, you can run the selected database support query.
Artisan command line
Web version of Laravel's artisan command line,you can run artisan commands in it,access it by visit http://localhost/admin/helpers/terminal/artisan.
Route list
This tool can use more intuitive to show all the routes, including uri, http methods and middleware, and also you can query routes. access it by visithttp://localhost/admin/helpers/routes.
File: docs/en/extension-media-manager.md
Media manager
This tool for manage local files
Installation
$ composer require laravel-admin-ext/media-manager -vvv
$ php artisan admin:import media-managerConfiguration
Open config/admin.php specify the disk you want to manage
'extensions' => [
'media-manager' => [
'disk' => 'public' // Points to the disk set in config/filesystem.php
],
],disk is the local disk you configured in config/filesystem.php, visit by access http://localhost/admin/media.
Note If you want to preview the picture in the disk, you must set the access url in the disk configuration:
config/filesystem.php:
'disks' => [
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage', // set url
'visibility' => 'public',
],
...
]File: docs/en/extension-scheduling.md
Task scheduling
This tool is a web interface for manage Laravel's scheduled tasks
Installation
$ composer require laravel-admin-ext/scheduling -vvv
$ php artisan admin:import schedulingThen open http://localhost/admin/scheduling
Add tasks
Open app/Console/Kernel.php, try adding two scheduled tasks:
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
$schedule->command('inspire')->everyTenMinutes();
$schedule->command('route:list')->dailyAt('02:00');
}
}And then you can see the tasks with details in the page, and you can also directly run these two tasks in the page.
File: docs/en/installation.md
Installation
This package requires PHP 7+ and Laravel 5.5, for old versions please refer to 1.4
First, install laravel, and make sure that the database connection settings are correct.
Then install require this package with command:
composer require encore/laravel-admin "1.5.*"Publish assets and config with command:
php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider"After runnung previous command you can find config file in config/admin.php, in this file you can change default install directory (/app/Admin), db connection or table names.
At last run following command to finish install:
php artisan admin:installTo check that all is working, run php artisan serve and open http://localhost/admin/ in browser, use username admin and password admin to login.
Generated files
After the installation is complete, the following files are generated in the project directory:
Configuration file
After the installation is complete, all configurations are in the config/admin.php file.
Admin files
After install,you can find directoryapp/Admin,and then most of our develop work is under this directory.
app/Admin
├── Controllers
│ ├── ExampleController.php
│ └── HomeController.php
├── bootstrap.php
└── routes.phpapp/Admin/routes.php is used to define routes.
app/Admin/bootstrap.php is bootstrapper for laravel-admin, for usage examples see comments inside it.
The app/Admin/Controllers directory is used to store all the controllers.
The HomeController.php file under this directory is used to handle home request of admin.
The ExampleController.php file is a controller example.
Static assets
The front-end static files are in the /public/packages/admin directory.
File: docs/en/LICENSE.md
The MIT License (MIT)
Copyright (c) 2015 Jens Segers
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File: docs/en/model-form-callback.md
Model form callback
model-form currently has three methods for receiving callback functions:
// callback after form submission
$form->submitted(function (Form $form) {
//...
});
// callback before save
$form->saving(function (Form $form) {
//...
});
// callback after save
$form->saved(function (Form $form) {
//...
});If required, you can add additional fields to ignore using the submitted function e.g.
$form->submitted(function (Form $form) {
$form->ignore('username');
});The form data that is currently submitted can be retrieved from the callback parameter $form:
$form->saving(function (Form $form) {
dump($form->username);
});Get data in model
$form->saved(function (Form $form) {
$form->model()->id;
});Can redirect other urls by returning an instance of Symfony\Component\HttpFoundation\Response directly in the callback:
$form->saving(function (Form $form) {
// returns a simple response
return response('xxxx');
});
$form->saving(function (Form $form) {
// redirect url
return redirect('/admin/users');
});
$form->saving(function (Form $form) {
// throws an exception
throw new \Exception('Error friends. . .');
});Return error or success information on the page:
use Illuminate\Support\MessageBag;
// redirect back with an error message
$form->saving(function ($form) {
$error = new MessageBag([
'title' => 'title...',
'message' => 'message....',
]);
return back()->with(compact('error'));
});
// redirect back with a successful message
$form->saving(function ($form) {
$success = new MessageBag([
'title' => 'title...',
'message' => 'message....',
]);
return back()->with(compact('success'));
});File: docs/en/model-form-field-management.md
Fields management
Remove field
The built-in map and editor fields requires the front-end files via cdn, and if there are problems with the network, they can be removed in the following ways
Locate the file app/Admin/bootstrap.php. If the file does not exist, update laravel-admin and create this file.
<?php
use Encore\Admin\Form;
Form::forget('map');
Form::forget('editor');
// or
Form::forget(['map', 'editor']);This removes the two fields, which can be used to remove the other fields.
Extend the custom field
Extend a PHP code editor based on codemirror with the following steps.
see PHP mode.
Download and unzip the codemirror library to the front-end resource directory, for example, in the directory public/packages/codemirror-5.20.2.
Create a new field class app/Admin/Extensions/PHPEditor.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Form\Field;
class PHPEditor extends Field
{
protected $view = 'admin.php-editor';
protected static $css = [
'/packages/codemirror-5.20.2/lib/codemirror.css',
];
protected static $js = [
'/packages/codemirror-5.20.2/lib/codemirror.js',
'/packages/codemirror-5.20.2/addon/edit/matchbrackets.js',
'/packages/codemirror-5.20.2/mode/htmlmixed/htmlmixed.js',
'/packages/codemirror-5.20.2/mode/xml/xml.js',
'/packages/codemirror-5.20.2/mode/javascript/javascript.js',
'/packages/codemirror-5.20.2/mode/css/css.js',
'/packages/codemirror-5.20.2/mode/clike/clike.js',
'/packages/codemirror-5.20.2/mode/php/php.js',
];
public function render()
{
$this->script = <<<EOT
CodeMirror.fromTextArea(document.getElementById("{$this->id}"), {
lineNumbers: true,
mode: "text/x-php",
extraKeys: {
"Tab": function(cm){
cm.replaceSelection(" " , "end");
}
}
});
EOT;
return parent::render();
}
}Static resources in the class can also be imported from outside, see Editor.php
Create a view file resources/views/admin/php-editor.blade.php:
<div class="form-group {!! !$errors->has($label) ?: 'has-error' !!}">
<label for="{{$id}}" class="col-sm-2 control-label">{{$label}}</label>
<div class="col-sm-6">
@include('admin::form.error')
<textarea class="form-control" id="{{$id}}" name="{{$name}}" placeholder="{{ trans('admin::lang.input') }} {{$label}}" {!! $attributes !!} >{{ old($column, $value) }}</textarea>
</div>
</div>Finally, find the file app/Admin/bootstrap.php, if the file does not exist, update laravel-admin, and then create this file, add the following code:
<?php
use App\Admin\Extensions\PHPEditor;
use Encore\Admin\Form;
Form::extend('php', PHPEditor::class);And then you can use PHP editor in model-form:
$form->php('code');In this way, you can add any form fields you want to add.
Integrate CKEditor
Here is another example to show you how to integrate ckeditor.
At first download CKEditor, unzip to public directory, for example public/packages/ckeditor/.
Then Write Extension class app/Admin/Extensions/Form/CKEditor.php:
<?php
namespace App\Admin\Extensions\Form;
use Encore\Admin\Form\Field;
class CKEditor extends Field
{
public static $js = [
'/packages/ckeditor/ckeditor.js',
'/packages/ckeditor/adapters/jquery.js',
];
protected $view = 'admin.ckeditor';
public function render()
{
$this->script = "$('textarea.{$this->getElementClass()}').ckeditor();";
return parent::render();
}
}Add blade file resources/views/admin/ckeditor.blade.php for view admin.ckeditor :
<div class="form-group {!! !$errors->has($errorKey) ?: 'has-error' !!}">
<label for="{{$id}}" class="col-sm-2 control-label">{{$label}}</label>
<div class="col-sm-6">
@include('admin::form.error')
<textarea class="form-control {{$class}}" id="{{$id}}" name="{{$name}}" placeholder="{{ $placeholder }}" {!! $attributes !!} >{{ old($column, $value) }}</textarea>
@include('admin::form.help-block')
</div>
</div>Register this extension in app/Admin/bootstrap.php:
use Encore\Admin\Form;
use App\Admin\Extensions\Form\CKEditor;
Form::extend('ckeditor', CKEditor::class);After this you can use ckeditor in your form:
$form->ckeditor('content');File: docs/en/model-form-fields.md
Builtin form fields
There are a lots of form components built into the model-form to help you quickly build forms.
Public methods
Set the value to save
$form->text('title')->value('text...');Set default value
$form->text('title')->default('text...');Set help message
$form->text('title')->help('help...');Set fa-icon class
$form->text('title')->icon('fa-copy');Set attributes of field element
$form->text('title')->attribute(['data-title' => 'title...']);
$form->text('title')->attribute('data-title', 'title...');Set placeholder
$form->text('title')->placeholder('Please input...');Model-form-tab
If the form contains too many fields, will lead to form page is too long, in which case you can use the tab to separate the form:
$form->tab('Basic info', function ($form) {
$form->text('username');
$form->email('email');
})->tab('Profile', function ($form) {
$form->image('avatar');
$form->text('address');
$form->mobile('phone');
})->tab('Jobs', function ($form) {
$form->hasMany('jobs', function () {
$form->text('company');
$form->date('start_date');
$form->date('end_date');
});
})Text input
$form->text($column, [$label]);
// Add a submission validation rule
$form->text($column, [$label])->rules('required|min:10');Select
$form->select($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);If have too many options, you can load option by ajax:
$form->select('user_id')->options(function ($id) {
$user = User::find($id);
if ($user) {
return [$user->id => $user->name];
}
})->ajax('/admin/api/users');
// using ajax and show selected item:
$form->select('user_id')->options(User::class)->ajax('/admin/api/users');
// or specifying the name and id
$form->select('user_id')->options(User::class, 'name', 'id')->ajax('/admin/api/users');Notice:if you have modified the value of the route.prefix in the config/admin.php file, this api route should be modified to config('admin.route.prefix').'/api/users'.
The controller method for api /admin/api/users is:
public function users(Request $request)
{
$q = $request->get('q');
return User::where('name', 'like', "%$q%")->paginate(null, ['id', 'name as text']);
}The json returned from api /admin/demo/options:
{
"total": 4,
"per_page": 15,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null,
"from": 1,
"to": 3,
"data": [
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
{
"id": 42,
"text": "xxx"
},
{
"id": 48,
"text": "xxx"
}
]
}Select linkage
select component supports one-way linkage of parent-child relationship:
$form->select('province')->options(...)->load('city', '/api/city');
$form->select('city');Where load('city', '/api/city'); means that, after the current select option is changed, the current option will call the api /api/city via the argument q api returns the data to fill the options for the city selection box, where api /api/city returns the data format that must match:
[
{
"id": 1,
"text": "foo"
},
{
"id": 2,
"text": "bar"
},
...
]The code for the controller action is as follows:
public function city(Request $request)
{
$provinceId = $request->get('q');
return ChinaArea::city()->where('parent_id', $provinceId)->get(['id', DB::raw('name as text')]);
}Multiple select
$form->multipleSelect($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);
// using ajax and show selected items:
$form->multipleSelect($column[, $label])->options(Model::class)->ajax('ajax_url');
// or specifying the name and id
$form->multipleSelect($column[, $label])->options(Model::class, 'name', 'id')->ajax('ajax_url');You can store value of multiple select in two ways, one is many-to-many relation.
class Post extends Models
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
$form->multipleSelect('tags')->options(Tag::all()->pluck('name', 'id'));The second is to store the option array into a single field. If the field is a string type, it is necessary to define accessor and Mutator for the field.
If have too many options, you can load option by ajax
$form->select('user_id')->options(function ($id) {
$user = User::find($id);
if ($user) {
return [$user->id => $user->name];
}
})->ajax('/admin/api/users');Notice:If you have modified the value of the route.prefix in the config/admin.php file, this api route should be modified to config('admin.route.prefix').'/api/users'.
The controller method for api /admin/api/users is:
public function users(Request $request)
{
$q = $request->get('q');
return User::where('name', 'like', "%$q%")->paginate(null, ['id', 'name as text']);
}The json returned from api /admin/demo/options:
{
"total": 4,
"per_page": 15,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null,
"from": 1,
"to": 3,
"data": [
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
{
"id": 42,
"text": "xxx"
},
{
"id": 48,
"text": "xxx"
}
]
}Listbox
The usage is as same as mutipleSelect.
$form->listbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);Textarea
$form->textarea($column[, $label])->rows(10);Radio
$form->radio($column[, $label])->options(['m' => 'Female', 'f'=> 'Male'])->default('m');
$form->radio($column[, $label])->options(['m' => 'Female', 'f'=> 'Male'])->default('m')->stacked();Checkbox
checkbox can store values in two ways, see[multiple select](#Multiple select)
The options() method is used to set options:
$form->checkbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);
$form->checkbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name'])->stacked();Email input
$form->email($column[, $label]);Password input
$form->password($column[, $label]);URL input
$form->url($column[, $label]);Ip input
$form->ip($column[, $label]);Phone number input
$form->mobile($column[, $label])->options(['mask' => '999 9999 9999']);Color select
$form->color($column[, $label])->default('#ccc');Time input
$form->time($column[, $label]);
// Set the time format, more formats reference http://momentjs.com/docs/#/displaying/format/
$form->time($column[, $label])->format('HH:mm:ss');Date input
$form->date($column[, $label]);
// Date format setting,more format please see http://momentjs.com/docs/#/displaying/format/
$form->date($column[, $label])->format('YYYY-MM-DD');Datetime input
$form->datetime($column[, $label]);
// Set the date format, more format reference http://momentjs.com/docs/#/displaying/format/
$form->datetime($column[, $label])->format('YYYY-MM-DD HH:mm:ss');Time range select
$startTime、$endTimeis the start and end time fields:
$form->timeRange($startTime, $endTime, 'Time Range');Date range select
$startDate、$endDateis the start and end date fields:
$form->dateRange($startDate, $endDate, 'Date Range');Datetime range select
$startDateTime、$endDateTime is the start and end datetime fields:
$form->datetimeRange($startDateTime, $endDateTime, 'DateTime Range');Currency input
$form->currency($column[, $label]);
// set the unit symbol
$form->currency($column[, $label])->symbol('¥');Number input
$form->number($column[, $label]);Rate input
$form->rate($column[, $label]);Image upload
Before use upload field, you must complete upload configuration, see image/file upload.
You can use compression, crop, add watermarks and other methods, please refer to [[Intervention] (http://image.intervention.io/getting_started/introduction)], picture upload directory in the file config / admin.php Upload.image configuration, if the directory does not exist, you need to create the directory and open write permissions:
$form->image($column[, $label]);
// Modify the image upload path and file name
$form->image($column[, $label])->move($dir, $name);
// Crop picture
$form->image($column[, $label])->crop(int $width, int $height, [int $x, int $y]);
// Add a watermark
$form->image($column[, $label])->insert($watermark, 'center');
// add delete button
$form->image($column[, $label])->removable();File upload
Before use upload field, you must complete upload configuration, see image/file upload.
The file upload directory is configured in upload.file in the file config/admin.php. If the directory does not exist, it needs to be created and write-enabled.
$form->file($column[, $label]);
// Modify the file upload path and file name
$form->file($column[, $label])->move($dir, $name);
// And set the upload file type
$form->file($column[, $label])->rules('mimes:doc,docx,xlsx');
// add delete button
$form->file($column[, $label])->removable();Multiple image/file upload
// multiple image
$form->multipleImage($column[, $label]);
// multiple file
$form->multipleFile($column[, $label]);
// add delete button
$form->multipleFile($column[, $label])->removable();The type of data submitted from multiple image/file field is array, if you the type of column in mysql table is array, or use mongodb, then you can save the array directly,
but if you use string type to store the array data ,you need to specify a string format, For example, if you want to use json string to store the array data, you need to define
a mutator for the column in model mutator, such as the field named pictures, define mutator:
public function setPicturesAttribute($pictures)
{
if (is_array($pictures)) {
$this->attributes['pictures'] = json_encode($pictures);
}
}
public function getPicturesAttribute($pictures)
{
return json_decode($pictures, true);
}Of course, you can also specify any other format.
Map
The map field refers to the network resource, and if there is a problem with the network refer to form Component Management to remove the component.
Used to select the latitude and longitude, $ latitude, $ longitude for the latitude and longitude field, using Tencent map when locale set of laravel is zh_CN, otherwise use Google Maps:
$form->map($latitude, $longitude, $label);
// Use Tencent map
$form->map($latitude, $longitude, $label)->useTencentMap();
// Use google map
$form->map($latitude, $longitude, $label)->useGoogleMap();Slider
Can be used to select the type of digital fields, such as age:
$form->slider($column[, $label])->options(['max' => 100, 'min' => 1, 'step' => 1, 'postfix' => 'years old']);More options please ref to https://github.com/IonDen/ion.rangeSlider#settings
Rich text editor
The editor field refers to the network resource, and if there is a problem with the network refer to form Component Management to remove the component.
$form->editor($column[, $label]);Hidden field
$form->hidden($column);Switch
On and off pairs of switches with the values 1 and 0:
$states = [
'on' => ['value' => 1, 'text' => 'enable', 'color' => 'success'],
'off' => ['value' => 0, 'text' => 'disable', 'color' => 'danger'],
];
$form->switch($column[, $label])->states($states);Display field
Only display the fields and without any action:
$form->display($column[, $label]);Divide
$form->divide();Html
insert html,the argument passed in could be objects which impletements Htmlable、Renderable, or has method __toString()
$form->html('html contents');Tags
Insert the comma (,) separated string tags
$form->tags('keywords');Icon
Select the font-awesome icon.
$form->icon('icon');HasMany
One-to-many built-in tables for dealing with one-to-many relationships. Here is a simple example:
There are two tables are one-to-many relationship:
CREATE TABLE `demo_painters` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`bio` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `demo_paintings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`painter_id` int(10) unsigned NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`body` text COLLATE utf8_unicode_ci NOT NULL,
`completed_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY painter_id (`painter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;The model of tables are:
<?php
namespace App\Models\Demo;
use Illuminate\Database\Eloquent\Model;
class Painter extends Model
{
public function paintings()
{
return $this->hasMany(Painting::class, 'painter_id');
}
}
<?php
namespace App\Models\Demo;
use Illuminate\Database\Eloquent\Model;
class Painting extends Model
{
protected $fillable = ['title', 'body', 'completed_at'];
public function painter()
{
return $this->belongsTo(Painter::class, 'painter_id');
}
}Build the form code as follows:
$form->display('id', 'ID');
$form->text('username')->rules('required');
$form->textarea('bio')->rules('required');
$form->hasMany('paintings', function (Form\NestedForm $form) {
$form->text('title');
$form->image('body');
$form->datetime('completed_at');
});
$form->display('created_at', 'Created At');
$form->display('updated_at', 'Updated At');Embeds
Used to handle the JSON type field data of mysql or object type data of mongodb, or the data values of multiple fields can be stored in the form of the JSON string in the character type of mysql
Such as the extra column of the JSON or string type in the orders table, used to store data for multiple fields:
class Order extends Model
{
protected $casts = [
'extra' => 'json',
];
}And then use in the form:
$form->embeds('extra', function ($form) {
$form->text('extra1')->rules('required');
$form->email('extra2')->rules('required');
$form->mobile('extra3');
$form->datetime('extra4');
$form->dateRange('extra5', 'extra6', 'Date range')->rules('required');
});
// Customize the title
$form->embeds('extra', 'Extra', function ($form) {
...
});Callback function inside the form element to create the method call and the outside is the same.
File: docs/en/model-form-upload.md
[Binary file]
File: docs/en/model-form-validation.md
Form validation
model-form uses laravel's validation rules to verify the data submitted by the form:
$form->text('title')->rules('required|min:3');
// Complex validation rules can be implemented in the callback
$form->text('title')->rules(function ($form) {
// If it is not an edit state, add field unique verification
if (!$id = $form->model()->id) {
return 'unique:users,email_address';
}
});You can also customize the error message for the validation rule:
$form->text('code')->rules('required|regex:/^\d+$/|min:10', [
'regex' => 'code must be numbers',
'min' => 'code can not be less than 10 characters',
]);If you want to allow the field to be empty, first in the database table to face the field set to NULL, and then
$form->text('title')->rules('nullable');Please refer to the more rules Validation.
File: docs/en/model-form.md
Model-Form
The Encore\Admin\Form class is used to generate a data model-based form. For example, there is a movies table in the database
CREATE TABLE `movies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`director` int(10) unsigned NOT NULL,
`describe` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`rate` tinyint unsigned NOT NULL,
`released` enum(0, 1),
`release_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;The corresponding data model is App\Models\Movie, and the following code can generate the movies data form:
use App\Models\Movie;
use Encore\Admin\Form;
use Encore\Admin\Facades\Admin;
$grid = Admin::form(Movie::class, function(Form $grid){
// Displays the record id
$form->display('id', 'ID');
// Add an input box of type text
$form->text('title', 'Movie title');
$directors = [
1 => 'John',
2 => 'Smith',
3 => 'Kate',
];
$form->select('director', 'Director')->options($directors);
// Add textarea for the describe field
$form->textarea('describe', 'Describe');
// Number input
$form->number('rate', 'Rate');
// Add a switch field
$form->switch('released', 'Released?');
// Add a date and time selection box
$form->dateTime('release_at', 'release time');
// Display two time column
$form->display('created_at', 'Created time');
$form->display('updated_at', 'Updated time');
});Custom tools
The top right corner of the form has two button tools by default. You can modify it in the following way:
$form->tools(function (Form\Tools $tools) {
// Disable back btn.
$tools->disableBackButton();
// Disable list btn
$tools->disableListButton();
// Add a button, the argument can be a string, or an instance of the object that implements the Renderable or Htmlable interface
$tools->add('<a class="btn btn-sm btn-danger"><i class="fa fa-trash"></i> delete</a>');
});Other methods
Disable submit btn:
$form->disableSubmit();Disable reset btn:
$form->disableReset();Ignore fields to store
$form->ignore('column1', 'column2', 'column3');Set width for label and field
$form->setWidth(10, 2);Set form action
$form->setAction('admin/users');Model relationship
One to One
The users table and the profiles table are generated one-to-one relation through the profiles.user_id field.
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`age` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;The corresponding data model are:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}You can associate them in a form with the following code:
Admin::form(User::class, function (Form $form) {
$form->display('id');
$form->text('name');
$form->text('email');
$form->text('profile.age');
$form->text('profile.gender');
$form->datetime('created_at');
$form->datetime('updated_at');
});File: docs/en/model-grid-actions.md
Model grid row actions
model-grid By default, there are two actions edit and delete, which can be turned off in the following way:
$grid->actions(function ($actions) {
$actions->disableDelete();
$actions->disableEdit();
});You can get the data for the current row by $actions parameter passed in:
$grid->actions(function ($actions) {
// the array of data for the current row
$actions->row;
// gets the current row primary key value
$actions->getKey();
});If you have a custom action button, you can add the following:
$grid->actions(function ($actions) {
// append an action.
$actions->append('<a href=""><i class="fa fa-eye"></i></a>');
// prepend an action.
$actions->prepend('<a href=""><i class="fa fa-paper-plane"></i></a>');
}If you have more complex actions, you can refer to the following ways:
First define the action class:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Admin;
class CheckRow
{
protected $id;
public function __construct($id)
{
$this->id = $id;
}
protected function script()
{
return <<<SCRIPT
$('.grid-check-row').on('click', function () {
// Your code.
console.log($(this).data('id'));
});
SCRIPT;
}
protected function render()
{
Admin::script($this->script());
return "<a class='btn btn-xs btn-success fa fa-check grid-check-row' data-id='{$this->id}'></a>";
}
public function __toString()
{
return $this->render();
}
}Then add the action:
$grid->actions(function ($actions) {
// add action
$actions->append(new CheckRow($actions->getKey()));
}Row manipulations with column conditions:
For row attributes, you can use $row->model() array or $row->column() method.
You need to set style after setting attributes. Otherwise style method will be by-passed
$grid->rows(function ($row) {
// if relased column value is Yes
if ( $row->column('released') == 'Yes' ) {
// Set attributes for row.
$row->setAttributes([ 'data-row-id' => $row->model()['id'], 'data-row-date' => $row->column('release_date') ]);
// Set style of row
$row->style("background-color:green");
}
});File: docs/en/model-grid-column.md
Model-grid column
model-grid built-in a lot of the operation of the column, you can use these methods very flexible operation of the column data.
The Encore\Admin\Grid\Column object has a built-in display() method to handle the value of the current column through the incoming callback function:
$grid->column('title')->display(function ($title) {
return "<span style='color:blue'>$title</span>";
});The display callback bound to the current row data object as a parent object, you can use the data in current row by this way:
$grid->first_name();
$grid->last_name();
$grid->column('full_name')->display(function () {
return $this->first_name . ' ' . $this->last_name;
});method
value()is a alias to methoddisplay().
Built-in methods
model-grid has built-in methods to help you extend the column functionality
editable
With the help of editable.js, you can edit the data in the grid directly:
$grid->title()->editable();
$grid->title()->editable('textarea');
$grid->title()->editable('select', [1 => 'option1', 2 => 'option2', 3 => 'option3']);
$grid->birth()->editable('date');
$grid->published_at()->editable('datetime');
$grid->column('year')->editable('year');
$grid->column('month')->editable('month');
$grid->column('day')->editable('day');switch
notice: If set up a switch for a column the grid, then need to set the column in the form of the same switch
Quickly turn a column into a switch component using the following methods:
$grid->status()->switch();
// set the `text`、`color`、and `value`
$states = [
'on' => ['value' => 1, 'text' => 'YES', 'color' => 'primary'],
'off' => ['value' => 2, 'text' => 'NO', 'color' => 'default'],
];
$grid->status()->switch($states);switchGroup
notice: If set up switch for some columns the grid, then need to set these columns in the form of the same switch
To quickly change a column into a switch component group, use the following method:
$states = [
'on' => ['text' => 'YES'],
'off' => ['text' => 'NO'],
];
$grid->column('switch_group')->switchGroup([
'hot' => 'Hot',
'new' => 'New',
'recommend' => 'Recommend',
], $states);select
$grid->options()->select([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);radio
$grid->options()->radio([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);checkbox
$grid->options()->checkbox([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);image
$grid->picture()->image();
//Set host, width and height
$grid->picture()->image('http://xxx.com', 100, 100);
// display multiple images
$grid->pictures()->display(function ($pictures) {
return json_decode($pictures, true);
})->image('http://xxx.com', 100, 100);label
$grid->name()->label();
//Set color,defaults to `success`, other options `danger`、`warning`、`info`、`primary`、`default`、`success`
$grid->name()->label('danger');
// can handle a array
$grid->keywords()->label();badge
$grid->name()->badge();
//Set color,defaults to `success`, other options `danger`、`warning`、`info`、`primary`、`default`、`success`
$grid->name()->badge('danger');
// can handle a array
$grid->keywords()->badge();Extend the column
There are two ways to extend the column function, the first one is through the anonymous function.
Add following code to app/Admin/bootstrap.php:
use Encore\Admin\Grid\Column;
Column::extend('color', function ($value, $color) {
return "<span style='color: $color'>$value</span>";
});Use this extension in model-grid:
$grid->title()->color('#ccc');If the column display logic is more complex, you can implement with an extension class.
Extension class app/Admin/Extensions/Popover.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class Popover extends AbstractDisplayer
{
public function display($placement = 'left')
{
Admin::script("$('[data-toggle=\"popover\"]').popover()");
return <<<EOT
<button type="button"
class="btn btn-secondary"
title="popover"
data-container="body"
data-toggle="popover"
data-placement="$placement"
data-content="{$this->value}"
>
Popover
</button>
EOT;
}
}And then register the extension in app/Admin/bootstrap.php:
use Encore\Admin\Grid\Column;
use App\Admin\Extensions\Popover;
Column::extend('popover', Popover::class);Use the extension in model-grid:
$grid->desciption()->popover('right');helpers
String operations
If the current output data is a string, you can call the method of class Illuminate\Support\Str.
For example, the following column shows the string value of the title field:
$grid->title();Call Str::limit() on title colum.
Can call Str::limit() method on the output string of the title column.
$grid->title()->limit(30);Continue to call Illuminate\Support\Str method:
$grid->title()->limit(30)->ucfirst();
$grid->title()->limit(30)->ucfirst()->substr(1, 10);Array operations
If the current output data is a array, you can call the method of class Illuminate\Support\Collection.
For example, the tags column is an array of data retrieved from a one-to-many relationship:
$grid->tags();
array (
0 =>
array (
'id' => '16',
'name' => 'php',
'created_at' => '2016-11-13 14:03:03',
'updated_at' => '2016-12-25 04:29:35',
),
1 =>
array (
'id' => '17',
'name' => 'python',
'created_at' => '2016-11-13 14:03:09',
'updated_at' => '2016-12-25 04:30:27',
),
)Call the Collection::pluck() method to get the name column from the array
$grid->tags()->pluck('name');
array (
0 => 'php',
1 => 'python',
),The output data is still a array after above, so you can call methods of Illuminate\Support\Collection continue.
$grid->tags()->pluck('name')->map('ucwords');
array (
0 => 'Php',
1 => 'Python',
),Outputs the array as a string
$grid->tags()->pluck('name')->map('ucwords')->implode('-');
"Php-Python"Mixed use
In the above two types of method calls, as long as the output of the previous step is to determine the type of value, you can call the corresponding type of method, it can be very flexible mix.
For example, the images field is a JSON-formatted string type that stores a multiple-picture address array:
$grid->images();
"['foo.jpg', 'bar.png']"
// chain method calls to display multiple images
$grid->images()->display(function ($images) {
return json_decode($images, true);
})->map(function ($path) {
return 'http://localhost/images/'. $path;
})->image();File: docs/en/model-grid-custom-tools.md
Custom tools
model-grid has batch delete and refresh operations tools as default, model-grid provides custom tool functionality if there are more operational requirements, the following example will show you how to add a Gender selector button group tool.
First define the tool class app/Admin/Extensions/Tools/UserGender.php:
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Tools\AbstractTool;
use Illuminate\Support\Facades\Request;
class UserGender extends AbstractTool
{
protected function script()
{
$url = Request::fullUrlWithQuery(['gender' => '_gender_']);
return <<<EOT
$('input:radio.user-gender').change(function () {
var url = "$url".replace('_gender_', $(this).val());
$.pjax({container:'#pjax-container', url: url });
});
EOT;
}
public function render()
{
Admin::script($this->script());
$options = [
'all' => 'All',
'm' => 'Male',
'f' => 'Female',
];
return view('admin.tools.gender', compact('options'));
}
}The blade file of view admin.tools.gender is resources/views/admin/tools/gender.blade.php:
<div class="btn-group" data-toggle="buttons">
@foreach($options as $option => $label)
<label class="btn btn-default btn-sm {{ \Request::get('gender', 'all') == $option ? 'active' : '' }}">
<input type="radio" class="user-gender" value="{{ $option }}">{{$label}}
</label>
@endforeach
</div>Import this tool in model-grid:
$grid->tools(function ($tools) {
$tools->append(new UserGender());
});In the model-grid, pass gender query to model:
if (in_array(Request::get('gender'), ['m', 'f'])) {
$grid->model()->where('gender', Request::get('gender'));
}You can refer to the above way to add your own tools.
Batch operation
At present, the default implementation of the batch delete operation, if you want to turn off the batch delete operation:
$grid->tools(function ($tools) {
$tools->batch(function ($batch) {
$batch->disableDelete();
});
});If you want to add a custom batch operation, you can refer to the following example.
The following example will show you how to implements a post batch release operation:
First define the tool class app/Admin/Extensions/Tools/ReleasePost.php:
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Grid\Tools\BatchAction;
class ReleasePost extends BatchAction
{
protected $action;
public function __construct($action = 1)
{
$this->action = $action;
}
public function script()
{
return <<<EOT
$('{$this->getElementClass()}').on('click', function() {
$.ajax({
method: 'post',
url: '{$this->resource}/release',
data: {
_token:LA.token,
ids: selectedRows(),
action: {$this->action}
},
success: function () {
$.pjax.reload('#pjax-container');
toastr.success('操作成功');
}
});
});
EOT;
}
}See the code above, use ajax to pass the selected ids to back-end api through a POST request, the back-end api modifies the state of the corresponding data according to the received ids, and then front-end refresh the page (pjax reload), and pop-up a toastr prompt operation is successful.
Import this operation in model-grid:
$grid->tools(function ($tools) {
$tools->batch(function ($batch) {
$batch->add('Release post', new ReleasePost(1));
$batch->add('Unrelease post', new ReleasePost(0));
});
});So that the batch operation of the drop-down button will add the following two operations, the final step is to add an api to handle the request of the batch operation, the api code is as follows:
class PostController extends Controller
{
...
public function release(Request $request)
{
foreach (Post::find($request->get('ids')) as $post) {
$post->released = $request->get('action');
$post->save();
}
}
...
}Then add a route for the api above:
$router->post('posts/release', 'PostController@release');This completes the entire process.
File: docs/en/model-grid-export.md
Data export
model-grid built-in export function is to achieve a simple csv format file export, if you encounter a file coding problem or can not meet their own needs, you can follow the steps below to customize the export function
This example uses Laravel-Excel as an excel library, and of course you can use any other excel library.
First install it:
composer require maatwebsite/excel:~2.1.0
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider"And then create a new custom export class, such as app/Admin/Extensions/ExcelExpoter.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Grid\Exporters\AbstractExporter;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Arr;
class ExcelExpoter extends AbstractExporter
{
public function export()
{
Excel::create('Filename', function($excel) {
$excel->sheet('Sheetname', function($sheet) {
// This logic get the columns that need to be exported from the table data
$rows = collect($this->getData())->map(function ($item) {
return Arr::only($item, ['id', 'title', 'content', 'rate', 'keywords']);
});
$sheet->rows($rows);
});
})->export('xls');
}
}And then use this class in model-grid:
use App\Admin\Extensions\ExcelExpoter;
$grid->exporter(new ExcelExpoter());For more information on how to use Laravel-Excel, refer to laravel-excel/docs
File: docs/en/model-grid-filters.md
Model grid filters
model-gridProvides a set of data filters:
$grid->filter(function($filter){
// Remove the default id filter
$filter->disableIdFilter();
// Add a column filter
$filter->like('name', 'name');
...
});Filter type
Currently supported filter types are the following:
Equal
sql: ... WHERE column = ""$input"":
$filter->equal('column', $label);Not equal
sql: ... WHERE column != ""$input"":
$filter->notEqual('column', $label);Like
sql: ... WHERE column LIKE "%"$input"%":
$filter->like('column', $label);Ilike
sql: ... WHERE column ILIKE "%"$input"%":
$filter->ilike('column', $label);Greater then
sql: ... WHERE column > "$input":
$filter->gt('column', $label);Less than
sql: ... WHERE column < "$input":
$filter->lt('column', $label);Between
sql: ... WHERE column BETWEEN "$start" AND "$end":
$filter->between('column', $label);
// set datetime field type
$filter->between('column', $label)->datetime();
// set time field type
$filter->between('column', $label)->time();In
sql: ... WHERE column in (...$inputs):
$filter->in('column', $label)->multipleSelect(['key' => 'value']);NotIn
sql: ... WHERE column not in (...$inputs):
$filter->notIn('column', $label)->multipleSelect(['key' => 'value']);Date
sql: ... WHERE DATE(column) = "$input":
$filter->date('column', $label);Day
sql: ... WHERE DAY(column) = "$input":
$filter->day('column', $label);Month
sql: ... WHERE MONTH(column) = "$input":
$filter->month('column', $label);year
sql: ... WHERE YEAR(column) = "$input":
$filter->year('column', $label);Where
You can use where to build more complex query filtering
sql: ... WHERE titleLIKE "%$input" ORcontent LIKE "%$input":
$filter->where(function ($query) {
$query->where('title', 'like', "%{$this->input}%")
->orWhere('content', 'like', "%{$this->input}%");
}, 'Text');sql: ... WHERE rate>= 6 ANDcreated_at = {$input}:
$filter->where(function ($query) {
$query->whereRaw("`rate` >= 6 AND `created_at` = {$this->input}");
}, 'Text');Relationship query, query the corresponding relationship profile field:
$filter->where(function ($query) {
$query->whereHas('profile', function ($query) {
$query->where('address', 'like', "%{$this->input}%")->orWhere('email', 'like', "%{$this->input}%");
});
}, 'Address or mobile');Field type
The default field type is text input, set placeholder for text input:
$filter->equal('column')->placeholder('Please input...');You can also restrict the user input format by using some of the following methods:
$filter->equal('column')->url();
$filter->equal('column')->email();
$filter->equal('column')->integer();
$filter->equal('column')->ip();
$filter->equal('column')->mac();
$filter->equal('column')->mobile();
// $options refer to https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->decimal($options = []);
// $options refer to https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->currency($options = []);
// $options refer to https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->percentage($options = []);
// $options refer to https://github.com/RobinHerbots/Inputmask
$filter->equal('column')->inputmask($options = [], $icon = 'pencil');Select
$filter->equal('column')->select(['key' => 'value'...]);
// Or from the api to obtain data, api format reference model-form `select` component
$filter->equal('column')->select('api/users');multipleSelect
Generally used in conjunction with in and notIn need to query the array of two types of inquiries can also be used in the type type of query:
$filter->in('column')->multipleSelect(['key' => 'value'...]);
// // Or from the api to obtain data, api format reference model-form `multipleSelect` component
$filter->in('column')->multipleSelect('api/users');radio
The more common scenario is the selection of categories
$filter->equal('released')->radio([
'' => 'All',
0 => 'Unreleased',
1 => 'Released',
]);checkbox
The more common scene is do the scope query with whereIn:
$filter->in('gender')->checkbox([
'm' => 'Male',
'f' => 'Female',
]);datetime
Use date and time components,$options parameter and value reference bootstrap-datetimepicker
$filter->equal('column')->datetime($options);
// `date()` equals to `datetime(['format' => 'YYYY-MM-DD'])`
$filter->equal('column')->date();
// `time()` equals to `datetime(['format' => 'HH:mm:ss'])`
$filter->equal('column')->time();
// `day()` equals to `datetime(['format' => 'DD'])`
$filter->equal('column')->day();
// `month()` equals to `datetime(['format' => 'MM'])`
$filter->equal('column')->month();
// `year()` equals to `datetime(['format' => 'YYYY'])`
$filter->equal('column')->year();Complex query filter
You can use the $this->input to trigger complex custom queries:
$filter->where(function ($query) {
switch ($this->input) {
case 'yes':
// custom complex query if the 'yes' option is selected
$query->has('somerelationship');
break;
case 'no':
$query->doesntHave('somerelationship');
break;
}
}, 'Label of the field', 'name_for_url_shortcut')->radio([
'' => 'All',
'yes' => 'Only with relationship',
'no' => 'Only without relationship',
]);File: docs/en/model-grid.md
Model-grid
Class Encore\Admin\Grid is used to generate tables based on the data model,for example,we have a table movies in database:
CREATE TABLE `movies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`director` int(10) unsigned NOT NULL,
`describe` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`rate` tinyint unsigned NOT NULL,
`released` enum(0, 1),
`release_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;And the model of this table is App\Models\Movie,The following code can generate the data grid for table movies:
use App\Models\Movie;
use Encore\Admin\Grid;
use Encore\Admin\Facades\Admin;
$grid = Admin::grid(Movie::class, function(Grid $grid){
// The first column displays the id field and sets the column as a sortable column
$grid->id('ID')->sortable();
// The second column shows the title field, because the title field name and the Grid object's title method conflict, so use Grid's column () method instead
$grid->column('title');
// The third column shows the director field, which is set by the display($callback) method to display the corresponding user name in the users table
$grid->director()->display(function($userId) {
return User::find($userId)->name;
});
// The fourth column appears as the describe field
$grid->describe();
// The fifth column is displayed as the rate field
$grid->rate();
// The sixth column shows the released field, formatting the display output through the display($callback) method
$grid->released('Release?')->display(function ($released) {
return $released ? 'yes' : 'no';
});
// The following shows the columns for the three time fields
$grid->release_at();
$grid->created_at();
$grid->updated_at();
// The filter($callback) method is used to set up a simple search box for the table
$grid->filter(function ($filter) {
// Sets the range query for the created_at field
$filter->between('created_at', 'Created Time')->datetime();
});
});Basic Usage
Add a column
// Add the column directly through the field name `username`
$grid->username('Username');
// The effect is the same as above
$grid->column('username', 'Username');
// Add multiple columns
$grid->columns('email', 'username' ...);Modify the source data
$grid->model()->where('id', '>', 100);
$grid->model()->orderBy('id', 'desc');
$grid->model()->take(100);Sets the number of lines displayed per page
// The default is 15 per page
$grid->paginate(20);Modify the display output of column
use Illuminate\Support\Str;
$grid->text()->display(function($text) {
return Str::limit($text, 30, '...');
});
$grid->name()->display(function ($name) {
return "<span class='label'>$name</span>";
});
$grid->email()->display(function ($email) {
return "mailto:$email";
});
// column not in table
$grid->column('column_not_in_table')->display(function () {
return 'blablabla....';
});The closure passed to method display() is bind to row data object, you can use other column data in current row.
$grid->first_name();
$grid->last_name();
// column not in table
$grid->column('full_name')->display(function () {
return $this->first_name.' '.$this->last_name;
});Disable the create button
$grid->disableCreateButton();Disable Pagination
$grid->disablePagination();Disable all tools on header of grid. Filter, refresh, export, batch actions
$grid->disableTools();Disable data filter
$grid->disableFilter();Disable the export button
$grid->disableExport();Disable row selector
$grid->disableRowSelector();Disable row actions
$grid->disableActions();Enable orderable grid
$grid->orderable();Set options for perPage selector
$grid->perPages([10, 20, 30, 40, 50]);Relation
One to one
The users table and the profiles table are generated one-to-one relation through the profiles.user_id field.
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`age` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;The corresponding data model are:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}You can associate them in a grid with the following code:
Admin::grid(User::class, function (Grid $grid) {
$grid->id('ID')->sortable();
$grid->name();
$grid->email();
$grid->column('profile.age');
$grid->column('profile.gender');
//or
$grid->profile()->age();
$grid->profile()->gender();
$grid->created_at();
$grid->updated_at();
});One to many
The posts and comments tables generate a one-to-many association via the comments.post_id field
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`content` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `comments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`post_id` int(10) unsigned NOT NULL,
`content` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;The corresponding data model are:
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}You can associate them in a grid with the following code:
return Admin::grid(Post::class, function (Grid $grid) {
$grid->id('id')->sortable();
$grid->title();
$grid->content();
$grid->comments('Comments count')->display(function ($comments) {
$count = count($comments);
return "<span class='label label-warning'>{$count}</span>";
});
$grid->created_at();
$grid->updated_at();
});
return Admin::grid(Comment::class, function (Grid $grid) {
$grid->id('id');
$grid->post()->title();
$grid->content();
$grid->created_at()->sortable();
$grid->updated_at();
});Many to many
The users and roles tables produce a many-to-many relationship through the pivot table role_user
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(190) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `roles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `role_users` (
`role_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
KEY `role_users_role_id_user_id_index` (`role_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ciThe corresponding data model are:
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class);
}
}You can associate them in a grid with the following code:
return Admin::grid(User::class, function (Grid $grid) {
$grid->id('ID')->sortable();
$grid->username();
$grid->name();
$grid->roles()->display(function ($roles) {
$roles = array_map(function ($role) {
return "<span class='label label-success'>{$role['name']}</span>";
}, $roles);
return join(' ', $roles);
});
$grid->created_at();
$grid->updated_at();
});File: docs/en/model-tree.md
Model-tree
Can be achieved through a model-tree to a tree-like components, you can drag the way to achieve the level of data, sorting and other operations, the following is the basic usage.
Table structure and model
To use model-tree, you have to follow the convention of the table structure:
CREATE TABLE `demo_categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
`order` int(11) NOT NULL DEFAULT '0',
`title` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ciThe above table structure has three necessary fields parent_id, order, title, and the other fields are not required.
The corresponding model is app/Models/Category.php:
<?php
namespace App\Models\Demo;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use ModelTree;
protected $table = 'demo_categories';
}Table structure in the three fields parent_id, order, title field name can be amended:
<?php
namespace App\Models\Demo;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use ModelTree;
protected $table = 'demo_categories';
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setParentColumn('pid');
$this->setOrderColumn('sort');
$this->setTitleColumn('name');
}
}Usage
然后就是在页面中使用model-tree了:
Then use model-tree in your page
<?php
namespace App\Admin\Controllers\Demo;
use App\Http\Controllers\Controller;
use App\Models\Category;
use Encore\Admin\Form;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use Encore\Admin\Controllers\ModelForm;
use Encore\Admin\Tree;
class CategoryController extends Controller
{
use ModelForm;
public function index()
{
return Admin::content(function (Content $content) {
$content->header('Categories');
$content->body(Category::tree());
});
}
}You can modify the display of branch in the following ways:
Category::tree(function ($tree) {
$tree->branch(function ($branch) {
$src = config('admin.upload.host') . '/' . $branch['logo'] ;
$logo = "<img src='$src' style='max-width:30px;max-height:30px' class='img'/>";
return "{$branch['id']} - {$branch['title']} $logo";
});
})The $branch parameter is array of current row data.
If you want to modify the query of the model, use the following way:
Category::tree(function ($tree) {
$tree->query(function ($model) {
return $model->where('type', 1);
});
})File: docs/en/permission.md
Access Control
laravel-admin has built-in RBAC permissions control module, expand the left sidebar Auth, you can see user, permissions and roles management panel, the use of permissions control as follows:
Route permission
In the laravel-admin 1.5, the permissions and routes are bound together, in the edit permission page which set the current permissions can access the routing, in the HTTP method select box to select the method of access to the path, in the HTTP path textarea fill in the path to access.
For example, to add a permission, the permission can access the path /admin/users in GET method, then HTTP method select GET, HTTP path fill in /users.
If you want to access all paths with the prefix /admin/users, then the HTTP path fill in /users*, if the permissions include multiple access paths, wrap the line for each path.
Page permission
If you want to control the user's permissions in the page, you can refer to the following example
example1
For example, there is now a scene, here is a article module, we use create articles as an example
At first open http://localhost/admi/auth/permissions, fill up slug field with text create-post, and Create post in name field, then assign this permission to some roles.
In your controller action:
use Encore\Admin\Auth\Permission;
class PostController extends Controller
{
public function create()
{
// check permission, only the roles with permission `create-post` can visit this action
Permission::check('create-post');
}
}example2
If you want to control the page elements of the user's display, then you need to first define permissions, such as delete-image and view-title-column, respectively, to control the permissions to delete pictures and display a column in grid, then assign these two permissions to roles, add following code to the grid:
$grid->actions(function ($actions) {
// The roles with this permission will not able to see the delete button in actions column.
if (!Admin::user()->can('delete-image')) {
$actions->disableDelete();
}
});
// Only roles with permission `view-title-column` can view this column in grid
if (Admin::user()->can('view-title-column')) {
$grid->column('title');
}Other methods
Get current user object.
Admin::user();Get current user id.
Admin::user()->id;Get user's roles.
Admin::user()->roles;Get user's permissions.
Admin::user()->permissions;User is role.
Admin::user()->isRole('developer');User has permission.
Admin::user()->can('create-post');User don't has permission.
Admin::user()->cannot('delete-post');Is user super administrator.
Admin::user()->isAdministrator();Is user in one of roles.
Admin::user()->inRoles(['editor', 'developer']);Permission middleware
You can use permission middleware in the routes to control the routing permission
// Allow roles `administrator` and `editor` access the routes under group.
Route::group([
'middleware' => 'admin.permission:allow,administrator,editor',
], function ($router) {
$router->resource('users', UserController::class);
...
});
// Deny roles `developer` and `operator` access the routes under group.
Route::group([
'middleware' => 'admin.permission:deny,developer,operator',
], function ($router) {
$router->resource('users', UserController::class);
...
});
// User has permission `edit-post`、`create-post` and `delete-post` can access routes under group.
Route::group([
'middleware' => 'admin.permission:check,edit-post,create-post,delete-post',
], function ($router) {
$router->resource('posts', PostController::class);
...
});The usage of permission middleware is just as same as other middleware.
File: docs/en/quick-start.md
Quick start
We use users table come with Laravel for example,the structure of table is:
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ciAnd the model for this table is App\User.php
You can follow these steps to setup CRUD interfaces of table users:
Add controller
Use the following command to create a controller for App\User model
php artisan admin:make UserController --model=App\\User
// under windows use:
php artisan admin:make UserController --model=App\UserThe above command will create the controller in app/Admin/Controllers/UserController.php.
Add route
Add a route in app/Admin/routes.php:
$router->resource('demo/users', UserController::class);Add left menu item
Open http://localhost:8000/admin/auth/menu, add menu link and refresh the page, then you can find a link item in left menu bar.
Where
urifills in the path part that does not contain the prefix of the route, such as the full pathhttp://localhost:8000/admin/demo/users, just inputdemo/users, If you want to add an external link, just fill in the full url, such ashttp://laravel-admin.org/.
Menu translations
append menu titles in menu_titles index at your language files.
For example 'Work Units' title:
in resources/lang/es/admin.php
...
// lowercase and replace spaces with _
'menu_titles' => [
'work_units' => 'Unidades de trabajo'
],Build grid and form
The rest needs to be done is open app/Admin/Contollers/UserController.php, find form() and grid() method and write few lines of code with model-grid and model-form,for more detail, please read model-grid and model-form.
File: docs/en/upgrade.md
Upgrade precautions
Because laravel-admin 1.5 built-in table structure has been modified, it is recommended that you re-install laravel 5.5 and laravel-admin 1.5, and then migrate the code over
Code migration needs attention:
- Please refer to the table structure changes tables.php
- Routing file structure is modified please refer to routes.stub
- Please refer to the configuration file structure changes admin.php
- The chart component has been removed and can no longer be used, please refer to Custom chart
File: docs/en/widgets.md
Web widgets
Box
Encore\Admin\Widgets\Box used to generate box components:
use Encore\Admin\Widgets\Box;
$box = new Box('Box Title', 'Box content');
$box->removable();
$box->collapsable();
$box->style('info');
$box->solid();
echo $box;The $content parameter is the content element of the Box, which can be either an implementation of the Illuminate\Contracts\Support\Renderable interface, or other printable variables.
Box::title($title) method is used to set the Box component title.
Box::content($content) method is used to set the content element of a Box component.
Box::removable() method sets the Box component as removable.
Box::collapsable() method sets the Box component as collapsable.
Box::style($style) method sets the style of the Box component to fill in primary, info, danger, warning, success, default.
Box::solid() method adds a border to the Box component.
Collapse
Encore\Admin\Widgets\Collapse class used to generate folding components:
use Encore\Admin\Widgets\Collapse;
$collapse = new Collapse();
$collapse->add('Bar', 'xxxxx');
$collapse->add('Orders', new Table());
echo $collapse->render();Collapse::add($title, $content) method is used to add a collapsed item to the collapsing component. The $title parameter sets the title of the item. The$content parameter is used to .
Form
Encore\Admin\Widgets\Form class is used to quickly build a form:
$form = new Form();
$form->action('example');
$form->email('email')->default('[email protected]');
$form->password('password');
$form->text('name');
$form->url('url');
$form->color('color');
$form->map('lat', 'lng');
$form->date('date');
$form->json('val');
$form->dateRange('created_at', 'updated_at');
echo $form->render();Form::__construct($data = []) generates a form object. If the $data parameter is passed, the elements in the $data array will be filled into the form.
Form::action($uri) method is used to set the form submission address.
Form::method($method) method is used to set the submit method of the form, the default is POST method.
Form::disablePjax() disable pjax for form submit.
Infobox
Encore\Admin\Widgets\InfoBox class is used to generate the information presentation block:
use Encore\Admin\Widgets\InfoBox;
$infoBox = new InfoBox('New Users', 'users', 'aqua', '/admin/users', '1024');
echo $infoBox->render();Refer to the section on the InfoBox in the index() method of the home page layout file HomeController.php.
Tab component
Encore\Admin\Widgets\Tab class is used to generate the tab components:
use Encore\Admin\Widgets\Tab;
$tab = new Tab();
$tab->add('Pie', $pie);
$tab->add('Table', new Table());
$tab->add('Text', 'blablablabla....');
echo $tab->render();Tab::add($title, $content) method is used to add new tab, $title is tab title, $content is tab content.
Table
Encore\Admin\Widgets\Table class is used to generate tables:
use Encore\Admin\Widgets\Table;
// table 1
$headers = ['Id', 'Email', 'Name', 'Company'];
$rows = [
[1, '[email protected]', 'Ms. Clotilde Gibson', 'Goodwin-Watsica'],
[2, '[email protected]', 'Allie Kuhic', 'Murphy, Koepp and Morar'],
[3, '[email protected]', 'Prof. Drew Heller', 'Kihn LLC'],
[4, '[email protected]', 'William Koss', 'Becker-Raynor'],
[5, '[email protected]', 'Ms. Antonietta Kozey Jr.'],
];
$table = new Table($headers, $rows);
echo $table->render();
// table 2
$headers = ['Keys', 'Values'];
$rows = [
'name' => 'Joe',
'age' => 25,
'gender' => 'Male',
'birth' => '1989-12-05',
];
$table = new Table($headers, $rows);
echo $table->render();File: docs/zh/README.md
laravel-admin
laravel-admin 是一个可以快速帮你构建后台管理的工具,它提供的页面组件和表单元素等功能,能帮助你使用很少的代码就实现功能完善的后台管理功能。
当前版本(1.5)需要安装
PHP 7+和Laravel 5.5, 如果你使用更早的版本,请参考文档: 1.4
特性
- 内置用户和权限系统
model-grid支持快速构建数据表格model-form支持快速构建数据表单model-tree支持快速构建树状数据- 内置40+种form元素组件、以及支持扩展组件
- 支持
Laravel的多种模型关系 mysql、mongodb、pgsql等多数据库支持- 支持引入第三方前端库
- 数据库和artisan命令行工具的web实现
- 支持自定义图表
- 多种常用web组件
- 支持本地和oss文件上传
Demo
打开http://laravel-admin.org/demo/,用账号密码admin/admin登陆
依赖
laravel-admin 基于以下组件或者服务:
- Laravel
- AdminLTE
- Datetimepicker
- font-awesome
- moment
- Google map
- Tencent map
- bootstrap-fileinput
- jquery-pjax
- Nestable
- toastr
- X-editable
- bootstrap-number-input
- fontawesome-iconpicker
交流
QQ群:278455482(已满)、635881319(已满)、533701919
为了避免广告及不看文档用户,请先到github star此项目,然后附上github账号申请入群
另外我已经屏蔽群私聊,所以请不要找我私聊,也请尽量不要在群里面 @我 🙏
支持
如果觉得这个项目帮你节约了时间,不妨支持一下;)
License
laravel-admin is licensed under The MIT License (MIT).
File: docs/zh/_sidebar.md
File: docs/zh/change-log.md
Change log
v1.2.9、v1.3.3、v1.4.1
- 添加用户设置和修改头像功能
- model-form自定义工具参考
- 内嵌表单支持参考
- 支持自定义导航条(右上角)参考
- 添加脚手架、数据库命令行工具、web artisan帮助工具参考
- 支持自定义登陆页面和登陆逻辑参考
- 表单支持设置宽度、设置action参考
- 优化表格过滤器
- 修复bug,优化代码和逻辑
File: docs/zh/content-layout.md
页面内容
laravel-admin的布局可参考后台首页的布局文件HomeController.php的index()方法。
Encore\Admin\Layout\Content类用来实现内容区的布局。Content::body($content)方法用来添加页面内容:
一个简单的后台页面代码如下:
public function index()
{
return Admin::content(function (Content $content) {
// 选填
$content->header('填写页面头标题');
// 选填
$content->description('填写页面描述小标题');
// 添加面包屑导航 since v1.5.7
$content->breadcrumb(
['text' => '首页', 'url' => '/admin'],
['text' => '用户管理', 'url' => '/admin/users'],
['text' => '编辑用户']
);
// 填充页面body部分,这里可以填入任何可被渲染的对象
$content->body('hello world');
});
}其中$content->body();方法可以接受任何可字符串化的对象作为参数,可以是字符串、数字、包含了__toString方法的对象,实现了Renderable、Htmlable接口的对象,包括laravel的视图。
布局
laravel-admin的布局使用bootstrap的栅格系统,每行的长度是12,下面是几个简单的示例:
添加一行内容:
$content->row('hello')
---------------------------------
|hello |
| |
| |
| |
| |
| |
---------------------------------行内添加多列:
$content->row(function(Row $row) {
$row->column(4, 'foo');
$row->column(4, 'bar');
$row->column(4, 'baz');
});
----------------------------------
|foo |bar |baz |
| | | |
| | | |
| | | |
| | | |
| | | |
----------------------------------
$content->row(function(Row $row) {
$row->column(4, 'foo');
$row->column(8, 'bar');
});
----------------------------------
|foo |bar |
| | |
| | |
| | |
| | |
| | |
----------------------------------列中添加行:
$content->row(function (Row $row) {
$row->column(4, 'xxx');
$row->column(8, function (Column $column) {
$column->row('111');
$column->row('222');
$column->row('333');
});
});
----------------------------------
|xxx |111 |
| |---------------------|
| |222 |
| |---------------------|
| |333 |
| | |
----------------------------------列中添加行, 行内再添加列:
$content->row(function (Row $row) {
$row->column(4, 'xxx');
$row->column(8, function (Column $column) {
$column->row('111');
$column->row('222');
$column->row(function(Row $row) {
$row->column(6, '444');
$row->column(6, '555');
});
});
});
----------------------------------
|xxx |111 |
| |---------------------|
| |222 |
| |---------------------|
| |444 |555 |
| | | |
----------------------------------File: docs/zh/custom-authentication.md
自定义登陆
如果不使用laravel-admin内置的认证登陆逻辑,可以参考下面的方式自定义登陆认证逻辑
首先要先定义一个user provider,用来获取用户身份, 比如app/Providers/CustomUserProvider.php:
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class CustomUserProvider implements UserProvider
{
public function retrieveById($identifier)
{}
public function retrieveByToken($identifier, $token)
{}
public function updateRememberToken(Authenticatable $user, $token)
{}
public function retrieveByCredentials(array $credentials)
{
// 用$credentials里面的用户名密码去获取用户信息,然后返回Illuminate\Contracts\Auth\Authenticatable对象
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// 用$credentials里面的用户名密码校验用户,返回true或false
}
}在方法retrieveByCredentials和validateCredentials中, 传入的$credentials就是登陆页面提交的用户名和密码数组,然后你可以使用$credentials去实现自己的登陆逻辑
Interface Illuminate\Contracts\Auth\Authenticatable的定义如下:
<?php
namespace Illuminate\Contracts\Auth;
interface Authenticatable {
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}上面interface每个方法的解释参考adding-custom-user-providers
定义好了User provider之后,打开app/Providers/AuthServiceProvider.php注册它:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('custom', function ($app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new CustomUserProvider();
});
}
}最后修改一下配置,打开config/admin.php,找到auth部分修改:
'auth' => [
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'admin',
]
],
// 修改下面
'providers' => [
'admin' => [
'driver' => 'custom',
]
],
],这样就完成了自定义登陆认证的逻辑,自定义登陆算是laravel中比较复杂的部分,需要开发者有耐心的一步步调试完成。
File: docs/zh/custom-chart.md
自定义图表
laravel-admin 1.5已经移除了所有的图表组件,如果要在页面中加入图表组件,可以参考下面的流程
用chartjs举例,首先要下载chartjs,放到public目录下面,比如放在public/vendor/chartjs目录
然后在app/Admin/bootstrap.php引入组件:
use Encore\Admin\Facades\Admin;
Admin::js('/vendor/chartjs/dist/Chart.min.js');新建视图文件 resources/views/admin/charts/bar.blade.php
<canvas id="myChart" width="400" height="400"></canvas>
<script>
$(function () {
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
});
</script>然后就可以在页面的任何地方引入这个图表视图了:
public function index()
{
return Admin::content(function (Content $content) {
$content->header('chart');
$content->description('.....');
$content->body(view('admin.charts.bar'));
});
}按照上面的方式可以引入任意图表库,多图表页面的布局,参考视图布局
File: docs/zh/custom-navbar.md
自定义头部导航条
从版本1.5.6开始,可以在顶部导航条上添加html元素了, 打开app/Admin/bootstrap.php:
use Encore\Admin\Facades\Admin;
Admin::navbar(function (\Encore\Admin\Widgets\Navbar $navbar) {
$navbar->left('html...');
$navbar->right('html...');
});left和right方法分别用来在头部的左右两边添加内容,方法参数可以是任何可以渲染的对象(实现了Htmlable、Renderable接口或者包含__toString()方法的对象)或字符串
左侧添加示例
举个例子,比如在左边添加一个搜索条,先创建一个blade视图resources/views/search-bar.blade.php:
<style>
.search-form {
width: 250px;
margin: 10px 0 0 20px;
border-radius: 3px;
float: left;
}
.search-form input[type="text"] {
color: #666;
border: 0;
}
.search-form .btn {
color: #999;
background-color: #fff;
border: 0;
}
</style>
<form action="/admin/posts" method="get" class="search-form" pjax-container>
<div class="input-group input-group-sm ">
<input type="text" name="title" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i></button>
</span>
</div>
</form>然后加入头部导航条:
$navbar->left(view('search-bar'));右侧添加示例
导航右侧只能添加<li>标签, 比如要添加一些提示图标,新建渲染对象app/Admin/Extensions/Nav/Links.php
<?php
namespace App\Admin\Extensions\Nav;
class Links
{
public function __toString()
{
return <<<HTML
<li>
<a href="#">
<i class="fa fa-envelope-o"></i>
<span class="label label-success">4</span>
</a>
</li>
<li>
<a href="#">
<i class="fa fa-bell-o"></i>
<span class="label label-warning">7</span>
</a>
</li>
<li>
<a href="#">
<i class="fa fa-flag-o"></i>
<span class="label label-danger">9</span>
</a>
</li>
HTML;
}
}然后加入头部导航条:
$navbar->right(new \App\Admin\Extensions\Nav\Links());或者用下面的html加入下拉菜单:
<li class="dropdown notifications-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i class="fa fa-bell-o"></i>
<span class="label label-warning">10</span>
</a>
<ul class="dropdown-menu">
<li class="header">You have 10 notifications</li>
<li>
<!-- inner menu: contains the actual data -->
<ul class="menu">
<li>
<a href="#">
<i class="fa fa-users text-aqua"></i> 5 new members joined today
</a>
</li>
<li>
<a href="#">
<i class="fa fa-warning text-yellow"></i> Very long description here that may not fit into the
page and may cause design problems
</a>
</li>
<li>
<a href="#">
<i class="fa fa-users text-red"></i> 5 new members joined
</a>
</li>
<li>
<a href="#">
<i class="fa fa-shopping-cart text-green"></i> 25 sales made
</a>
</li>
<li>
<a href="#">
<i class="fa fa-user text-red"></i> You changed your username
</a>
</li>
</ul>
</li>
<li class="footer"><a href="#">View all</a></li>
</ul>
</li>更多的组件可以参考Bootstrap
File: docs/zh/extension-api-tester.md
[Binary file]
File: docs/zh/extension-config.md
[Binary file]
File: docs/zh/extension-helpers.md
帮助工具
在最新的版本中新增了面向开发人员的帮助工具,能在开发中提供帮助提高效率,目前提供脚手架,数据库命令行和artisan命令行三个工具,如果有更好的其它实用工具的想法,欢迎提供建议。
安装:
composer require laravel-admin-ext/helpers
php artisan admin:import helpers工具的部分功能会在项目中创建或删除文件,可能会出现文件或目录权限的问题,这个问题需要自行解决。
另外部分数据库和artisan命令无法在web环境下使用。
脚手架工具
脚手架工具能帮你一键生成控制器、模型、迁移文件,并运行迁移文件,访问http://localhost/admin/helpers/scaffold打开。
其中设置迁移表结构的时候,主键字段是自动生成的不需要填写。
数据库命令行
数据库命令行工具的web集成,目前支持mysql、mongodb 和 redis,访问http://localhost/admin/helpers/terminal/database打开。
在右上角的select选择框切换数据库连接,然后在底部的输入框输入对应数据库的查询语句然后回车,就能得到查询结果:
实用方式和终端上操作数据库是一致的,可以运行所选择数据库的所支持的查询语句。
artisan命令行工具
Laravel的artisan命令的web实现,可以在上面运行artisan命令,访问http://localhost/admin/helpers/terminal/artisan打开。
路由列表
这个工具能用用比较直观的展现出系统的所有路由,包括路由的uri、方法和中间件等,还能查询路由。访问http://localhost/admin/helpers/routes打开。
File: docs/zh/extension-media-manager.md
文件管理
文件管理是一个对本地文件的可视化管理的工具
安装
$ composer require laravel-admin-ext/media-manager -vvv
$ php artisan admin:import media-manager配置
打开config/admin.php指定你要管理的disk
'extensions' => [
'media-manager' => [
'disk' => 'public' // 指向config/filesystem.php中设置的disk
],
],disk为config/filesystem.php中设置的本地disk,然后打开http://localhost/admin/media访问.
注意如果要预览disk中的图片,必须在disk中设置访问url前缀:
config/filesystem.php:
'disks' => [
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage', // 设置文件访问url
'visibility' => 'public',
],
...
]File: docs/zh/extension-scheduling.md
定时任务
这个工具是管理Laravel计划任务的web管理页面
安装
$ composer require laravel-admin-ext/scheduling -vvv
$ php artisan admin:import scheduling打开http://localhost/admin/scheduling访问。
添加任务
打开app/Console/Kernel.php, 试着添加两项计划任务:
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
$schedule->command('inspire')->everyTenMinutes();
$schedule->command('route:list')->dailyAt('02:00');
}
}然后就能在后台看到这两项计划任务的详细情况,也能直接运行这两个计划任务。
File: docs/zh/installation.md
[Binary file]
File: docs/zh/LICENSE.md
The MIT License (MIT)
Copyright (c) 2015 Jens Segers
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File: docs/zh/model-form-callback.md
模型表单回调
model-form目前提供了两个方法来接收回调函数:
//保存前回调
$form->saving(function (Form $form) {
//...
});
//保存后回调
$form->saved(function (Form $form) {
//...
});可以从回调参数$form中获取当前提交的表单数据:
$form->saving(function (Form $form) {
dump($form->username);
});获取获取模型中的数据
$form->saved(function (Form $form) {
$form->model()->id;
});可以直接在回调中返回Symfony\Component\HttpFoundation\Response的实例,来跳转或进入页面:
$form->saving(function (Form $form) {
// 返回一个简单response
return response('xxxx');
});
$form->saving(function (Form $form) {
// 跳转页面
return redirect('/admin/users');
});
$form->saving(function (Form $form) {
// 抛出异常
throw new \Exception('出错啦。。。');
});返回错误或者成功信息在页面上:
use Illuminate\Support\MessageBag;
// 抛出错误信息
$form->saving(function ($form) {
$error = new MessageBag([
'title' => 'title...',
'message' => 'message....',
]);
return back()->with(compact('error'));
});
// 抛出成功信息
$form->saving(function ($form) {
$success = new MessageBag([
'title' => 'title...',
'message' => 'message....',
]);
return back()->with(compact('success'));
});File: docs/zh/model-form-field-management.md
组件管理
移除已有组件
form表单内置的map和editor组件通过cdn的方式引用了前端文件,如果网络方面有问题,可以通过下面的方式将它们移除
找到文件app/Admin/bootstrap.php,如果文件不存在,请更新laravel-admin,然后新建该文件
<?php
use Encore\Admin\Form;
Form::forget('map');
Form::forget('editor');
// or
Form::forget(['map', 'editor']);这样就去掉了这两个组件,可以通过该方式去掉其它组件。
扩展自定义组件
集成富文本编辑器wangEditor
wangEditor是一个优秀的国产的轻量级富文本编辑器,如果laravel-admin自带的基于ckeditor的编辑器组件使用上有问题,可以通过下面的步骤可以集成它,并覆盖掉ckeditor:
先下载前端库文件wangEditor,解压到目录public/vendor/wangEditor-3.0.9。
然后新建组件类app/Admin/Extensions/WangEditor.php。
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Form\Field;
class WangEditor extends Field
{
protected $view = 'admin.wang-editor';
protected static $css = [
'/vendor/wangEditor-3.0.9/release/wangEditor.min.css',
];
protected static $js = [
'/vendor/wangEditor-3.0.9/release/wangEditor.min.js',
];
public function render()
{
$name = $this->formatName($this->column);
$this->script = <<<EOT
var E = window.wangEditor
var editor = new E('#{$this->id}');
editor.customConfig.zIndex = 0
editor.customConfig.uploadImgShowBase64 = true
editor.customConfig.onchange = function (html) {
$('input[name=$name]').val(html);
}
editor.create()
EOT;
return parent::render();
}
}新建视图文件resources/views/admin/wang-editor.blade.php:
<div class="form-group {!! !$errors->has($label) ?: 'has-error' !!}">
<label for="{{$id}}" class="col-sm-2 control-label">{{$label}}</label>
<div class="{{$viewClass['field']}}">
@include('admin::form.error')
<div id="{{$id}}" style="width: 100%; height: 100%;">
<p>{!! old($column, $value) !!}</p>
</div>
<input type="hidden" name="{{$name}}" value="{{ old($column, $value) }}" />
</div>
</div>然后注册进laravel-admin,在app/Admin/bootstrap.php中添加以下代码:
<?php
use App\Admin\Extensions\WangEditor;
use Encore\Admin\Form;
Form::extend('editor', WangEditor::class);调用:
$form->editor('body');集成富文本编辑器ckeditor
先下载ckeditor 并解压到/public目录,比如放在/public/packages/目录下。
然后新建扩展文件app/Admin/Extensions/Form/CKEditor.php:
<?php
namespace App\Admin\Extensions\Form;
use Encore\Admin\Form\Field;
class CKEditor extends Field
{
public static $js = [
'/packages/ckeditor/ckeditor.js',
'/packages/ckeditor/adapters/jquery.js',
];
protected $view = 'admin.ckeditor';
public function render()
{
$this->script = "$('textarea.{$this->getElementClass()}').ckeditor();";
return parent::render();
}
}新建view resources/views/admin/ckeditor.blade.php:
<div class="form-group {!! !$errors->has($errorKey) ?: 'has-error' !!}">
<label for="{{$id}}" class="col-sm-2 control-label">{{$label}}</label>
<div class="col-sm-6">
@include('admin::form.error')
<textarea class="form-control {{ $class }}" name="{{$name}}" placeholder="{{ $placeholder }}" {!! $attributes !!} >{{ old($column, $value) }}</textarea>
@include('admin::form.help-block')
</div>
</div>然后在app/Admin/bootstrap.php中引入扩展:
use App\Admin\Extensions\Form\CKEditor;
use Encore\Admin\Form;
Form::extend('ckeditor', CKEditor::class);然后就能在form中使用了:
$form->ckeditor('content');集成PHP editor
通过下面的步骤来扩展一个基于codemirror的PHP代码编辑器,效果参考PHP mode。
先将codemirror库下载并解压到前端资源目录下,比如放在public/packages/codemirror-5.20.2目录下。
新建组件类app/Admin/Extensions/PHPEditor.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Form\Field;
class PHPEditor extends Field
{
protected $view = 'admin.php-editor';
protected static $css = [
'/packages/codemirror-5.20.2/lib/codemirror.css',
];
protected static $js = [
'/packages/codemirror-5.20.2/lib/codemirror.js',
'/packages/codemirror-5.20.2/addon/edit/matchbrackets.js',
'/packages/codemirror-5.20.2/mode/htmlmixed/htmlmixed.js',
'/packages/codemirror-5.20.2/mode/xml/xml.js',
'/packages/codemirror-5.20.2/mode/javascript/javascript.js',
'/packages/codemirror-5.20.2/mode/css/css.js',
'/packages/codemirror-5.20.2/mode/clike/clike.js',
'/packages/codemirror-5.20.2/mode/php/php.js',
];
public function render()
{
$this->script = <<<EOT
CodeMirror.fromTextArea(document.getElementById("{$this->id}"), {
lineNumbers: true,
mode: "text/x-php",
extraKeys: {
"Tab": function(cm){
cm.replaceSelection(" " , "end");
}
}
});
EOT;
return parent::render();
}
}类中的静态资源也同样可以从外部引入,参考Editor.php
创建视图resources/views/admin/php-editor.blade.php:
<div class="form-group {!! !$errors->has($label) ?: 'has-error' !!}">
<label for="{{$id}}" class="col-sm-2 control-label">{{$label}}</label>
<div class="col-sm-6">
@include('admin::form.error')
<textarea class="form-control" id="{{$id}}" name="{{$name}}" placeholder="{{ trans('admin::lang.input') }} {{$label}}" {!! $attributes !!} >{{ old($column, $value) }}</textarea>
</div>
</div>最后找到文件app/Admin/bootstrap.php,如果文件不存在,请更新laravel-admin,然后新建该文件,添加下面代码:
<?php
use App\Admin\Extensions\PHPEditor;
use Encore\Admin\Form;
Form::extend('php', PHPEditor::class);这样就能在model-form中使用PHP编辑器了:
$form->php('code');通过这种方式,可以添加任意你想要添加的form组件。
File: docs/zh/model-form-fields.md
表单组件
在model-form中内置了大量的form组件来帮助你快速的构建form表单
公共方法
设置保存值
$form->text('title')->value('text...');设置默认值
$form->text('title')->default('text...');设置help信息
$form->text('title')->help('help...');设置属性
$form->text('title')->attribute(['data-title' => 'title...']);
$form->text('title')->attribute('data-title', 'title...');设置placeholder
$form->text('title')->placeholder('请输入。。。');model-form-tab
如果表单元素太多,会导致form页面太长, 这种情况下可以使用tab来分隔form:
$form->tab('Basic info', function ($form) {
$form->text('username');
$form->email('email');
})->tab('Profile', function ($form) {
$form->image('avatar');
$form->text('address');
$form->mobile('phone');
})->tab('Jobs', function ($form) {
$form->hasMany('jobs', function () {
$form->text('company');
$form->date('start_date');
$form->date('end_date');
});
})文本输入框
$form->text($column, [$label]);
// 添加提交验证规则
$form->text($column, [$label])->rules('required|min:10');select选择框
$form->select($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);或者从api中获取选项列表:
$form->select($column[, $label])->options('/api/users');
// 使用ajax并显示所选项目
$form->select($column[, $label])->options(Model::class)->ajax('/api/users');
// 或指定名称和ID
$form->select($column[, $label])->options(Model::class, 'name', 'id')->ajax('/api/users');其中api接口的格式必须为下面格式:
[
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
...
]如果选项过多,可通过ajax方式动态分页载入选项:
$form->select('user_id')->options(function ($id) {
$user = User::find($id);
if ($user) {
return [$user->id => $user->name];
}
})->ajax('/admin/api/users');注:如果你修改了config/admin.php配置文件中route.prefix的值,此处的接口路由应该修改为config('admin.route.prefix').'/api/users'。
API /admin/api/users接口的代码:
public function users(Request $request)
{
$q = $request->get('q');
return User::where('name', 'like', "%$q%")->paginate(null, ['id', 'name as text']);
}接口返回的数据结构为
{
"total": 4,
"per_page": 15,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null,
"from": 1,
"to": 3,
"data": [
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
{
"id": 42,
"text": "xxx"
},
{
"id": 48,
"text": "xxx"
}
]
}select 联动
select组件支持父子关系的单向联动:
$form->select('province')->options(...)->load('city', '/api/city');
$form->select('city');其中load('city', '/api/city');的意思是,在当前select的选项切换之后,会把当前选项的值通过参数q, 调用接口/api/city,并把api返回的数据填充为city选择框的选项,其中api/api/city返回的数据格式必须符合:
[
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
...
]控制器action的代码示例如下:
public function city(Request $request)
{
$provinceId = $request->get('q');
return ChinaArea::city()->where('parent_id', $provinceId)->get(['id', DB::raw('name as text')]);
}多选框
$form->multipleSelect($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);
// 使用ajax并显示所选项目:
$form->multipleSelect($column[, $label])->options(Model::class)->ajax('ajax_url');
// 或指定名称和ID
$form->multipleSelect($column[, $label])->options(Model::class, 'name', 'id')->ajax('ajax_url');多选框可以处理两种情况,第一种是ManyToMany的关系。
class Post extends Models
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
$form->multipleSelect('tags')->options(Tag::all()->pluck('name', 'id'));第二种是将选项数组存储到单字段中,如果字段是字符串类型,那就需要在模型里面为该字段定义访问器和修改器来存储和读取了。
如果选项过多,可通过ajax方式动态分页载入选项:
$form->select('friends')->options(function ($ids) {
return User::find($ids)->pluck('name', 'id');
})->ajax('/admin/api/users');注:如果你修改了config/admin.php配置文件中route.prefix的值,此处的接口路由应该修改为config('admin.route.prefix').'/api/users'。
API /admin/api/users接口的代码:
public function users(Request $request)
{
$q = $request->get('q');
return User::where('name', 'like', "%$q%")->paginate(null, ['id', 'name as text']);
}接口返回的数据结构为
{
"total": 4,
"per_page": 15,
"current_page": 1,
"last_page": 1,
"next_page_url": null,
"prev_page_url": null,
"from": 1,
"to": 3,
"data": [
{
"id": 9,
"text": "xxx"
},
{
"id": 21,
"text": "xxx"
},
{
"id": 42,
"text": "xxx"
},
{
"id": 48,
"text": "xxx"
}
]
}listbox
使用方法和multipleSelect类似
$form->listbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);textarea输入框
$form->textarea($column[, $label])->rows(10);radio选择
$form->radio($column[, $label])->options(['m' => 'Female', 'f'=> 'Male'])->default('m');
// 竖排
$form->radio($column[, $label])->options(['m' => 'Female', 'f'=> 'Male'])->stacked();checkbox选择
checkbox能处理两种数据存储情况,参考多选框
options()方法用来设置选择项:
$form->checkbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name']);
// 竖排
$form->checkbox($column[, $label])->options([1 => 'foo', 2 => 'bar', 'val' => 'Option name'])->stacked();email个数输入框
$form->email($column[, $label]);密码输入框
$form->password($column[, $label]);url输入框
$form->url($column[, $label]);ip输入框
$form->ip($column[, $label]);电话号码输入框
$form->mobile($column[, $label])->options(['mask' => '999 9999 9999']);颜色选择框
$form->color($column[, $label])->default('#ccc');时间输入框
$form->time($column[, $label]);
// 设置时间格式,更多格式参考http://momentjs.com/docs/#/displaying/format/
$form->time($column[, $label])->format('HH:mm:ss');日期输入框
$form->date($column[, $label]);
// 设置日期格式,更多格式参考http://momentjs.com/docs/#/displaying/format/
$form->date($column[, $label])->format('YYYY-MM-DD');日期时间输入框
$form->datetime($column[, $label]);
// 设置日期格式,更多格式参考http://momentjs.com/docs/#/displaying/format/
$form->datetime($column[, $label])->format('YYYY-MM-DD HH:mm:ss');时间范围选择框
$startTime、$endTime为开始和结束时间字段:
$form->timeRange($startTime, $endTime, 'Time Range');日期范围选框
$startDate、$endDate为开始和结束日期字段:
$form->dateRange($startDate, $endDate, 'Date Range');时间日期范围选择框
$startDateTime、$endDateTime为开始和结束时间日期:
$form->datetimeRange($startDateTime, $endDateTime, 'DateTime Range');货币输入框
$form->currency($column[, $label]);
// 设置单位符号
$form->currency($column[, $label])->symbol('¥');数字输入框
$form->number($column[, $label]);比例输入框
$form->rate($column[, $label]);图片上传
使用图片上传功能之前需要先完成上传配置,请参考:图片/文件上传.
图片上传目录在文件config/admin.php中的upload.image中配置,如果目录不存在,需要创建该目录并开放写权限。
可以使用压缩、裁切、添加水印等各种方法,需要先安装intervention/image.
更多使用方法请参考[Intervention]:
$form->image($column[, $label]);
// 修改图片上传路径和文件名
$form->image($column[, $label])->move($dir, $name);
// 剪裁图片
$form->image($column[, $label])->crop(int $width, int $height, [int $x, int $y]);
// 加水印
$form->image($column[, $label])->insert($watermark, 'center');
// 添加图片删除按钮
$form->image($column[, $label])->removable();文件上传
使用图片上传功能之前需要先完成上传配置,请参考:图片/文件上传.
文件上传目录在文件config/admin.php中的upload.file中配置,如果目录不存在,需要创建该目录并开放写权限。
$form->file($column[, $label]);
// 修改文件上传路径和文件名
$form->file($column[, $label])->move($dir, $name);
// 并设置上传文件类型
$form->file($column[, $label])->rules('mimes:doc,docx,xlsx');
// 添加文件删除按钮
$form->file($column[, $label])->removable();多图/文件上传
// 多图
$form->multipleImage($column[, $label]);
// 添加删除按钮
$form->multipleImage($column[, $label])->removable();
// 多文件
$form->multipleFile($column[, $label]);
// 添加删除按钮
$form->multipleFile($column[, $label])->removable();多图/文件上传的时候提交的数据为文件路径数组,可以直接用mysql的JSON类型字段存储,如果用mongodb的话也能直接存储,但是如果用字符串类型来存储的话,就需要指定数据的存储格式了,
比如,如果要用json字符串来存储文件数据,就需要在模型中定义字段的mutator,比如字段名为pictures,定义mutator:
public function setPicturesAttribute($pictures)
{
if (is_array($pictures)) {
$this->attributes['pictures'] = json_encode($pictures);
}
}
public function getPicturesAttribute($pictures)
{
return json_decode($pictures, true);
}当然你也可以指定其它任何格式.
地图控件
地图组件引用了网络资源,默认关闭,如果要开启这个组件参考form组件管理
地图控件,用来选择经纬度,$latitude, $longitude为经纬度字段,Laravel的locale设置为zh_CN的时候使用腾讯地图,否则使用Google地图:
$form->map($latitude, $longitude, $label);滑动选择控件
可以用来数字类型字段的选择,比如年龄:
$form->slider($column[, $label])->options(['max' => 100, 'min' => 1, 'step' => 1, 'postfix' => 'years old']);更多options请参考:https://github.com/IonDen/ion.rangeSlider#settings
富文本编辑框
编辑器组件引用了网络资源,默认关闭,如果要开启这个组件参考form组件管理.
$form->editor($column[, $label]);隐藏域
$form->hidden($column);开关选择
on和off对用开关的两个值1和0:
$states = [
'on' => ['value' => 1, 'text' => '打开', 'color' => 'success'],
'off' => ['value' => 0, 'text' => '关闭', 'color' => 'danger'],
];
$form->switch($column[, $label])->states($states);显示字段
只显示字段,不做任何操作:
$form->display($column[, $label]);
//更复杂的显示
$form->display($column[, $label])->with(function ($value) {
return "<img src="$value" />";
});分割线
$form->divide();Html
插入html内容,参数可以是实现了Htmlable、Renderable或者实现了__toString()方法的类
$form->html('你的html内容', $label = '');标签
插入逗号(,)隔开的字符串tags
$form->tags('keywords');tags同样支持ManyToMany的关系,示例如下:
$form->tags('tags', '文章标签')
->pluck('name', 'id') // name 为需要显示的 Tag 模型的字段,id 为主键
->options(Tag::all());// 下拉框选项注意:处理ManyToMany关系时必须调用pluck方法,指定显示的字段名和主键。
此外 options 方法传入一个Collection对象时,options会自动调用该对象的pluck方法转为['主键名' => '显示字段名'] 数组,作为下拉框选项。或者可以直接使用['主键名' => '显示字段名']这样的数组作为参数。
tags还支持saving方法用于处理提交的数据,示例如下:
$form->tags('tags', '文章标签')
->pluck('name', 'id')
->options(Tag::all())
->saving(function ($value) {
return $value;
});saving 方法接收一个「参数为 tags 的提交值,返回值为修改后的 tags 提交值」的闭包,可以用于实现自动创建新 tag 或其它功能。
图标
选择font-awesome图标
$form->icon('icon');一对多
一对多内嵌表格,用于处理一对多的关系,下面是个简单的例子:
有两张表是一对多关系:
CREATE TABLE `demo_painters` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`bio` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `demo_paintings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`painter_id` int(10) unsigned NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`body` text COLLATE utf8_unicode_ci NOT NULL,
`completed_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY painter_id (`painter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;表的模型为:
<?php
namespace App\Models\Demo;
use Illuminate\Database\Eloquent\Model;
class Painter extends Model
{
public function paintings()
{
return $this->hasMany(Painting::class, 'painter_id');
}
}
<?php
namespace App\Models\Demo;
use Illuminate\Database\Eloquent\Model;
class Painting extends Model
{
protected $fillable = ['title', 'body', 'completed_at'];
public function painter()
{
return $this->belongsTo(Painter::class, 'painter_id');
}
}构建表单代码如下:
$form->display('id', 'ID');
$form->text('username')->rules('required');
$form->textarea('bio')->rules('required');
$form->hasMany('paintings', function (Form\NestedForm $form) {
$form->text('title');
$form->image('body');
$form->datetime('completed_at');
});
$form->display('created_at', 'Created At');
$form->display('updated_at', 'Updated At');
// 也可以设置label
$form->hasMany('paintings', '画作', function (Form\NestedForm $form) {
});内嵌
用于处理mysql的JSON类型字段数据或者mongodb的object类型数据,也可以将多个field的数据值以JSON字符串的形式存储在mysql的字符串类型字段中
比如orders表中的JSON或字符串类型的extra字段,用来存储多个field的数据,先定义model:
class Order extends Model
{
protected $casts = [
'extra' => 'json',
];
}然后在form中使用:
$form->embeds('extra', function ($form) {
$form->text('extra1')->rules('required');
$form->email('extra2')->rules('required');
$form->mobile('extra3');
$form->datetime('extra4');
$form->dateRange('extra5', 'extra6', '范围')->rules('required');
});
// 自定义标题
$form->embeds('extra', '附加信息', function ($form) {
...
});回调函数里面构建表单元素的方法调用和外面是一样的。
File: docs/zh/model-form-upload.md
文件/图片上传
model-form通过以下的调用来生成form元素。
$form->file('file_column');
$form->image('image_column');修改存储路径或文件名
// 修改上传目录
$form->image('picture')->move('public/upload/image1/');
// 使用随机生成文件名 (md5(uniqid()).extension)
$form->image('picture')->uniqueName();
// 自定义文件名
$form->image('picture')->name(function ($file) {
return 'test.'.$file->guessExtension();
});model-form支持本地和云存储的文件上传
本地上传
先添加存储配置,config/filesystems.php 添加一项disk:
'disks' => [
... ,
'admin' => [
'driver' => 'local',
'root' => public_path('uploads'),
'visibility' => 'public',
'url' => env('APP_URL').'/uploads',
],
],设置上传的路径为public/uploads(public_path('uploads'))。
然后选择上传的disk,打开config/admin.php找到:
'upload' => [
'disk' => 'admin',
'directory' => [
'image' => 'images',
'file' => 'files',
]
],将disk设置为上面添加的admin,directory.image和directory.file分别为用$form->image($column)和$form->file($column)上传的图片和文件的上传目录。
云盘上传
如果需要上传到云存储,需要安装对应laravel storage的适配器,拿七牛云存储举例
首先安装 zgldh/qiniu-laravel-storage
同样配置好disk,在config/filesystems.php 添加一项:
'disks' => [
... ,
'qiniu' => [
'driver' => 'qiniu',
'domains' => [
'default' => 'xxxxx.com1.z0.glb.clouddn.com', //你的七牛域名
'https' => 'dn-yourdomain.qbox.me', //你的HTTPS域名
'custom' => 'static.abc.com', //你的自定义域名
],
'access_key'=> '', //AccessKey
'secret_key'=> '', //SecretKey
'bucket' => '', //Bucket名字
'notify_url'=> '', //持久化处理回调地址
'url' => 'http://of8kfibjo.bkt.clouddn.com/', // 填写文件访问根url
],
],然后修改laravel-admin的上传配置,打开config/admin.php找到:
'upload' => [
'disk' => 'qiniu',
'directory' => [
'image' => 'image',
'file' => 'file',
],
],disk选择上面配置的qiniu。
File: docs/zh/model-form-validation.md
表单验证
model-form使用laravel的验证规则来验证表单提交的数据:
$form->text('title')->rules('required|min:3');
// 复杂的验证规则可以在回调里面实现
$form->text('title')->rules(function ($form) {
// 如果不是编辑状态,则添加字段唯一验证
if (!$id = $form->model()->id) {
return 'unique:users,email_address';
}
});也可以给验证规则自定义错误提示消息:
$form->text('code')->rules('required|regex:/^\d+$/|min:10', [
'regex' => 'code必须全部为数字',
'min' => 'code不能少于10个字符',
]);如果要允许字段为空,首先要在数据库的表里面对该字段设置为NULL,然后
$form->text('title')->rules('nullable');更多规则请参考Validation.
File: docs/zh/model-form.md
基于数据模型的表单
Encore\Admin\Form类用于生成基于数据模型的表单,先来个例子,数据库中有movies表
CREATE TABLE `movies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`director` int(10) unsigned NOT NULL,
`describe` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`rate` tinyint unsigned NOT NULL,
`released` enum(0, 1),
`release_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;对应的数据模型为App\Models\Movie,下面的代码可以生成movies的数据表单:
use App\Models\Movie;
use Encore\Admin\Form;
use Encore\Admin\Facades\Admin;
$grid = Admin::form(Movie::class, function(Form $form){
// 显示记录id
$form->display('id', 'ID');
// 添加text类型的input框
$form->text('title', '电影标题');
$directors = [
1 => 'John',
2 => 'Smith',
3 => 'Kate',
];
$form->select('director', '导演')->options($directors);
// 添加describe的textarea输入框
$form->textarea('describe', '简介');
// 数字输入框
$form->number('rate', '打分');
// 添加开关操作
$form->switch('released', '发布?');
// 添加日期时间选择框
$form->datetime('release_at', '发布时间');
// 两个时间显示
$form->display('created_at', '创建时间');
$form->display('updated_at', '修改时间');
});自定义工具
表单右上角默认有返回和跳转列表两个按钮工具, 可以使用下面的方式修改它:
$form->tools(function (Form\Tools $tools) {
// 去掉返回按钮
$tools->disableBackButton();
// 去掉跳转列表按钮
$tools->disableListButton();
// 添加一个按钮, 参数可以是字符串, 或者实现了Renderable或Htmlable接口的对象实例
$tools->add('<a class="btn btn-sm btn-danger"><i class="fa fa-trash"></i> delete</a>');
});其它方法
去掉提交按钮:
$form->disableSubmit();去掉重置按钮:
$form->disableReset();忽略掉不需要保存的字段
$form->ignore(['column1', 'column2', 'column3']);设置宽度
$form->setWidth(10, 2);设置表单提交的action
$form->setAction('admin/users');关联模型
一对一
users表和profiles表通过profiles.user_id字段生成一对一关联
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`age` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;对应的数据模分别为:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}通过下面的代码可以关联在一个form里面:
Admin::form(User::class, function (Form $form) {
$form->display('id');
$form->text('name');
$form->text('email');
$form->text('profile.age');
$form->text('profile.gender');
$form->datetime('created_at');
$form->datetime('updated_at');
});File: docs/zh/model-grid-actions.md
模型表格行操作
model-grid默认有三个行操作编辑、删除和详情,可以通过下面的方式关闭它们:
$grid->actions(function ($actions) {
$actions->disableDelete();
$actions->disableEdit();
$actions->disableView();
});可以通过传入的$actions参数来获取当前行的数据:
$grid->actions(function ($actions) {
// 当前行的数据数组
$actions->row;
// 获取当前行主键值
$actions->getKey();
});如果有自定义的操作按钮,可以通过下面的方式添加:
$grid->actions(function ($actions) {
// append一个操作
$actions->append('<a href=""><i class="fa fa-eye"></i></a>');
// prepend一个操作
$actions->prepend('<a href=""><i class="fa fa-paper-plane"></i></a>');
}如果有比较复杂的操作,可以参考下面的方式:
先定义操作类
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Admin;
class CheckRow
{
protected $id;
public function __construct($id)
{
$this->id = $id;
}
protected function script()
{
return <<<SCRIPT
$('.grid-check-row').on('click', function () {
// Your code.
console.log($(this).data('id'));
});
SCRIPT;
}
protected function render()
{
Admin::script($this->script());
return "<a class='btn btn-xs btn-success fa fa-check grid-check-row' data-id='{$this->id}'></a>";
}
public function __toString()
{
return $this->render();
}
}然后添加操作:
$grid->actions(function ($actions) {
// 添加操作
$actions->append(new CheckRow($actions->getKey()));
});File: docs/zh/model-grid-column.md
列操作
model-grid内置了很多对于列的操作方法,可以通过这些方法很灵活的操作列数据。
Encore\Admin\Grid\Column对象内置了display()方法来通过传入的回调函数来处理当前列的值,
$grid->column('title')->display(function ($title) {
return "<span style='color:blue'>$title</span>";
});在传入的匿名函数中可以通过任何方式对数据进行处理,另外匿名函数绑定了当前列的数据作为父对象,可以在函数中调用当前行的数据:
$grid->first_name();
$grid->last_name();
// 不存在的`full_name`字段
$grid->column('full_name')->display(function () {
return $this->first_name . ' ' . $this->last_name;
});
value()方法作为display()方法的别名存在
内置方法
model-grid内置了若干方法来帮助你扩展列功能
editable
通过editable.js的帮助,可以让你在表格中直接编辑数据,使用方法如下
$grid->title()->editable();
$grid->title()->editable('textarea');
$grid->title()->editable('select', [1 => 'option1', 2 => 'option2', 3 => 'option3']);
// select 支持传递闭包作为参数,该闭包接收参数为当前行对应的模型
$grid->title()->editable('select', function($row) {
if ($row->title === 'test') {
return ['test1', 'test2'];
}
return ['test3', 'test4'];
});
$grid->birth()->editable('date');
$grid->published_at()->editable('datetime');
$grid->column('year')->editable('year');
$grid->column('month')->editable('month');
$grid->column('day')->editable('day');switch
注意:在
grid中对某字段设置了switch,同时需要在form里面对该字段设置同样的switch
快速将列变成开关组件,使用方法如下:
$grid->status()->switch();
// 设置text、color、和存储值
$states = [
'on' => ['value' => 1, 'text' => '打开', 'color' => 'primary'],
'off' => ['value' => 2, 'text' => '关闭', 'color' => 'default'],
];
$grid->status()->switch($states);switchGroup
注意:在
grid中对某些字段设置了switch,同时需要在form里面对这些字段设置同样的switch
快速将列变成开关组件组,使用方法如下:
$states = [
'on' => ['text' => 'YES'],
'off' => ['text' => 'NO'],
];
$grid->column('switch_group')->switchGroup([
'hot' => '热门',
'new' => '最新',
'recommend' => '推荐',
'image.show' => '显示图片', // 更新对应关联模型
], $states);select
$grid->options()->select([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);select 也支持参数为闭包,使用方法和editable的select类似。
radio
$grid->options()->radio([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);radio 也支持参数为闭包,使用方法和editable的select类似。
checkbox
$grid->options()->checkbox([
1 => 'Sed ut perspiciatis unde omni',
2 => 'voluptatem accusantium doloremque',
3 => 'dicta sunt explicabo',
4 => 'laudantium, totam rem aperiam',
]);checkbox 也支持参数为闭包,使用方法和editable的select类似。
image
$grid->picture()->image();
//设置服务器和宽高
$grid->picture()->image('http://xxx.com', 100, 100);
// 显示多图
$grid->pictures()->display(function ($pictures) {
return json_decode($pictures, true);
})->image('http://xxx.com', 100, 100);label
$grid->name()->label();
//设置颜色,默认`success`,可选`danger`、`warning`、`info`、`primary`、`default`、`success`
$grid->name()->label('danger');
// 接收数组
$grid->keywords()->label();badge
$grid->name()->badge();
//设置颜色,默认`success`,可选`danger`、`warning`、`info`、`primary`、`default`、`success`
$grid->name()->badge('danger');
// 接收数组
$grid->keywords()->badge();扩展列功能
可以通过两种方式扩展列功能,第一种是通过匿名函数的方式。
在app/Admin/bootstrap.php加入以下代码:
use Encore\Admin\Grid\Column;
Column::extend('color', function ($value, $color) {
return "<span style='color: $color'>$value</span>"
});然后在model-grid中使用这个扩展:
$grid->title()->color('#ccc');如果列显示逻辑比较复杂,可以通过扩展类来实现。
扩展类app/Admin/Extensions/Popover.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
class Popover extends AbstractDisplayer
{
public function display($placement = 'left')
{
Admin::script("$('[data-toggle=\"popover\"]').popover()");
return <<<EOT
<button type="button"
class="btn btn-secondary"
title="popover"
data-container="body"
data-toggle="popover"
data-placement="$placement"
data-content="{$this->value}"
>
弹出提示
</button>
EOT;
}
}然后在app/Admin/bootstrap.php注册扩展类:
use Encore\Admin\Grid\Column;
use App\Admin\Extensions\Popover;
Column::extend('popover', Popover::class);然后就能在model-grid中使用了:
$grid->desciption()->popover('right');帮助方法
字符串操作
如果当前里的输出数据为字符串,那么可以通过链式方法调用Illuminate\Support\Str的方法。
比如有如下一列,显示title字段的字符串值:
$grid->title();在title列输出的字符串基础上调用Str::limit()方法
$grid->title()->limit(30);调用方法之后输出的还是字符串,所以可以继续调用Illuminate\Support\Str的方法:
$grid->title()->limit(30)->ucfirst();
$grid->title()->limit(30)->ucfirst()->substr(1, 10);数组操作
如果当前列输出的是数组,可以直接链式调用Illuminate\Support\Collection方法。
比如tags列是从一对多关系取出来的数组数据:
$grid->tags();
array (
0 =>
array (
'id' => '16',
'name' => 'php',
'created_at' => '2016-11-13 14:03:03',
'updated_at' => '2016-12-25 04:29:35',
),
1 =>
array (
'id' => '17',
'name' => 'python',
'created_at' => '2016-11-13 14:03:09',
'updated_at' => '2016-12-25 04:30:27',
),
)调用Collection::pluck()方法取出数组的中的name列
$grid->tags()->pluck('name');
array (
0 => 'php',
1 => 'python',
),取出name列之后输出的还是数组,还能继续调用用Illuminate\Support\Collection的方法
$grid->tags()->pluck('name')->map('ucwords');
array (
0 => 'Php',
1 => 'Python',
),将数组输出为字符串
$grid->tags()->pluck('name')->map('ucwords')->implode('-');
"Php-Python"混合使用
在上面两种类型的方法调用中,只要上一步输出的是确定类型的值,便可以调用类型对应的方法,所以可以很灵活的混合使用。
比如images字段是存储多图片地址数组的JSON格式字符串类型:
$grid->images();
"['foo.jpg', 'bar.png']"
// 链式方法调用来显示多图
$grid->images()->display(function ($images) {
return json_decode($images, true);
})->map(function ($path) {
return 'http://localhost/images/'. $path;
})->image();File: docs/zh/model-grid-custom-tools.md
自定义工具
在model-grid的头部默认有批量删除和刷新两个操作工具,如果有更多的操作需求,model-grid提供了自定义工具的功能,下面的示例添加一个性别分类选择的按钮组工具。
先定义工具类app/Admin/Extensions/Tools/UserGender.php:
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Tools\AbstractTool;
use Illuminate\Support\Facades\Request;
class UserGender extends AbstractTool
{
protected function script()
{
$url = Request::fullUrlWithQuery(['gender' => '_gender_']);
return <<<EOT
$('input:radio.user-gender').change(function () {
var url = "$url".replace('_gender_', $(this).val());
$.pjax({container:'#pjax-container', url: url });
});
EOT;
}
public function render()
{
Admin::script($this->script());
$options = [
'all' => 'All',
'm' => 'Male',
'f' => 'Female',
];
return view('admin.tools.gender', compact('options'));
}
}视图admin.tools.gender文件为resources/views/admin/tools/gender.blade.php:
<div class="btn-group" data-toggle="buttons">
@foreach($options as $option => $label)
<label class="btn btn-default btn-sm {{ \Request::get('gender', 'all') == $option ? 'active' : '' }}">
<input type="radio" class="user-gender" value="{{ $option }}">{{$label}}
</label>
@endforeach
</div>在model-grid引入这个工具:
$grid->tools(function ($tools) {
$tools->append(new UserGender());
});在model-grid定义中接收到gender参数后,做好数据查询就可以了:
if (in_array(Request::get('gender'), ['m', 'f'])) {
$grid->model()->where('gender', Request::get('gender'));
}可以参考上面的方式来添加自己的工具。
批量操作
目前默认实现了批量删除操作的功能,如果要关掉批量删除操作:
$grid->tools(function ($tools) {
$tools->batch(function ($batch) {
$batch->disableDelete();
});
});如果要添加自定义的批量操作,可以参考下面的例子。
下面是扩展一个对文章批量发布的功能:
先定义操作类app/Admin/Extensions/Tools/ReleasePost.php:
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Grid\Tools\BatchAction;
class ReleasePost extends BatchAction
{
protected $action;
public function __construct($action = 1)
{
$this->action = $action;
}
public function script()
{
return <<<EOT
$('{$this->getElementClass()}').on('click', function() {
$.ajax({
method: 'post',
url: '{$this->resource}/release',
data: {
_token:LA.token,
ids: selectedRows(),
action: {$this->action}
},
success: function () {
$.pjax.reload('#pjax-container');
toastr.success('操作成功');
}
});
});
EOT;
}
}看代码的实现,通过click操作发送一个post请求,把选中的行数据id通过数组的形式传给后端接口,后端接口拿到id列表和要修改的状态来更新数据,然后前端刷新页面(pjax reload),并弹出toastr提示操作成功。
在model-grid中加入这个批量操作功能:
$grid->tools(function ($tools) {
$tools->batch(function ($batch) {
$batch->add('发布文章', new ReleasePost(1));
$batch->add('文章下线', new ReleasePost(0));
});
});这样批量操作下拉按钮下面就会添加两个操作项,最后一步就是添加一个接口来处理批量操作的请求了,接口代码如下:
class PostController extends Controller
{
...
public function release(Request $request)
{
foreach (Post::find($request->get('ids')) as $post) {
$post->released = $request->get('action');
$post->save();
}
}
...
}然后添加路由指向上面的接口:
$router->post('posts/release', 'PostController@release');这样整个流程就完成了。
File: docs/zh/model-grid-data.md
表格数据源
数据来自模型
如果使用模型来获取数据,那么修改来源数据就非常简单:
// 添加默认查询条件
$grid->model()->where('id', '>', 100);
// 设置初始排序条件
$grid->model()->orderBy('id', 'desc');
...其它查询方法可以参考eloquent的查询方法.
数据来自外部API
如果数据是来自外部的API,需要展示在数据表格中那么可以建立model,然后覆盖掉模型的几个获取数据的方法来实现, 下面的例子用豆瓣电影的API获取并展示数据:
<?php
namespace App\Models\Movie;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Request;
class InTheater extends Model
{
public function paginate()
{
$perPage = Request::get('per_page', 10);
$page = Request::get('page', 1);
$start = ($page-1)*$perPage;
$data = file_get_contents("https://api.douban.com/v2/movie/in_theaters?city=上海&start=$start&count=$perPage");
$data = json_decode($data, true);
extract($data);
$movies = static::hydrate($subjects);
$paginator = new LengthAwarePaginator($movies, $total, $perPage);
$paginator->setPath(url()->current());
return $paginator;
}
public static function with($relations)
{
return new static;
}
}通过覆盖模型的paginate和with方法获取API数据, 通过findOrFail方法来获取单项数据展示在表单中
同理如果要在form表单中获取或者保存数据那么也可以通过覆盖相应的方法来实现:
// 获取单项数据展示在form中
public function findOrFail($id)
{
$data = file_get_contents("http://api.douban.com/v2/movie/subject/$id");
$data = json_decode($data, true);
return static::newFromBuilder($data);
}
// 保存提交的form数据
public function save(array $options = [])
{
$attributes = $this->getAttributes();
// save $attributes
}数据来自复杂SQL查询
如果来源数据需要执行比较复杂的SQL语句获取,那么有两个办法, 第一个办法就是上面的方法,覆盖掉model的方法实现
public function paginate()
{
$perPage = Request::get('per_page', 10);
$page = Request::get('page', 1);
$start = ($page-1)*$perPage;
// 运行sql获取数据数组
$sql = 'select * from ...';
$result = DB::select($sql);
$movies = static::hydrate($result);
$paginator = new LengthAwarePaginator($movies, $total, $perPage);
$paginator->setPath(url()->current());
return $paginator;
}
public static function with($relations)
{
return new static;
}第二个方式是在数据库中建立视图和model绑定(未测试过,理论上可行)
File: docs/zh/model-grid-export.md
数据导出
model-grid内置的导出功能只是实现了简单的csv格式文件的导出,如果遇到文件编码问题或者满足不了自己需求的情况,可以按照下面的步骤来自定义导出功能
本示例用Laravel-Excel作为excel操作库,当然也可以使用任何其他excel库
首先安装好它:
composer require maatwebsite/excel:~2.1.0
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider"然后新建自定义导出类,比如app/Admin/Extensions/ExcelExpoter.php:
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Grid\Exporters\AbstractExporter;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Arr;
class ExcelExpoter extends AbstractExporter
{
public function export()
{
Excel::create('Filename', function($excel) {
$excel->sheet('Sheetname', function($sheet) {
// 这段逻辑是从表格数据中取出需要导出的字段
$rows = collect($this->getData())->map(function ($item) {
return Arr::only($item, ['id', 'title', 'content', 'rate', 'keywords']);
});
$sheet->rows($rows);
});
})->export('xls');
}
}然后在model-grid中使用这个导出类:
use App\Admin\Extensions\ExcelExpoter;
$grid->exporter(new ExcelExpoter());有关更多Laravel-Excel的使用方法,参考laravel-excel/docs
File: docs/zh/model-grid-exporter.md
自定义导出
laravel-admin的数据表格默认支持导出csv文件,
<?php
namespace App\Admin\Extensions;
use Encore\Admin\Grid\Exporters\AbstractExporter;
class CustomExporter extends AbstractExporter
{
public function export()
{
$filename = $this->getTable().'.csv';
$data = $this->getData();
$output = '';
$headers = [
'Content-Encoding' => 'UTF-8',
'Content-Type' => 'text/csv;charset=UTF-8',
'Content-Disposition' => "attachment; filename=\"$filename\"",
];
response(rtrim($output, "\n"), 200, $headers)->send();
exit;
}
}File: docs/zh/model-grid-filters.md
数据查询过滤
model-grid提供了一系列的方法实现表格数据的查询过滤:
$grid->filter(function($filter){
// 去掉默认的id过滤器
$filter->disableIdFilter();
// 自定义id过滤器,在去掉默认id过滤器后必须调用 setId 方法,
// 并设置一个和主键不同的值,自定义的id过滤器才会显示。
$filter->equal('id', '产品序列号')->setId('product_id');
// 在这里添加字段过滤器
$filter->like('name', 'name');
...
});查询类型
目前支持的过滤类型有下面这些:
equal
sql: ... WHERE column = "$input":
$filter->equal('column', $label);not equal
sql: ... WHERE column != "$input":
$filter->notEqual('column', $label);like
sql: ... WHERE column LIKE "%$input%":
$filter->like('column', $label);ilike
sql: ... WHERE column ILIKE "%$input%":
$filter->ilike('column', $label);大于
sql: ... WHERE column > "$input":
$filter->gt('column', $label);小于
sql: ... WHERE column < "$input":
$filter->lt('column', $label);between
sql: ... WHERE column BETWEEN "$start" AND "$end":
$filter->between('column', $label);
// 设置datetime类型
$filter->between('column', $label)->datetime();
// 设置time类型
$filter->between('column', $label)->time();in
sql: ... WHERE column in (...$inputs):
$filter->in('column', $label)->multipleSelect(['key' => 'value']);notIn
sql: ... WHERE column not in (...$inputs):
$filter->notIn('column', $label)->multipleSelect(['key' => 'value']);date
sql: ... WHERE DATE(column) = "$input":
$filter->date('column', $label);day
sql: ... WHERE DAY(column) = "$input":
$filter->day('column', $label);month
sql: ... WHERE MONTH(column) = "$input":
$filter->month('column', $label);year
sql: ... WHERE YEAR(column) = "$input":
$filter->year('column', $label);where
可以用where来构建比较复杂的查询过滤
sql: ... WHERE titleLIKE "%$input" ORcontent LIKE "%$input":
$filter->where(function ($query) {
$query->where('title', 'like', "%{$this->input}%")
->orWhere('content', 'like', "%{$this->input}%");
}, 'Text');sql: ... WHERE rate>= 6 ANDcreated_at = {$input}:
$filter->where(function ($query) {
$query->whereRaw("`rate` >= 6 AND `created_at` = {$this->input}");
}, 'Text');关系查询,查询对应关系profile的字段:
$filter->where(function ($query) {
$query->whereHas('profile', function ($query) {
$query->where('address', 'like', "%{$this->input}%")->orWhere('email', 'like', "%{$this->input}%");
});
}, '地址或手机号');表单类型
text
表单类型默认是text input,可以设置placeholder:
$filter->equal('column')->placeholder('请输入。。。');也可以通过下面的一些方法来限制用户输入格式:
$filter->equal('column')->url();
$filter->equal('column')->email();
$filter->equal('column')->integer();
$filter->equal('column')->ip();
$filter->equal('column')->mac();
$filter->equal('column')->mobile();
// $options 参考 https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->decimal($options = []);
// $options 参考 https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->currency($options = []);
// $options 参考 https://github.com/RobinHerbots/Inputmask/blob/4.x/README_numeric.md
$filter->equal('column')->percentage($options = []);
// $options 参考 https://github.com/RobinHerbots/Inputmask, $icon为input前面的图标
$filter->equal('column')->inputmask($options = [], $icon = 'pencil');select
$filter->equal('column')->select(['key' => 'value'...]);
// 或者从api获取数据,api的格式参考model-form的select组件
$filter->equal('column')->select('api/users');multipleSelect
一般用来配合in和notIn两个需要查询数组的查询类型使用,也可以在where类型的查询中使用:
$filter->in('column')->multipleSelect(['key' => 'value'...]);
// 或者从api获取数据,api的格式参考model-form的multipleSelect组件
$filter->in('column')->multipleSelect('api/users');radio
比较常见的场景是选择分类
$filter->equal('released')->radio([
'' => 'All',
0 => 'Unreleased',
1 => 'Released',
]);checkbox
比较常见的场景是配合whereIn来做范围筛选
$filter->in('gender')->checkbox([
'm' => 'Male',
'f' => 'Female',
]);datetime
通过日期时间组件来查询,$options的参数和值参考bootstrap-datetimepicker
$filter->equal('column')->datetime($options);
// `date()` 相当于 `datetime(['format' => 'YYYY-MM-DD'])`
$filter->equal('column')->date();
// `time()` 相当于 `datetime(['format' => 'HH:mm:ss'])`
$filter->equal('column')->time();
// `day()` 相当于 `datetime(['format' => 'DD'])`
$filter->equal('column')->day();
// `month()` 相当于 `datetime(['format' => 'MM'])`
$filter->equal('column')->month();
// `year()` 相当于 `datetime(['format' => 'YYYY'])`
$filter->equal('column')->year();File: docs/zh/model-grid.md
[Binary file]
File: docs/zh/model-tree.md
模型树
可以通过model-tree来实现一个树状组件,可以用拖拽的方式实现数据的层级、排序等操作,下面是基本的用法.
表结构和模型
要使用model-tree,要遵守约定的表结构:
CREATE TABLE `demo_categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
`order` int(11) NOT NULL DEFAULT '0',
`title` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci上面的表格结构里面有三个必要的字段parent_id、order、title,其它字段没有要求。
对应的模型为app/Models/Category.php:
<?php
namespace App\Models\Demo;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use ModelTree;
protected $table = 'demo_categories';
}表结构中的三个字段parent_id、order、title的字段名也是可以修改的:
<?php
namespace App\Models\Demo;
use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use ModelTree;
protected $table = 'demo_categories';
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setParentColumn('pid');
$this->setOrderColumn('sort');
$this->setTitleColumn('name');
}
}使用方法
然后就是在页面中使用model-tree了:
<?php
namespace App\Admin\Controllers\Demo;
use App\Http\Controllers\Controller;
use App\Models\Category;
use Encore\Admin\Form;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use Encore\Admin\Controllers\ModelForm;
use Encore\Admin\Tree;
class CategoryController extends Controller
{
use ModelForm;
public function index()
{
return Admin::content(function (Content $content) {
$content->header('树状模型');
$content->body(Category::tree());
});
}
}可以通过下面的方式来修改行数据的显示:
Category::tree(function ($tree) {
$tree->branch(function ($branch) {
$src = config('admin.upload.host') . '/' . $branch['logo'] ;
$logo = "<img src='$src' style='max-width:30px;max-height:30px' class='img'/>";
return "{$branch['id']} - {$branch['title']} $logo";
});
})在回调函数中返回的字符串类型数据,就是在树状组件中的每一行的显示内容,$branch参数是当前行的数据数组。
如果要修改模型的查询,用下面的方式
Category::tree(function ($tree) {
$tree->query(function ($model) {
return $model->where('type', 1);
});
})---
## File: docs/zh/permission.md
# 权限控制
`laravel-admin`已经内置了`RBAC`权限控制模块,展开左侧边栏的`Auth`,下面有用户、角色、权限三项的管理面板,权限控制的使用如下:
## 路由控制
在`laravel-admin 1.5`中,权限和路由是绑定在一起的,在编辑权限页面里面设置当前权限能访问的路由,在`HTTP方法`select框中选择访问路由的方法,在`HTTP路径`textarea中填写能访问的路径。
比如要添加一个权限,该权限可以以`GET`方式访问路径`/admin/users`,那么`HTTP方法`选择`GET`,`HTTP路径`填写`/users`。
如果要访问前缀是`/admin/users`的所有路径,那么`HTTP路径`填写`/users*`,如果权限包括多个访问路径,换行填写每条路径。
## 页面控制
如果你要在页面中控制用户的权限,可以参考下面的例子
### 场景1
比如现在有一个场景,对文章发布模块做权限管理,以创建文章为例
首先创建一项权限,进入`http://localhost/admin/auth/permissions`,权限标识(slug)填写`create-post`,权限名称填写`创建文章`,这样权限就创建好了。
第二步可以把这个权限直接附加给个人或者角色,在用户编辑页面可以直接把上面创建好的权限附加给当前编辑用户,也可以在编辑角色页面附加给某个角色。
第三步,在创建文章控制器里面添加控制代码:
```php
use Encore\Admin\Auth\Permission;
class PostController extends Controller
{
public function create()
{
// 检查权限,有create-post权限的用户或者角色可以访问创建文章页面
Permission::check('create-post');
}
}这样就完成了一个页面的权限控制。
场景2
如果你要在表格中控制用户对元素的显示,那么需要先定义两个权限,比如权限标示delete-image、和view-title-column分别用来控制有删除图片的权限和显示某一列的权限,把这两个权限赋给你设置的角色,然后在grid中加入代码:
$grid->actions(function ($actions) {
// 没有`delete-image`权限的角色不显示删除按钮
if (!Admin::user()->can('delete-image')) {
$actions->disableDelete();
}
});
// 只有具有`view-title-column`权限的用户才能显示`title`这一列
if (Admin::user()->can('view-title-column')) {
$grid->column('title');
}相关方法
获取当前用户对象
Admin::user();获取当前用户id
Admin::user()->id;获取用户角色
Admin::user()->roles;获取用户的权限
Admin::user()->permissions;用户是否某个角色
Admin::user()->isRole('developer');是否有某个权限
Admin::user()->can('create-post');是否没有某个权限
Admin::user()->cannot('delete-post');是否是超级管理员
Admin::user()->isAdministrator();是否是其中的角色
Admin::user()->inRoles(['editor', 'developer']);权限中间件
可以在路由配置上结合权限中间件来控制路由的权限
// 允许administrator、editor两个角色访问group里面的路由
Route::group([
'middleware' => 'admin.permission:allow,administrator,editor',
], function ($router) {
$router->resource('users', UserController::class);
...
});
// 禁止developer、operator两个角色访问group里面的路由
Route::group([
'middleware' => 'admin.permission:deny,developer,operator',
], function ($router) {
$router->resource('users', UserController::class);
...
});
// 有edit-post、create-post、delete-post三个权限的用户可以访问group里面的路由
Route::group([
'middleware' => 'admin.permission:check,edit-post,create-post,delete-post',
], function ($router) {
$router->resource('posts', PostController::class);
...
});权限中间件和其它中间件使用方法一致。
File: docs/zh/qa.md
[Binary file]
File: docs/zh/quick-start.md
快速开始
数据表结构
用Laravel自带的users表举例,表结构为:
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci对应的数据模型为文件 App\User.php
laravel-admin可以通过使用以下几步来快速生成users表的CURD操作页面:
添加路由器
使用下面的命令来创建一个对应App\User模型的路由器
php artisan admin:make UserController --model=App\\User
// 在windows系统中
php artisan admin:make UserController --model=App\User上面的命令会创建路由器文件app/Admin/Controllers/UserController.php.
添加路由配置
在laravel-admin的路由配置文件app/Admin/routes.php里添加一行:
$router->resource('users', UserController::class);添加左侧菜单栏连接
打开http://localhost:8000/admin/auth/menu,添加对应的menu, 然后就能在后台管理页面的左侧边栏看到用户管理页面的链接入口了。
其中
uri填写不包含路由前缀的的路径部分,比如完整路径是http://localhost:8000/admin/demo/users, 那么就填demo/users,如果要添加外部链接,只要填写完整的url即可,比如http://laravel-admin.org/.
菜单翻译
在您的语言文件的menu_titles索引中追加菜单标题。
例如“工作单位”标题:
在resources/lang/es/admin.php中
...
// 用_小写并用_替换空格
'menu_titles' => [
'work_units' => 'Unidades de trabajo'
],创建表格表单
剩下的工作就是构建数据表格和表单了,打开 app/Admin/Contollers/UserController.php,找到form()和grid()方法,然添加构建代码更多详细使用请查看model-grid和model-form。
File: docs/zh/upgrade.md
升级注意事项
因为laravel-admin 1.5的内置表结构有修改,所以建议大家重新安装laravel 5.5和laravel-admin 1.5,然后再将代码迁移过来
代码迁移需要注意的事项:
- 表结构有修改 请参考 tables.php
- 路由文件结构有修改 请参考 routes.stub
- 配置文件结构有修改 请参考 admin.php
- 图表组件已经移除,不能再使用,参考 自定义图表
File: docs/zh/widgets.md
前端组件
Box组件
Encore\Admin\Widgets\Box用来生成box组件:
use Encore\Admin\Widgets\Box;
$box = new Box('Box标题', 'Box内容');
$box->removable();
$box->collapsable();
$box->style('info');
$box->solid();
echo $box;Box::__construct($title, $content),$title参数为Box组件的标题,$content参数为Box的内容元素,可以是实现了Illuminate\Contracts\Support\Renderable接口的对象或者其他可打印变量。
Box::title($title)方法用来设置Box组件标题。
Box::content($content)方法用来设置Box组件的内容元素。
Box::removable()方法将Box组件设置为可关闭。
Box::collapsable()方法将Box组件设置为可展开和收起。
Box::style($style)方法设置Box组件的样式,可填值为primary,info,danger,warning,success,default。
Box::solid()方法为Box组件添加边框。
Collapse组件
Encore\Admin\Widgets\Collapse类用来生成折叠插件:
use Encore\Admin\Widgets\Collapse;
$collapse = new Collapse();
$collapse->add('Bar', 'xxxxx');
$collapse->add('Orders', new Table());
echo $collapse->render();Collapse::add($title, $content)方法用来给折叠组件添加一个折叠项,$title参数设置该折叠项的标题,$content参数用来舍子折叠区的内用。
表单
Encore\Admin\Widgets\Form类用来快速构建表单:
$form = new Form();
$form->action('example');
$form->email('email')->default('[email protected]');
$form->password('password');
$form->text('name', '输入框');
$form->url('url');
$form->color('color');
$form->map('lat', 'lng');
$form->date('date');
$form->json('val');
$form->dateRange('created_at', 'updated_at');
echo $form->render();Form::__construct($data = [])生成一个form对象,如果传入了$data参数,$data数组中的元素将会按照key对应填入form对应name的表单中。
Form::action($uri)方法用来设置表单提交地址。
Form::method($method)方法用来设置form表单的提交方法,默认为POST方法。
Form::disablePjax() 不使用pjax方式提交表单。
Infobox组件
Encore\Admin\Widgets\InfoBox类用来生成信息展示块:
use Encore\Admin\Widgets\InfoBox;
$infoBox = new InfoBox('New Users', 'users', 'aqua', '/admin/users', '1024');
echo $infoBox->render();效果请参考后台首页的布局文件HomeController.php的index()方法中,关于InfoBox的部分。
Tab组件
Encore\Admin\Widgets\Tab类用来生成选项卡组件:
use Encore\Admin\Widgets\Tab;
$tab = new Tab();
$tab->add('Pie', $pie);
$tab->add('Table', new Table());
$tab->add('Text', 'blablablabla....');
echo $tab->render();Tab::add($title, $content)方法用来添加一个选项卡,$title为选项标题,$content选项卡内容。
Table
Encore\Admin\Widgets\Table类用来生成表格:
use Encore\Admin\Widgets\Table;
// table 1
$headers = ['Id', 'Email', 'Name', 'Company'];
$rows = [
[1, '[email protected]', 'Ms. Clotilde Gibson', 'Goodwin-Watsica'],
[2, '[email protected]', 'Allie Kuhic', 'Murphy, Koepp and Morar'],
[3, '[email protected]', 'Prof. Drew Heller', 'Kihn LLC'],
[4, '[email protected]', 'William Koss', 'Becker-Raynor'],
[5, '[email protected]', 'Ms. Antonietta Kozey Jr.'],
];
$table = new Table($headers, $rows);
echo $table->render();
// table 2
$headers = ['Keys', 'Values'];
$rows = [
'name' => 'Joe',
'age' => 25,
'gender' => 'Male',
'birth' => '1989-12-05',
];
$table = new Table($headers, $rows);
echo $table->render();File: resources/assets/AdminLTE/plugins/input-mask/phone-codes/readme.txt
more phone masks can be found at https://github.com/andr-04/inputmask-multi
File: resources/assets/AdminLTE/plugins/input-mask/phone-codes/phone-be.json
[
{ "mask": "+32(53)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Aalst (Alost)" },
{ "mask": "+32(3)###-##-##", "cc": "BE", "cd": "Belgium", "city": "Antwerpen (Anvers)" },
{ "mask": "+32(63)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Arlon" },
{ "mask": "+32(67)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Ath" },
{ "mask": "+32(50)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Brugge (Bruges)" },
{ "mask": "+32(2)###-##-##", "cc": "BE", "cd": "Belgium", "city": "Brussel/Bruxelles (Brussels)" },
{ "mask": "+32(71)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Charleroi" },
{ "mask": "+32(60)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Chimay" },
{ "mask": "+32(83)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Ciney" },
{ "mask": "+32(52)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Dendermonde" },
{ "mask": "+32(13)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Diest" },
{ "mask": "+32(82)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Dinant" },
{ "mask": "+32(86)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Durbuy" },
{ "mask": "+32(89)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Genk" },
{ "mask": "+32(9)###-##-##", "cc": "BE", "cd": "Belgium", "city": "Gent (Gand)" },
{ "mask": "+32(11)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Hasselt" },
{ "mask": "+32(14)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Herentals" },
{ "mask": "+32(85)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Huy (Hoei)" },
{ "mask": "+32(64)##-##-##", "cc": "BE", "cd": "Belgium", "city": "La Louvière" },
{ "mask": "+32(16)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Leuven (Louvain)" },
{ "mask": "+32(61)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Libramont" },
{ "mask": "+32(4)###-##-##", "cc": "BE", "cd": "Belgium", "city": "Liège (Luik)" },
{ "mask": "+32(15)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Mechelen (Malines)" },
{ "mask": "+32(47#)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Mobile Phones" },
{ "mask": "+32(48#)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Mobile Phones" },
{ "mask": "+32(49#)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Mobile Phones" },
{ "mask": "+32(65)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Mons (Bergen)" },
{ "mask": "+32(81)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Namur (Namen)" },
{ "mask": "+32(58)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Nieuwpoort (Nieuport)" },
{ "mask": "+32(54)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Ninove" },
{ "mask": "+32(67)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Nivelles (Nijvel)" },
{ "mask": "+32(59)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Oostende (Ostende)" },
{ "mask": "+32(51)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Roeselare (Roulers)" },
{ "mask": "+32(55)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Ronse" },
{ "mask": "+32(80)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Stavelot" },
{ "mask": "+32(12)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Tongeren (Tongres)" },
{ "mask": "+32(69)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Tounai" },
{ "mask": "+32(14)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Turnhout" },
{ "mask": "+32(87)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Verviers" },
{ "mask": "+32(58)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Veurne" },
{ "mask": "+32(19)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Wareme" },
{ "mask": "+32(10)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Wavre (Waver)" },
{ "mask": "+32(50)##-##-##", "cc": "BE", "cd": "Belgium", "city": "Zeebrugge" }
]
File: resources/assets/AdminLTE/plugins/input-mask/phone-codes/phone-codes.json
[
{ "mask": "+247-####", "cc": "AC", "name_en": "Ascension", "desc_en": "", "name_ru": "Остров Вознесения", "desc_ru": "" },
{ "mask": "+376-###-###", "cc": "AD", "name_en": "Andorra", "desc_en": "", "name_ru": "Андорра", "desc_ru": "" },
{ "mask": "+971-5#-###-####", "cc": "AE", "name_en": "United Arab Emirates", "desc_en": "mobile", "name_ru": "Объединенные Арабские Эмираты", "desc_ru": "мобильные" },
{ "mask": "+971-#-###-####", "cc": "AE", "name_en": "United Arab Emirates", "desc_en": "", "name_ru": "Объединенные Арабские Эмираты", "desc_ru": "" },
{ "mask": "+93-##-###-####", "cc": "AF", "name_en": "Afghanistan", "desc_en": "", "name_ru": "Афганистан", "desc_ru": "" },
{ "mask": "+1(268)###-####", "cc": "AG", "name_en": "Antigua & Barbuda", "desc_en": "", "name_ru": "Антигуа и Барбуда", "desc_ru": "" },
{ "mask": "+1(264)###-####", "cc": "AI", "name_en": "Anguilla", "desc_en": "", "name_ru": "Ангилья", "desc_ru": "" },
{ "mask": "+355(###)###-###", "cc": "AL", "name_en": "Albania", "desc_en": "", "name_ru": "Албания", "desc_ru": "" },
{ "mask": "+374-##-###-###", "cc": "AM", "name_en": "Armenia", "desc_en": "", "name_ru": "Армения", "desc_ru": "" },
{ "mask": "+599-###-####", "cc": "AN", "name_en": "Caribbean Netherlands", "desc_en": "", "name_ru": "Карибские Нидерланды", "desc_ru": "" },
{ "mask": "+599-###-####", "cc": "AN", "name_en": "Netherlands Antilles", "desc_en": "", "name_ru": "Нидерландские Антильские острова", "desc_ru": "" },
{ "mask": "+599-9###-####", "cc": "AN", "name_en": "Netherlands Antilles", "desc_en": "Curacao", "name_ru": "Нидерландские Антильские острова", "desc_ru": "Кюрасао" },
{ "mask": "+244(###)###-###", "cc": "AO", "name_en": "Angola", "desc_en": "", "name_ru": "Ангола", "desc_ru": "" },
{ "mask": "+672-1##-###", "cc": "AQ", "name_en": "Australian bases in Antarctica", "desc_en": "", "name_ru": "Австралийская антарктическая база", "desc_ru": "" },
{ "mask": "+54(###)###-####", "cc": "AR", "name_en": "Argentina", "desc_en": "", "name_ru": "Аргентина", "desc_ru": "" },
{ "mask": "+1(684)###-####", "cc": "AS", "name_en": "American Samoa", "desc_en": "", "name_ru": "Американское Самоа", "desc_ru": "" },
{ "mask": "+43(###)###-####", "cc": "AT", "name_en": "Austria", "desc_en": "", "name_ru": "Австрия", "desc_ru": "" },
{ "mask": "+61-#-####-####", "cc": "AU", "name_en": "Australia", "desc_en": "", "name_ru": "Австралия", "desc_ru": "" },
{ "mask": "+297-###-####", "cc": "AW", "name_en": "Aruba", "desc_en": "", "name_ru": "Аруба", "desc_ru": "" },
{ "mask": "+994-##-###-##-##", "cc": "AZ", "name_en": "Azerbaijan", "desc_en": "", "name_ru": "Азербайджан", "desc_ru": "" },
{ "mask": "+387-##-#####", "cc": "BA", "name_en": "Bosnia and Herzegovina", "desc_en": "", "name_ru": "Босния и Герцеговина", "desc_ru": "" },
{ "mask": "+387-##-####", "cc": "BA", "name_en": "Bosnia and Herzegovina", "desc_en": "", "name_ru": "Босния и Герцеговина", "desc_ru": "" },
{ "mask": "+1(246)###-####", "cc": "BB", "name_en": "Barbados", "desc_en": "", "name_ru": "Барбадос", "desc_ru": "" },
{ "mask": "+880-##-###-###", "cc": "BD", "name_en": "Bangladesh", "desc_en": "", "name_ru": "Бангладеш", "desc_ru": "" },
{ "mask": "+32(###)###-###", "cc": "BE", "name_en": "Belgium", "desc_en": "", "name_ru": "Бельгия", "desc_ru": "" },
{ "mask": "+226-##-##-####", "cc": "BF", "name_en": "Burkina Faso", "desc_en": "", "name_ru": "Буркина Фасо", "desc_ru": "" },
{ "mask": "+359(###)###-###", "cc": "BG", "name_en": "Bulgaria", "desc_en": "", "name_ru": "Болгария", "desc_ru": "" },
{ "mask": "+973-####-####", "cc": "BH", "name_en": "Bahrain", "desc_en": "", "name_ru": "Бахрейн", "desc_ru": "" },
{ "mask": "+257-##-##-####", "cc": "BI", "name_en": "Burundi", "desc_en": "", "name_ru": "Бурунди", "desc_ru": "" },
{ "mask": "+229-##-##-####", "cc": "BJ", "name_en": "Benin", "desc_en": "", "name_ru": "Бенин", "desc_ru": "" },
{ "mask": "+1(441)###-####", "cc": "BM", "name_en": "Bermuda", "desc_en": "", "name_ru": "Бермудские острова", "desc_ru": "" },
{ "mask": "+673-###-####", "cc": "BN", "name_en": "Brunei Darussalam", "desc_en": "", "name_ru": "Бруней-Даруссалам", "desc_ru": "" },
{ "mask": "+591-#-###-####", "cc": "BO", "name_en": "Bolivia", "desc_en": "", "name_ru": "Боливия", "desc_ru": "" },
{ "mask": "+55-##-####[#]-####", "cc": "BR", "name_en": "Brazil", "desc_en": "", "name_ru": "Бразилия", "desc_ru": "" },
{ "mask": "+1(242)###-####", "cc": "BS", "name_en": "Bahamas", "desc_en": "", "name_ru": "Багамские Острова", "desc_ru": "" },
{ "mask": "+975-17-###-###", "cc": "BT", "name_en": "Bhutan", "desc_en": "", "name_ru": "Бутан", "desc_ru": "" },
{ "mask": "+975-#-###-###", "cc": "BT", "name_en": "Bhutan", "desc_en": "", "name_ru": "Бутан", "desc_ru": "" },
{ "mask": "+267-##-###-###", "cc": "BW", "name_en": "Botswana", "desc_en": "", "name_ru": "Ботсвана", "desc_ru": "" },
{ "mask": "+375(##)###-##-##", "cc": "BY", "name_en": "Belarus", "desc_en": "", "name_ru": "Беларусь (Белоруссия)", "desc_ru": "" },
{ "mask": "+501-###-####", "cc": "BZ", "name_en": "Belize", "desc_en": "", "name_ru": "Белиз", "desc_ru": "" },
{ "mask": "+243(###)###-###", "cc": "CD", "name_en": "Dem. Rep. Congo", "desc_en": "", "name_ru": "Дем. Респ. Конго (Киншаса)", "desc_ru": "" },
{ "mask": "+236-##-##-####", "cc": "CF", "name_en": "Central African Republic", "desc_en": "", "name_ru": "Центральноафриканская Республика", "desc_ru": "" },
{ "mask": "+242-##-###-####", "cc": "CG", "name_en": "Congo (Brazzaville)", "desc_en": "", "name_ru": "Конго (Браззавиль)", "desc_ru": "" },
{ "mask": "+41-##-###-####", "cc": "CH", "name_en": "Switzerland", "desc_en": "", "name_ru": "Швейцария", "desc_ru": "" },
{ "mask": "+225-##-###-###", "cc": "CI", "name_en": "Cote d’Ivoire (Ivory Coast)", "desc_en": "", "name_ru": "Кот-д’Ивуар", "desc_ru": "" },
{ "mask": "+682-##-###", "cc": "CK", "name_en": "Cook Islands", "desc_en": "", "name_ru": "Острова Кука", "desc_ru": "" },
{ "mask": "+56-#-####-####", "cc": "CL", "name_en": "Chile", "desc_en": "", "name_ru": "Чили", "desc_ru": "" },
{ "mask": "+237-####-####", "cc": "CM", "name_en": "Cameroon", "desc_en": "", "name_ru": "Камерун", "desc_ru": "" },
{ "mask": "+86(###)####-####", "cc": "CN", "name_en": "China (PRC)", "desc_en": "", "name_ru": "Китайская Н.Р.", "desc_ru": "" },
{ "mask": "+86(###)####-###", "cc": "CN", "name_en": "China (PRC)", "desc_en": "", "name_ru": "Китайская Н.Р.", "desc_ru": "" },
{ "mask": "+86-##-#####-#####", "cc": "CN", "name_en": "China (PRC)", "desc_en": "", "name_ru": "Китайская Н.Р.", "desc_ru": "" },
{ "mask": "+57(###)###-####", "cc": "CO", "name_en": "Colombia", "desc_en": "", "name_ru": "Колумбия", "desc_ru": "" },
{ "mask": "+506-####-####", "cc": "CR", "name_en": "Costa Rica", "desc_en": "", "name_ru": "Коста-Рика", "desc_ru": "" },
{ "mask": "+53-#-###-####", "cc": "CU", "name_en": "Cuba", "desc_en": "", "name_ru": "Куба", "desc_ru": "" },
{ "mask": "+238(###)##-##", "cc": "CV", "name_en": "Cape Verde", "desc_en": "", "name_ru": "Кабо-Верде", "desc_ru": "" },
{ "mask": "+599-###-####", "cc": "CW", "name_en": "Curacao", "desc_en": "", "name_ru": "Кюрасао", "desc_ru": "" },
{ "mask": "+357-##-###-###", "cc": "CY", "name_en": "Cyprus", "desc_en": "", "name_ru": "Кипр", "desc_ru": "" },
{ "mask": "+420(###)###-###", "cc": "CZ", "name_en": "Czech Republic", "desc_en": "", "name_ru": "Чехия", "desc_ru": "" },
{ "mask": "+49(####)###-####", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+49(###)###-####", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+49(###)##-####", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+49(###)##-###", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+49(###)##-##", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+49-###-###", "cc": "DE", "name_en": "Germany", "desc_en": "", "name_ru": "Германия", "desc_ru": "" },
{ "mask": "+253-##-##-##-##", "cc": "DJ", "name_en": "Djibouti", "desc_en": "", "name_ru": "Джибути", "desc_ru": "" },
{ "mask": "+45-##-##-##-##", "cc": "DK", "name_en": "Denmark", "desc_en": "", "name_ru": "Дания", "desc_ru": "" },
{ "mask": "+1(767)###-####", "cc": "DM", "name_en": "Dominica", "desc_en": "", "name_ru": "Доминика", "desc_ru": "" },
{ "mask": "+1(809)###-####", "cc": "DO", "name_en": "Dominican Republic", "desc_en": "", "name_ru": "Доминиканская Республика", "desc_ru": "" },
{ "mask": "+1(829)###-####", "cc": "DO", "name_en": "Dominican Republic", "desc_en": "", "name_ru": "Доминиканская Республика", "desc_ru": "" },
{ "mask": "+1(849)###-####", "cc": "DO", "name_en": "Dominican Republic", "desc_en": "", "name_ru": "Доминиканская Республика", "desc_ru": "" },
{ "mask": "+213-##-###-####", "cc": "DZ", "name_en": "Algeria", "desc_en": "", "name_ru": "Алжир", "desc_ru": "" },
{ "mask": "+593-##-###-####", "cc": "EC", "name_en": "Ecuador ", "desc_en": "mobile", "name_ru": "Эквадор ", "desc_ru": "мобильные" },
{ "mask": "+593-#-###-####", "cc": "EC", "name_en": "Ecuador", "desc_en": "", "name_ru": "Эквадор", "desc_ru": "" },
{ "mask": "+372-####-####", "cc": "EE", "name_en": "Estonia ", "desc_en": "mobile", "name_ru": "Эстония ", "desc_ru": "мобильные" },
{ "mask": "+372-###-####", "cc": "EE", "name_en": "Estonia", "desc_en": "", "name_ru": "Эстония", "desc_ru": "" },
{ "mask": "+20(###)###-####", "cc": "EG", "name_en": "Egypt", "desc_en": "", "name_ru": "Египет", "desc_ru": "" },
{ "mask": "+291-#-###-###", "cc": "ER", "name_en": "Eritrea", "desc_en": "", "name_ru": "Эритрея", "desc_ru": "" },
{ "mask": "+34(###)###-###", "cc": "ES", "name_en": "Spain", "desc_en": "", "name_ru": "Испания", "desc_ru": "" },
{ "mask": "+251-##-###-####", "cc": "ET", "name_en": "Ethiopia", "desc_en": "", "name_ru": "Эфиопия", "desc_ru": "" },
{ "mask": "+358(###)###-##-##", "cc": "FI", "name_en": "Finland", "desc_en": "", "name_ru": "Финляндия", "desc_ru": "" },
{ "mask": "+679-##-#####", "cc": "FJ", "name_en": "Fiji", "desc_en": "", "name_ru": "Фиджи", "desc_ru": "" },
{ "mask": "+500-#####", "cc": "FK", "name_en": "Falkland Islands", "desc_en": "", "name_ru": "Фолклендские острова", "desc_ru": "" },
{ "mask": "+691-###-####", "cc": "FM", "name_en": "F.S. Micronesia", "desc_en": "", "name_ru": "Ф.Ш. Микронезии", "desc_ru": "" },
{ "mask": "+298-###-###", "cc": "FO", "name_en": "Faroe Islands", "desc_en": "", "name_ru": "Фарерские острова", "desc_ru": "" },
{ "mask": "+262-#####-####", "cc": "FR", "name_en": "Mayotte", "desc_en": "", "name_ru": "Майотта", "desc_ru": "" },
{ "mask": "+33(###)###-###", "cc": "FR", "name_en": "France", "desc_en": "", "name_ru": "Франция", "desc_ru": "" },
{ "mask": "+508-##-####", "cc": "FR", "name_en": "St Pierre & Miquelon", "desc_en": "", "name_ru": "Сен-Пьер и Микелон", "desc_ru": "" },
{ "mask": "+590(###)###-###", "cc": "FR", "name_en": "Guadeloupe", "desc_en": "", "name_ru": "Гваделупа", "desc_ru": "" },
{ "mask": "+241-#-##-##-##", "cc": "GA", "name_en": "Gabon", "desc_en": "", "name_ru": "Габон", "desc_ru": "" },
{ "mask": "+1(473)###-####", "cc": "GD", "name_en": "Grenada", "desc_en": "", "name_ru": "Гренада", "desc_ru": "" },
{ "mask": "+995(###)###-###", "cc": "GE", "name_en": "Rep. of Georgia", "desc_en": "", "name_ru": "Грузия", "desc_ru": "" },
{ "mask": "+594-#####-####", "cc": "GF", "name_en": "Guiana (French)", "desc_en": "", "name_ru": "Фр. Гвиана", "desc_ru": "" },
{ "mask": "+233(###)###-###", "cc": "GH", "name_en": "Ghana", "desc_en": "", "name_ru": "Гана", "desc_ru": "" },
{ "mask": "+350-###-#####", "cc": "GI", "name_en": "Gibraltar", "desc_en": "", "name_ru": "Гибралтар", "desc_ru": "" },
{ "mask": "+299-##-##-##", "cc": "GL", "name_en": "Greenland", "desc_en": "", "name_ru": "Гренландия", "desc_ru": "" },
{ "mask": "+220(###)##-##", "cc": "GM", "name_en": "Gambia", "desc_en": "", "name_ru": "Гамбия", "desc_ru": "" },
{ "mask": "+224-##-###-###", "cc": "GN", "name_en": "Guinea", "desc_en": "", "name_ru": "Гвинея", "desc_ru": "" },
{ "mask": "+240-##-###-####", "cc": "GQ", "name_en": "Equatorial Guinea", "desc_en": "", "name_ru": "Экваториальная Гвинея", "desc_ru": "" },
{ "mask": "+30(###)###-####", "cc": "GR", "name_en": "Greece", "desc_en": "", "name_ru": "Греция", "desc_ru": "" },
{ "mask": "+502-#-###-####", "cc": "GT", "name_en": "Guatemala", "desc_en": "", "name_ru": "Гватемала", "desc_ru": "" },
{ "mask": "+1(671)###-####", "cc": "GU", "name_en": "Guam", "desc_en": "", "name_ru": "Гуам", "desc_ru": "" },
{ "mask": "+245-#-######", "cc": "GW", "name_en": "Guinea-Bissau", "desc_en": "", "name_ru": "Гвинея-Бисау", "desc_ru": "" },
{ "mask": "+592-###-####", "cc": "GY", "name_en": "Guyana", "desc_en": "", "name_ru": "Гайана", "desc_ru": "" },
{ "mask": "+852-####-####", "cc": "HK", "name_en": "Hong Kong", "desc_en": "", "name_ru": "Гонконг", "desc_ru": "" },
{ "mask": "+504-####-####", "cc": "HN", "name_en": "Honduras", "desc_en": "", "name_ru": "Гондурас", "desc_ru": "" },
{ "mask": "+385-##-###-###", "cc": "HR", "name_en": "Croatia", "desc_en": "", "name_ru": "Хорватия", "desc_ru": "" },
{ "mask": "+509-##-##-####", "cc": "HT", "name_en": "Haiti", "desc_en": "", "name_ru": "Гаити", "desc_ru": "" },
{ "mask": "+36(###)###-###", "cc": "HU", "name_en": "Hungary", "desc_en": "", "name_ru": "Венгрия", "desc_ru": "" },
{ "mask": "+62(8##)###-####", "cc": "ID", "name_en": "Indonesia ", "desc_en": "mobile", "name_ru": "Индонезия ", "desc_ru": "мобильные" },
{ "mask": "+62-##-###-##", "cc": "ID", "name_en": "Indonesia", "desc_en": "", "name_ru": "Индонезия", "desc_ru": "" },
{ "mask": "+62-##-###-###", "cc": "ID", "name_en": "Indonesia", "desc_en": "", "name_ru": "Индонезия", "desc_ru": "" },
{ "mask": "+62-##-###-####", "cc": "ID", "name_en": "Indonesia", "desc_en": "", "name_ru": "Индонезия", "desc_ru": "" },
{ "mask": "+62(8##)###-###", "cc": "ID", "name_en": "Indonesia ", "desc_en": "mobile", "name_ru": "Индонезия ", "desc_ru": "мобильные" },
{ "mask": "+62(8##)###-##-###", "cc": "ID", "name_en": "Indonesia ", "desc_en": "mobile", "name_ru": "Индонезия ", "desc_ru": "мобильные" },
{ "mask": "+353(###)###-###", "cc": "IE", "name_en": "Ireland", "desc_en": "", "name_ru": "Ирландия", "desc_ru": "" },
{ "mask": "+972-5#-###-####", "cc": "IL", "name_en": "Israel ", "desc_en": "mobile", "name_ru": "Израиль ", "desc_ru": "мобильные" },
{ "mask": "+972-#-###-####", "cc": "IL", "name_en": "Israel", "desc_en": "", "name_ru": "Израиль", "desc_ru": "" },
{ "mask": "+91(####)###-###", "cc": "IN", "name_en": "India", "desc_en": "", "name_ru": "Индия", "desc_ru": "" },
{ "mask": "+246-###-####", "cc": "IO", "name_en": "Diego Garcia", "desc_en": "", "name_ru": "Диего-Гарсия", "desc_ru": "" },
{ "mask": "+964(###)###-####", "cc": "IQ", "name_en": "Iraq", "desc_en": "", "name_ru": "Ирак", "desc_ru": "" },
{ "mask": "+98(###)###-####", "cc": "IR", "name_en": "Iran", "desc_en": "", "name_ru": "Иран", "desc_ru": "" },
{ "mask": "+354-###-####", "cc": "IS", "name_en": "Iceland", "desc_en": "", "name_ru": "Исландия", "desc_ru": "" },
{ "mask": "+39(###)####-###", "cc": "IT", "name_en": "Italy", "desc_en": "", "name_ru": "Италия", "desc_ru": "" },
{ "mask": "+1(876)###-####", "cc": "JM", "name_en": "Jamaica", "desc_en": "", "name_ru": "Ямайка", "desc_ru": "" },
{ "mask": "+962-#-####-####", "cc": "JO", "name_en": "Jordan", "desc_en": "", "name_ru": "Иордания", "desc_ru": "" },
{ "mask": "+81-##-####-####", "cc": "JP", "name_en": "Japan ", "desc_en": "mobile", "name_ru": "Япония ", "desc_ru": "мобильные" },
{ "mask": "+81(###)###-###", "cc": "JP", "name_en": "Japan", "desc_en": "", "name_ru": "Япония", "desc_ru": "" },
{ "mask": "+254-###-######", "cc": "KE", "name_en": "Kenya", "desc_en": "", "name_ru": "Кения", "desc_ru": "" },
{ "mask": "+996(###)###-###", "cc": "KG", "name_en": "Kyrgyzstan", "desc_en": "", "name_ru": "Киргизия", "desc_ru": "" },
{ "mask": "+855-##-###-###", "cc": "KH", "name_en": "Cambodia", "desc_en": "", "name_ru": "Камбоджа", "desc_ru": "" },
{ "mask": "+686-##-###", "cc": "KI", "name_en": "Kiribati", "desc_en": "", "name_ru": "Кирибати", "desc_ru": "" },
{ "mask": "+269-##-#####", "cc": "KM", "name_en": "Comoros", "desc_en": "", "name_ru": "Коморы", "desc_ru": "" },
{ "mask": "+1(869)###-####", "cc": "KN", "name_en": "Saint Kitts & Nevis", "desc_en": "", "name_ru": "Сент-Китс и Невис", "desc_ru": "" },
{ "mask": "+850-191-###-####", "cc": "KP", "name_en": "DPR Korea (North) ", "desc_en": "mobile", "name_ru": "Корейская НДР ", "desc_ru": "мобильные" },
{ "mask": "+850-##-###-###", "cc": "KP", "name_en": "DPR Korea (North)", "desc_en": "", "name_ru": "Корейская НДР", "desc_ru": "" },
{ "mask": "+850-###-####-###", "cc": "KP", "name_en": "DPR Korea (North)", "desc_en": "", "name_ru": "Корейская НДР", "desc_ru": "" },
{ "mask": "+850-###-###", "cc": "KP", "name_en": "DPR Korea (North)", "desc_en": "", "name_ru": "Корейская НДР", "desc_ru": "" },
{ "mask": "+850-####-####", "cc": "KP", "name_en": "DPR Korea (North)", "desc_en": "", "name_ru": "Корейская НДР", "desc_ru": "" },
{ "mask": "+850-####-#############", "cc": "KP", "name_en": "DPR Korea (North)", "desc_en": "", "name_ru": "Корейская НДР", "desc_ru": "" },
{ "mask": "+82-##-###-####", "cc": "KR", "name_en": "Korea (South)", "desc_en": "", "name_ru": "Респ. Корея", "desc_ru": "" },
{ "mask": "+965-####-####", "cc": "KW", "name_en": "Kuwait", "desc_en": "", "name_ru": "Кувейт", "desc_ru": "" },
{ "mask": "+1(345)###-####", "cc": "KY", "name_en": "Cayman Islands", "desc_en": "", "name_ru": "Каймановы острова", "desc_ru": "" },
{ "mask": "+7(6##)###-##-##", "cc": "KZ", "name_en": "Kazakhstan", "desc_en": "", "name_ru": "Казахстан", "desc_ru": "" },
{ "mask": "+7(7##)###-##-##", "cc": "KZ", "name_en": "Kazakhstan", "desc_en": "", "name_ru": "Казахстан", "desc_ru": "" },
{ "mask": "+856(20##)###-###", "cc": "LA", "name_en": "Laos ", "desc_en": "mobile", "name_ru": "Лаос ", "desc_ru": "мобильные" },
{ "mask": "+856-##-###-###", "cc": "LA", "name_en": "Laos", "desc_en": "", "name_ru": "Лаос", "desc_ru": "" },
{ "mask": "+961-##-###-###", "cc": "LB", "name_en": "Lebanon ", "desc_en": "mobile", "name_ru": "Ливан ", "desc_ru": "мобильные" },
{ "mask": "+961-#-###-###", "cc": "LB", "name_en": "Lebanon", "desc_en": "", "name_ru": "Ливан", "desc_ru": "" },
{ "mask": "+1(758)###-####", "cc": "LC", "name_en": "Saint Lucia", "desc_en": "", "name_ru": "Сент-Люсия", "desc_ru": "" },
{ "mask": "+423(###)###-####", "cc": "LI", "name_en": "Liechtenstein", "desc_en": "", "name_ru": "Лихтенштейн", "desc_ru": "" },
{ "mask": "+94-##-###-####", "cc": "LK", "name_en": "Sri Lanka", "desc_en": "", "name_ru": "Шри-Ланка", "desc_ru": "" },
{ "mask": "+231-##-###-###", "cc": "LR", "name_en": "Liberia", "desc_en": "", "name_ru": "Либерия", "desc_ru": "" },
{ "mask": "+266-#-###-####", "cc": "LS", "name_en": "Lesotho", "desc_en": "", "name_ru": "Лесото", "desc_ru": "" },
{ "mask": "+370(###)##-###", "cc": "LT", "name_en": "Lithuania", "desc_en": "", "name_ru": "Литва", "desc_ru": "" },
{ "mask": "+352(###)###-###", "cc": "LU", "name_en": "Luxembourg", "desc_en": "", "name_ru": "Люксембург", "desc_ru": "" },
{ "mask": "+371-##-###-###", "cc": "LV", "name_en": "Latvia", "desc_en": "", "name_ru": "Латвия", "desc_ru": "" },
{ "mask": "+218-##-###-###", "cc": "LY", "name_en": "Libya", "desc_en": "", "name_ru": "Ливия", "desc_ru": "" },
{ "mask": "+218-21-###-####", "cc": "LY", "name_en": "Libya", "desc_en": "Tripoli", "name_ru": "Ливия", "desc_ru": "Триполи" },
{ "mask": "+212-##-####-###", "cc": "MA", "name_en": "Morocco", "desc_en": "", "name_ru": "Марокко", "desc_ru": "" },
{ "mask": "+377(###)###-###", "cc": "MC", "name_en": "Monaco", "desc_en": "", "name_ru": "Монако", "desc_ru": "" },
{ "mask": "+377-##-###-###", "cc": "MC", "name_en": "Monaco", "desc_en": "", "name_ru": "Монако", "desc_ru": "" },
{ "mask": "+373-####-####", "cc": "MD", "name_en": "Moldova", "desc_en": "", "name_ru": "Молдова", "desc_ru": "" },
{ "mask": "+382-##-###-###", "cc": "ME", "name_en": "Montenegro", "desc_en": "", "name_ru": "Черногория", "desc_ru": "" },
{ "mask": "+261-##-##-#####", "cc": "MG", "name_en": "Madagascar", "desc_en": "", "name_ru": "Мадагаскар", "desc_ru": "" },
{ "mask": "+692-###-####", "cc": "MH", "name_en": "Marshall Islands", "desc_en": "", "name_ru": "Маршалловы Острова", "desc_ru": "" },
{ "mask": "+389-##-###-###", "cc": "MK", "name_en": "Republic of Macedonia", "desc_en": "", "name_ru": "Респ. Македония", "desc_ru": "" },
{ "mask": "+223-##-##-####", "cc": "ML", "name_en": "Mali", "desc_en": "", "name_ru": "Мали", "desc_ru": "" },
{ "mask": "+95-##-###-###", "cc": "MM", "name_en": "Burma (Myanmar)", "desc_en": "", "name_ru": "Бирма (Мьянма)", "desc_ru": "" },
{ "mask": "+95-#-###-###", "cc": "MM", "name_en": "Burma (Myanmar)", "desc_en": "", "name_ru": "Бирма (Мьянма)", "desc_ru": "" },
{ "mask": "+95-###-###", "cc": "MM", "name_en": "Burma (Myanmar)", "desc_en": "", "name_ru": "Бирма (Мьянма)", "desc_ru": "" },
{ "mask": "+976-##-##-####", "cc": "MN", "name_en": "Mongolia", "desc_en": "", "name_ru": "Монголия", "desc_ru": "" },
{ "mask": "+853-####-####", "cc": "MO", "name_en": "Macau", "desc_en": "", "name_ru": "Макао", "desc_ru": "" },
{ "mask": "+1(670)###-####", "cc": "MP", "name_en": "Northern Mariana Islands", "desc_en": "", "name_ru": "Северные Марианские острова Сайпан", "desc_ru": "" },
{ "mask": "+596(###)##-##-##", "cc": "MQ", "name_en": "Martinique", "desc_en": "", "name_ru": "Мартиника", "desc_ru": "" },
{ "mask": "+222-##-##-####", "cc": "MR", "name_en": "Mauritania", "desc_en": "", "name_ru": "Мавритания", "desc_ru": "" },
{ "mask": "+1(664)###-####", "cc": "MS", "name_en": "Montserrat", "desc_en": "", "name_ru": "Монтсеррат", "desc_ru": "" },
{ "mask": "+356-####-####", "cc": "MT", "name_en": "Malta", "desc_en": "", "name_ru": "Мальта", "desc_ru": "" },
{ "mask": "+230-###-####", "cc": "MU", "name_en": "Mauritius", "desc_en": "", "name_ru": "Маврикий", "desc_ru": "" },
{ "mask": "+960-###-####", "cc": "MV", "name_en": "Maldives", "desc_en": "", "name_ru": "Мальдивские острова", "desc_ru": "" },
{ "mask": "+265-1-###-###", "cc": "MW", "name_en": "Malawi", "desc_en": "Telecom Ltd", "name_ru": "Малави", "desc_ru": "Telecom Ltd" },
{ "mask": "+265-#-####-####", "cc": "MW", "name_en": "Malawi", "desc_en": "", "name_ru": "Малави", "desc_ru": "" },
{ "mask": "+52(###)###-####", "cc": "MX", "name_en": "Mexico", "desc_en": "", "name_ru": "Мексика", "desc_ru": "" },
{ "mask": "+52-##-##-####", "cc": "MX", "name_en": "Mexico", "desc_en": "", "name_ru": "Мексика", "desc_ru": "" },
{ "mask": "+60-##-###-####", "cc": "MY", "name_en": "Malaysia ", "desc_en": "mobile", "name_ru": "Малайзия ", "desc_ru": "мобильные" },
{ "mask": "+60(###)###-###", "cc": "MY", "name_en": "Malaysia", "desc_en": "", "name_ru": "Малайзия", "desc_ru": "" },
{ "mask": "+60-##-###-###", "cc": "MY", "name_en": "Malaysia", "desc_en": "", "name_ru": "Малайзия", "desc_ru": "" },
{ "mask": "+60-#-###-###", "cc": "MY", "name_en": "Malaysia", "desc_en": "", "name_ru": "Малайзия", "desc_ru": "" },
{ "mask": "+258-##-###-###", "cc": "MZ", "name_en": "Mozambique", "desc_en": "", "name_ru": "Мозамбик", "desc_ru": "" },
{ "mask": "+264-##-###-####", "cc": "NA", "name_en": "Namibia", "desc_en": "", "name_ru": "Намибия", "desc_ru": "" },
{ "mask": "+687-##-####", "cc": "NC", "name_en": "New Caledonia", "desc_en": "", "name_ru": "Новая Каледония", "desc_ru": "" },
{ "mask": "+227-##-##-####", "cc": "NE", "name_en": "Niger", "desc_en": "", "name_ru": "Нигер", "desc_ru": "" },
{ "mask": "+672-3##-###", "cc": "NF", "name_en": "Norfolk Island", "desc_en": "", "name_ru": "Норфолк (остров)", "desc_ru": "" },
{ "mask": "+234(###)###-####", "cc": "NG", "name_en": "Nigeria", "desc_en": "", "name_ru": "Нигерия", "desc_ru": "" },
{ "mask": "+234-##-###-###", "cc": "NG", "name_en": "Nigeria", "desc_en": "", "name_ru": "Нигерия", "desc_ru": "" },
{ "mask": "+234-##-###-##", "cc": "NG", "name_en": "Nigeria", "desc_en": "", "name_ru": "Нигерия", "desc_ru": "" },
{ "mask": "+234(###)###-####", "cc": "NG", "name_en": "Nigeria ", "desc_en": "mobile", "name_ru": "Нигерия ", "desc_ru": "мобильные" },
{ "mask": "+505-####-####", "cc": "NI", "name_en": "Nicaragua", "desc_en": "", "name_ru": "Никарагуа", "desc_ru": "" },
{ "mask": "+31-##-###-####", "cc": "NL", "name_en": "Netherlands", "desc_en": "", "name_ru": "Нидерланды", "desc_ru": "" },
{ "mask": "+47(###)##-###", "cc": "NO", "name_en": "Norway", "desc_en": "", "name_ru": "Норвегия", "desc_ru": "" },
{ "mask": "+977-##-###-###", "cc": "NP", "name_en": "Nepal", "desc_en": "", "name_ru": "Непал", "desc_ru": "" },
{ "mask": "+674-###-####", "cc": "NR", "name_en": "Nauru", "desc_en": "", "name_ru": "Науру", "desc_ru": "" },
{ "mask": "+683-####", "cc": "NU", "name_en": "Niue", "desc_en": "", "name_ru": "Ниуэ", "desc_ru": "" },
{ "mask": "+64(###)###-###", "cc": "NZ", "name_en": "New Zealand", "desc_en": "", "name_ru": "Новая Зеландия", "desc_ru": "" },
{ "mask": "+64-##-###-###", "cc": "NZ", "name_en": "New Zealand", "desc_en": "", "name_ru": "Новая Зеландия", "desc_ru": "" },
{ "mask": "+64(###)###-####", "cc": "NZ", "name_en": "New Zealand", "desc_en": "", "name_ru": "Новая Зеландия", "desc_ru": "" },
{ "mask": "+968-##-###-###", "cc": "OM", "name_en": "Oman", "desc_en": "", "name_ru": "Оман", "desc_ru": "" },
{ "mask": "+507-###-####", "cc": "PA", "name_en": "Panama", "desc_en": "", "name_ru": "Панама", "desc_ru": "" },
{ "mask": "+51(###)###-###", "cc": "PE", "name_en": "Peru", "desc_en": "", "name_ru": "Перу", "desc_ru": "" },
{ "mask": "+689-##-##-##", "cc": "PF", "name_en": "French Polynesia", "desc_en": "", "name_ru": "Французская Полинезия (Таити)", "desc_ru": "" },
{ "mask": "+675(###)##-###", "cc": "PG", "name_en": "Papua New Guinea", "desc_en": "", "name_ru": "Папуа-Новая Гвинея", "desc_ru": "" },
{ "mask": "+63(###)###-####", "cc": "PH", "name_en": "Philippines", "desc_en": "", "name_ru": "Филиппины", "desc_ru": "" },
{ "mask": "+92(###)###-####", "cc": "PK", "name_en": "Pakistan", "desc_en": "", "name_ru": "Пакистан", "desc_ru": "" },
{ "mask": "+48(###)###-###", "cc": "PL", "name_en": "Poland", "desc_en": "", "name_ru": "Польша", "desc_ru": "" },
{ "mask": "+970-##-###-####", "cc": "PS", "name_en": "Palestine", "desc_en": "", "name_ru": "Палестина", "desc_ru": "" },
{ "mask": "+351-##-###-####", "cc": "PT", "name_en": "Portugal", "desc_en": "", "name_ru": "Португалия", "desc_ru": "" },
{ "mask": "+680-###-####", "cc": "PW", "name_en": "Palau", "desc_en": "", "name_ru": "Палау", "desc_ru": "" },
{ "mask": "+595(###)###-###", "cc": "PY", "name_en": "Paraguay", "desc_en": "", "name_ru": "Парагвай", "desc_ru": "" },
{ "mask": "+974-####-####", "cc": "QA", "name_en": "Qatar", "desc_en": "", "name_ru": "Катар", "desc_ru": "" },
{ "mask": "+262-#####-####", "cc": "RE", "name_en": "Reunion", "desc_en": "", "name_ru": "Реюньон", "desc_ru": "" },
{ "mask": "+40-##-###-####", "cc": "RO", "name_en": "Romania", "desc_en": "", "name_ru": "Румыния", "desc_ru": "" },
{ "mask": "+381-##-###-####", "cc": "RS", "name_en": "Serbia", "desc_en": "", "name_ru": "Сербия", "desc_ru": "" },
{ "mask": "+7(###)###-##-##", "cc": "RU", "name_en": "Russia", "desc_en": "", "name_ru": "Россия", "desc_ru": "" },
{ "mask": "+250(###)###-###", "cc": "RW", "name_en": "Rwanda", "desc_en": "", "name_ru": "Руанда", "desc_ru": "" },
{ "mask": "+966-5-####-####", "cc": "SA", "name_en": "Saudi Arabia ", "desc_en": "mobile", "name_ru": "Саудовская Аравия ", "desc_ru": "мобильные" },
{ "mask": "+966-#-###-####", "cc": "SA", "name_en": "Saudi Arabia", "desc_en": "", "name_ru": "Саудовская Аравия", "desc_ru": "" },
{ "mask": "+677-###-####", "cc": "SB", "name_en": "Solomon Islands ", "desc_en": "mobile", "name_ru": "Соломоновы Острова ", "desc_ru": "мобильные" },
{ "mask": "+677-#####", "cc": "SB", "name_en": "Solomon Islands", "desc_en": "", "name_ru": "Соломоновы Острова", "desc_ru": "" },
{ "mask": "+248-#-###-###", "cc": "SC", "name_en": "Seychelles", "desc_en": "", "name_ru": "Сейшелы", "desc_ru": "" },
{ "mask": "+249-##-###-####", "cc": "SD", "name_en": "Sudan", "desc_en": "", "name_ru": "Судан", "desc_ru": "" },
{ "mask": "+46-##-###-####", "cc": "SE", "name_en": "Sweden", "desc_en": "", "name_ru": "Швеция", "desc_ru": "" },
{ "mask": "+65-####-####", "cc": "SG", "name_en": "Singapore", "desc_en": "", "name_ru": "Сингапур", "desc_ru": "" },
{ "mask": "+290-####", "cc": "SH", "name_en": "Saint Helena", "desc_en": "", "name_ru": "Остров Святой Елены", "desc_ru": "" },
{ "mask": "+290-####", "cc": "SH", "name_en": "Tristan da Cunha", "desc_en": "", "name_ru": "Тристан-да-Кунья", "desc_ru": "" },
{ "mask": "+386-##-###-###", "cc": "SI", "name_en": "Slovenia", "desc_en": "", "name_ru": "Словения", "desc_ru": "" },
{ "mask": "+421(###)###-###", "cc": "SK", "name_en": "Slovakia", "desc_en": "", "name_ru": "Словакия", "desc_ru": "" },
{ "mask": "+232-##-######", "cc": "SL", "name_en": "Sierra Leone", "desc_en": "", "name_ru": "Сьерра-Леоне", "desc_ru": "" },
{ "mask": "+378-####-######", "cc": "SM", "name_en": "San Marino", "desc_en": "", "name_ru": "Сан-Марино", "desc_ru": "" },
{ "mask": "+221-##-###-####", "cc": "SN", "name_en": "Senegal", "desc_en": "", "name_ru": "Сенегал", "desc_ru": "" },
{ "mask": "+252-##-###-###", "cc": "SO", "name_en": "Somalia", "desc_en": "", "name_ru": "Сомали", "desc_ru": "" },
{ "mask": "+252-#-###-###", "cc": "SO", "name_en": "Somalia", "desc_en": "", "name_ru": "Сомали", "desc_ru": "" },
{ "mask": "+252-#-###-###", "cc": "SO", "name_en": "Somalia ", "desc_en": "mobile", "name_ru": "Сомали ", "desc_ru": "мобильные" },
{ "mask": "+597-###-####", "cc": "SR", "name_en": "Suriname ", "desc_en": "mobile", "name_ru": "Суринам ", "desc_ru": "мобильные" },
{ "mask": "+597-###-###", "cc": "SR", "name_en": "Suriname", "desc_en": "", "name_ru": "Суринам", "desc_ru": "" },
{ "mask": "+211-##-###-####", "cc": "SS", "name_en": "South Sudan", "desc_en": "", "name_ru": "Южный Судан", "desc_ru": "" },
{ "mask": "+239-##-#####", "cc": "ST", "name_en": "Sao Tome and Principe", "desc_en": "", "name_ru": "Сан-Томе и Принсипи", "desc_ru": "" },
{ "mask": "+503-##-##-####", "cc": "SV", "name_en": "El Salvador", "desc_en": "", "name_ru": "Сальвадор", "desc_ru": "" },
{ "mask": "+1(721)###-####", "cc": "SX", "name_en": "Sint Maarten", "desc_en": "", "name_ru": "Синт-Маартен", "desc_ru": "" },
{ "mask": "+963-##-####-###", "cc": "SY", "name_en": "Syrian Arab Republic", "desc_en": "", "name_ru": "Сирийская арабская республика", "desc_ru": "" },
{ "mask": "+268-##-##-####", "cc": "SZ", "name_en": "Swaziland", "desc_en": "", "name_ru": "Свазиленд", "desc_ru": "" },
{ "mask": "+1(649)###-####", "cc": "TC", "name_en": "Turks & Caicos", "desc_en": "", "name_ru": "Тёркс и Кайкос", "desc_ru": "" },
{ "mask": "+235-##-##-##-##", "cc": "TD", "name_en": "Chad", "desc_en": "", "name_ru": "Чад", "desc_ru": "" },
{ "mask": "+228-##-###-###", "cc": "TG", "name_en": "Togo", "desc_en": "", "name_ru": "Того", "desc_ru": "" },
{ "mask": "+66-##-###-####", "cc": "TH", "name_en": "Thailand ", "desc_en": "mobile", "name_ru": "Таиланд ", "desc_ru": "мобильные" },
{ "mask": "+66-##-###-###", "cc": "TH", "name_en": "Thailand", "desc_en": "", "name_ru": "Таиланд", "desc_ru": "" },
{ "mask": "+992-##-###-####", "cc": "TJ", "name_en": "Tajikistan", "desc_en": "", "name_ru": "Таджикистан", "desc_ru": "" },
{ "mask": "+690-####", "cc": "TK", "name_en": "Tokelau", "desc_en": "", "name_ru": "Токелау", "desc_ru": "" },
{ "mask": "+670-###-####", "cc": "TL", "name_en": "East Timor", "desc_en": "", "name_ru": "Восточный Тимор", "desc_ru": "" },
{ "mask": "+670-77#-#####", "cc": "TL", "name_en": "East Timor", "desc_en": "Timor Telecom", "name_ru": "Восточный Тимор", "desc_ru": "Timor Telecom" },
{ "mask": "+670-78#-#####", "cc": "TL", "name_en": "East Timor", "desc_en": "Timor Telecom", "name_ru": "Восточный Тимор", "desc_ru": "Timor Telecom" },
{ "mask": "+993-#-###-####", "cc": "TM", "name_en": "Turkmenistan", "desc_en": "", "name_ru": "Туркменистан", "desc_ru": "" },
{ "mask": "+216-##-###-###", "cc": "TN", "name_en": "Tunisia", "desc_en": "", "name_ru": "Тунис", "desc_ru": "" },
{ "mask": "+676-#####", "cc": "TO", "name_en": "Tonga", "desc_en": "", "name_ru": "Тонга", "desc_ru": "" },
{ "mask": "+90(###)###-####", "cc": "TR", "name_en": "Turkey", "desc_en": "", "name_ru": "Турция", "desc_ru": "" },
{ "mask": "+1(868)###-####", "cc": "TT", "name_en": "Trinidad & Tobago", "desc_en": "", "name_ru": "Тринидад и Тобаго", "desc_ru": "" },
{ "mask": "+688-90####", "cc": "TV", "name_en": "Tuvalu ", "desc_en": "mobile", "name_ru": "Тувалу ", "desc_ru": "мобильные" },
{ "mask": "+688-2####", "cc": "TV", "name_en": "Tuvalu", "desc_en": "", "name_ru": "Тувалу", "desc_ru": "" },
{ "mask": "+886-#-####-####", "cc": "TW", "name_en": "Taiwan", "desc_en": "", "name_ru": "Тайвань", "desc_ru": "" },
{ "mask": "+886-####-####", "cc": "TW", "name_en": "Taiwan", "desc_en": "", "name_ru": "Тайвань", "desc_ru": "" },
{ "mask": "+255-##-###-####", "cc": "TZ", "name_en": "Tanzania", "desc_en": "", "name_ru": "Танзания", "desc_ru": "" },
{ "mask": "+380(##)###-##-##", "cc": "UA", "name_en": "Ukraine", "desc_en": "", "name_ru": "Украина", "desc_ru": "" },
{ "mask": "+256(###)###-###", "cc": "UG", "name_en": "Uganda", "desc_en": "", "name_ru": "Уганда", "desc_ru": "" },
{ "mask": "+44-##-####-####", "cc": "UK", "name_en": "United Kingdom", "desc_en": "", "name_ru": "Великобритания", "desc_ru": "" },
{ "mask": "+598-#-###-##-##", "cc": "UY", "name_en": "Uruguay", "desc_en": "", "name_ru": "Уругвай", "desc_ru": "" },
{ "mask": "+998-##-###-####", "cc": "UZ", "name_en": "Uzbekistan", "desc_en": "", "name_ru": "Узбекистан", "desc_ru": "" },
{ "mask": "+39-6-698-#####", "cc": "VA", "name_en": "Vatican City", "desc_en": "", "name_ru": "Ватикан", "desc_ru": "" },
{ "mask": "+1(784)###-####", "cc": "VC", "name_en": "Saint Vincent & the Grenadines", "desc_en": "", "name_ru": "Сент-Винсент и Гренадины", "desc_ru": "" },
{ "mask": "+58(###)###-####", "cc": "VE", "name_en": "Venezuela", "desc_en": "", "name_ru": "Венесуэла", "desc_ru": "" },
{ "mask": "+1(284)###-####", "cc": "VG", "name_en": "British Virgin Islands", "desc_en": "", "name_ru": "Британские Виргинские острова", "desc_ru": "" },
{ "mask": "+1(340)###-####", "cc": "VI", "name_en": "US Virgin Islands", "desc_en": "", "name_ru": "Американские Виргинские острова", "desc_ru": "" },
{ "mask": "+84-##-####-###", "cc": "VN", "name_en": "Vietnam", "desc_en": "", "name_ru": "Вьетнам", "desc_ru": "" },
{ "mask": "+84(###)####-###", "cc": "VN", "name_en": "Vietnam", "desc_en": "", "name_ru": "Вьетнам", "desc_ru": "" },
{ "mask": "+678-##-#####", "cc": "VU", "name_en": "Vanuatu ", "desc_en": "mobile", "name_ru": "Вануату ", "desc_ru": "мобильные" },
{ "mask": "+678-#####", "cc": "VU", "name_en": "Vanuatu", "desc_en": "", "name_ru": "Вануату", "desc_ru": "" },
{ "mask": "+681-##-####", "cc": "WF", "name_en": "Wallis and Futuna", "desc_en": "", "name_ru": "Уоллис и Футуна", "desc_ru": "" },
{ "mask": "+685-##-####", "cc": "WS", "name_en": "Samoa", "desc_en": "", "name_ru": "Самоа", "desc_ru": "" },
{ "mask": "+967-###-###-###", "cc": "YE", "name_en": "Yemen ", "desc_en": "mobile", "name_ru": "Йемен ", "desc_ru": "мобильные" },
{ "mask": "+967-#-###-###", "cc": "YE", "name_en": "Yemen", "desc_en": "", "name_ru": "Йемен", "desc_ru": "" },
{ "mask": "+967-##-###-###", "cc": "YE", "name_en": "Yemen", "desc_en": "", "name_ru": "Йемен", "desc_ru": "" },
{ "mask": "+27-##-###-####", "cc": "ZA", "name_en": "South Africa", "desc_en": "", "name_ru": "Южно-Африканская Респ.", "desc_ru": "" },
{ "mask": "+260-##-###-####", "cc": "ZM", "name_en": "Zambia", "desc_en": "", "name_ru": "Замбия", "desc_ru": "" },
{ "mask": "+263-#-######", "cc": "ZW", "name_en": "Zimbabwe", "desc_en": "", "name_ru": "Зимбабве", "desc_ru": "" },
{ "mask": "+1(###)###-####", "cc": ["US", "CA"], "name_en": "USA and Canada", "desc_en": "", "name_ru": "США и Канада", "desc_ru": "" }
]
File: .github/dependabot.yml
version: 2
updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: daily
time: "21:00"
open-pull-requests-limit: 10
File: .github/stale.yml
Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
Label to use when marking an issue as stale
staleLabel: wontfix
Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false