关键词微翻译,自用。
Events
There are many events that are fired throughout the applications lifecycle if they meet the requirements. 如果满足需求,在应用程序生命周期中会触发许多事件。
A current list of events fired are:
Frontend
- UserConfirmed
- UserLoggedIn
- UserLoggedOut
- UserRegistered
- UserProviderRegistered
Backend
- UserCreated
- UserDeactivated(禁用)
- UserDeleted()
- UserPasswordChanged
- UserPermanentlyDeleted(永久删除)
- UserReactivated(激活)
- UserRestored
- UserUpdated
- UserConfirmed
- UserUnconfirmed
- UserSocialDeleted
- RoleCreated
- RoleDeleted
- RoleUpdated
The event stubs(实例) are found in the app\Events directory and are registered in the EventServiceProvider using the Event Subscriber feature of Laravel.
The event listeners are found in the app\Listeners directory.
Subscribers (事件订阅)
Currently, all the subscriber events do is log when the user performs the event being fired. This is meant to be extended by you to do whatever you want.
目前,所有订阅事件都会在用户执行触发事件时进行日志记录。这就意味着你可以做任何你想要的扩展。
The events are fired using the Laravel event() helper throughout the application.
所有的事件都是通过 Laravel event() 来触发。
When the event helper is called with the event class, the objects needed to process the event are passed through, which are caught in the constructor of the Event classes themselves, and passed through to the listener as a property on the $event variable. (This is default Laravel behavior and not specific to this application).
当 event class 调用 event helper 时,处理事件所需的对象将被传递,这些对象在事件类本身的构造函数中被捕获,并作为 $ event 变量传递给侦听器。(这是 Laravel 默认的行为,并不是针对这个应用程序的)
Exceptions (异常)
Besides the default PHP Exception class, there is one custom exception that gets thrown called GeneralException. 除了默认的PHP异常类之外,还会抛出一个自定义的异常 GeneralException
This exception does nothing special except act as a way to change the default exception functionality when calling it. 这个异常的作用是提供一种修改默认异常的调用方式。(demo?)
Any custom exceptions live in the app\Exceptions directory. app\Exceptions 目录中可以存放任何自定义的异常处理类。
Anytime you throw this exception, it will not display a "Whoops" error. Instead, it will take the error into a session variable and redirect the user to the previous page and display the message in a bootstrap danger alert box. 当您抛出这个异常时,它将不会显示“Whoops”错误。相反,它会将错误带到会话变量中,并将用户重定向到上一个页面,并在危险警告框中显示出错消息。
If a regular PHP Exception is thrown, it will still get caught and the default functionality will takeover.
Example order of events would be:
-
User tries to delete the administrator account.
-
Repository says this is not allowed and throws a GeneralException with the text explaining the reason.
-
The Handler class catches the exception and redirects the user back with old input.
-
A session variable called flash_danger is set with the exception text.
-
The page is rendered and a file called messages.blade.php checks for this session variable, and displays the proper bootstrap alert with the provided text.
-
一个用户试图删除 管理员账号.
-
Repository 说这是不允许的,并抛出了一个 GeneralException 异常,并显示了异常原因.
-
处理程序类捕获异常并将用户重定向到之前的输入。
-
异常信息存储在 session 中的 flash_danger 变量中。
-
messages.blade.php 会检查这个 session 变量并渲染,然后弹出警告框显示这个信息。
Note: The withFlashDanger($text) is some Laravel magic that is parsed into with('flash_danger', $text). This functionality exists in many places around the Laravel Framework. Note: withFlashDanger($text) 是 Laravel 的魔术方法,会被解析成 with('flash_danger',$text) . 这个函数在 Laravel 里面很多地方都存在。
Note: In the case of the request classes, the default functionality when validation fails is to redirect back with errors:
From the Laravel Documentation:
"If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors." “如果验证失败,Laravel 会自动把用户重定向到先前的位置。另外,所有的验证错误信息会被自动闪存到 session。如果请求是AJAX请求,那么将返回带有422状态码的HTTP响应和验证错误信息的JSON数据"
Breadcrumbs (面包屑导航)
The breadcrumb links in the backend are managed by the davejamesmiller/laravel-breadcrumbs package.
The parts to this package integrated are:
- The config\breadcrumbs.php file which tells the system which view file to use for the rendering.
- The views\backend\includes\partials\breadcrumbs.blade.php file which renders the links.
- The call to render the breadcrumbs.
- The breadcrumb files located in routes/breadcrumbs.
- The resources/views/backend/includes/partials/breadcrumbs.php file that the plugin looks for to render the links.
Whenever you add new routes to your project, you must add breadcrumb files to go along with them to have them rendered, it is not automatic. Use the existing files for reference. 当您在项目中添加新的路由时,您必须添加面包屑文件以便与它们一起呈现,这不是自动的。使用现有的文件作为参考。
Composers ( 视图合成器 )
The app\Http\Composers directory holds all view composers. 在 app\Http\Composers 目录中可以看到所有的 view composers (cn)
There are 2 composers that ship with the boilerplate:
- GlobalComposer: Variables sent to every view.(全局视图合成器的变量可以传递到所有的视图)
- SidebarComposer: Variables sent to views containing a sidebar.(侧边栏视图合成器)
The composer classes themselves are registered in the app\Providers\ComposerServiceProvider class.
The GlobalComposer binds the variable $logged_in_user to every view. 全局视图合成器会绑定一个全局变量 $logged_in_user 到所有视图。
If the user is logged in, it will be an app\Models\Auth\User object, if they are not it will be false.
Routes ( 路由 )
The only stock route file modified is routes\web.php
Lets look at web.php, line by line:
// 第一行的路由主要用户多语言切换的下拉选择框,
// 用户选择相应的语言会被存储在session中
Route::get('lang/{lang}', 'LanguageController@swap');
The first line gets envoked anytime the user chooses a language from the language picker dropdown. The LanguageController's swap method switches the language code in the session and refreshes the page for it to take effect.
Route::group(['namespace' => 'Frontend', 'as' => 'frontend.'], function () {
includeRouteFiles(__DIR__ . '/frontend/');
});
This section registers all of the frontend routes, such as login, register, etc.
Key Points:
- The namespaces of the routes indicate the folder structure. In the above case the routes that will be included live in routes\frontend.( 在上面这个例子中, routes\frontend 目录中的路由都会被导入进来。)
- The as property prepends the value to all routes inside the closure, so in the above case all included route names will be prepended with frontend..
- The includeRouteFiles() is a helper function located in app\helpers.php and autoloaded by composer. (Learn about the helpers here). This takes all files in the specified directory and includes them in the closure so you can add new routes without having to touch the web.php routes file. includeRouteFiles() 是 app\helpers.php 提供的帮助函数,这个文件会被 composer 自动导入。这个函数的作用是通过闭包导入你放置在不同特殊目录下的路由文件到 web.php 中。
Route::group(
['namespace' => 'Backend',
'prefix' => 'admin', 'as' => 'admin.',
'middleware' => 'admin'
], function () {
includeRouteFiles(__DIR__ . '/backend/');
});
This section registers all of the backend routes, such as admin dashboard, user management, etc.
It is nearly identical as the frontend routes with the addition of the admin middleware and prefix.( 后台跟前台的区别是多了 middleware 和 prefix )
Key Points:
- The namespaces of the routes indicate the folder structure. In the above case the routes that will be included live in routes\backend.
- The as property prepends the value to all routes inside the closure, so in the above case all included route names will be prepended with admin..
- The prefix property prepends the value before all of the URL's of the routes inside the closure, so in the above case all route URL's will be prepended with admin/.
- The includeRouteFiles() is a helper function located in app\helpers.php and autoloaded by composer. (Learn about the helpers here). This takes all files in the specified directory and includes them in the closure so you can add new routes without having to touch the web.php routes file.
- The admin middleware is specified in app\Http\Kernel.php and states that anyone trying to access the routes in the following closure must:( admin 中间件过滤所有要访问后台路由的请求必须满足以下条件 )
- Be logged in ( 登录 )
- Have the view-backend permission associated with one of their roles or by itself. ( 包含相应的权限 )
Note: Administrator should get all permissions so you do not have to specify the administrator role everywhere.
Note: Most route resources use Laravel's Route/Model Binding which you will see as well in the controller methods.
For more information about the permission middleware included in the admin middleware, see middleware and Access Control.
Controllers ( 控制器 )
All of the controllers live in the default Laravel controller location of app\Http\Controllers and follow a namespacing convention of the folders they're in.
The controllers are very clean, so there is not much to say, some key points and different sections to read about are: ( Controller 几乎没有什么改动,以下几个关键点可以注意下 )
- All controller methods use injected Request Classes to both validate and act as a second security check after the middleware.
- All database logic is extracted out to Repositories.
- Controllers are only comprised of CRUD methods, any other methods have been extracted to other controllers for cleanliness.
- Any data table has its own dedicated controller.
- Any data being bound or returned to views are using Laravel magic with() method just like elsewhere in the application.
- Example: withUser($user) is converted to with('user', $user)
Middleware
Laravel Boilerplate ships with 4 additional( 额外的 ) middleware out of the box.(开箱即用)
There is also one extra middleware group:
LocaleMiddleware ( 区域中间件 )
The LocaleMiddleware is appended to the web middleware stack and runs on each request.
It takes care of:
- Checking to see if localization is turned on in the config file.
- Setting the session locale code.
- Setting the PHP locale code.
- Setting the Carbon library locale code.
- Checking to see if the required language is a "Right-to-Left" language and sets a session variable to be used elsewhere in the system.
Role
(role 和 permission 用的是 Laravel permission design by spatie)
The Role middleware is the default role middleware used by the spatie permission package.
Permission
The Permission middleware is the default permission middleware used by the spatie permission package.
PasswordExpires ( 密码有效期控制 )
The password expires middleware forces the user to change their password after X number of days if specified in the access config file's password_expires_days property. You can disable it by setting it to false. 密码到期中间件会强制用户更改到期后的密码,如果你想取消可以到配置文件中更改配置项:
// .env
PASSWORD_EXPIRES_DAYS=false
Middleware Groups
Laravel Boilerplate currently ships with one additional middleware group called admin.
admin Middleware Group 主要用于管控需要登录权限访问的路由分组。
The admin middleware is specified in app\Http\Kernel.php and states that anyone trying to access the routes in the following closure must:
- Be logged in
- Have the view-backend permission associated with one of their roles.
- Be subject to being forced to change your password after X number of days.
It currently wraps all backend routes by default.
Note: If you remove the admin middleware from the backend routes, anyone will be able to access the backend unless you specify elsewhere.
Requests
Any controller method throught the application that either needs validation or a security check ( 验证和安全检查 ), will have its own injected Request class.
App requests are stored in the app\Http\Request folder and their namespaces match the folder structure.
Example Request (查看 request demo 理解 requests)
We are going to look at the store method of the app\Http\Controllers\Backend\Auth\User\UserController to see what's going on.
public function store(StoreUserRequest $request)
{
$this->userRepository->create($request->only(
'first_name',
'last_name',
'email',
'password',
'timezone',
'active',
'confirmed',
'confirmation_email',
'roles',
'permissions'
));
return redirect()->route('admin.auth.user.index')->withFlashSuccess(__('alerts.backend.users.created'));
}
Disregard what's inside the method and instead look at the parameter list.
The StoreUserRequest is being injected to the controller, you do not have to do anything as it is parsed by Laravel automatically, and before the code inside the method even runs.
The request itself looks like this:
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'first_name' => 'required|max:191',
'last_name' => 'required|max:191',
'email' => ['required', 'email', 'max:191', Rule::unique('users')],
'timezone' => 'required|max:191',
'password' => 'required|min:6|confirmed',
'roles' => 'required|array',
];
}
}
As you can see it does two things:
- It make sure the user is an administrator. (确保是管理员操作)
- It validates the incoming request. (验证参数)
If the authorization fails the user will be redirected back with a message stating they do not have the required permissions.
If the validation fails the default Laravel functionality takes over which is:
"If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors."
Any controller method that needs to validate data or permission should use a request class for an extra layer of security.
Note: This project uses more general permissions for CRUD operations, meaning the default requests for a manage-users permission would be:
- ManageUserRequest (管理用户请求)- for any requests that do not have validation but want to assure the user can manage-users
- StoreUserRequest - for creating a user and assuring the user can manage-users
- UpdateUserRequest - for updating a user and assuring the user can manage-users
If you were to get more granular(粒度), and have separate permissions for all stages of CRUD, you may have controller methods that are injecting the following request classes:
- ViewUserRequest - auth()->can('view-users')
- StoreUserRequest - auth()->can('store-users')
- EditUserRequest - auth()->can('edit-users')
- UpdateUserRequest - auth()->can('update-users')
- DeleteUserRequest - auth()->can('delete-users')
Learn more about Laravel request classes here.
Models
Models in Laravel Boilerplate live in the app\Models directory instead of the root for organizational purposes. Laravel Boilerplate 对 Models 划分了一个 app\Models 目录来管理。
The folder structure of the Model indicates its namespace as always.
The majority of the models share the same characteristics:
- The $table property houses the table name, whether static or pulling from a config file.
- The $fillable property defines the columns that can be mass assigned. This is preferred over guarded as it is more specific.
- Some models have a $hidden property to hide certain information from being displayed, in such ways as in an API.
- Some models have a $dates property to cast certain columns as Carbon objects, such as when using the Laravel SoftDeletes trait.
- Many models have traits that extend or include other functionality, see below.
// 成员变量 table 用于指定 数据库表名
$table
// fillable 可以设置能被批量赋值的属性
$fillable
Attributes
If a model has any custom attributes(自定义属性), they are held as traits in an Attributes folder of the current model directory.
This is also where the system builds the action buttons for each object type, if applicable. See Action Buttons.
Relationships (模型间关系,一对多,多对多等等)
If a model has any relationships, they are held as traits in an Relationships folder of the current model directory.
Scopes
If a model has any scopes, they are held as traits in an Scopes folder of the current model directory.
Notifications
Laravel Boilerplate ships with a few Models that utilize the Notifiable trait, and in turn (反过来) there are a few notifications that get sent.
- UserNeedsConfirmation - Send when a user registers and email confirmation is on.
- UserNeedsPasswordReset- Send when the user requests a password reset.
All notifications are stored in the app\Notifications directory by default. app\Notifications 目录存放所有的 notification。
All notifications are self-explanatory(自解释), for more information view the Laravel Documentation.
Providers
Service Providers are classes that sort of "bootstrap" your application. Laravel ships with many, there are some new ones, and some default ones have been modified. (Laravel 提供了一些 Provider,本项目对有些进行了一些修改。)
- AppServiceProvider - Modified (修改)
- AuthServiceProvider - Untouched(未受影响)
- BladeServiceProvider - New(新增)
- BroadcastServiceProvider - Untouched
- ComposerServiceProvider - New
- EventServiceProvider - Modified
- RouteServiceProvider - Modified
AppServiceProvider
The following modifications have been made to the default AppServiceProvider:
- The boot method does the same checks as the LocalizationMiddleware class.(修改 boot 增加一些检测)
- There is a conditional to check to see if the app is in production if you need to do anything specific such as force https.
- The register method has a conditional for local development where you can register service providers that you only need for local development, since there is no point in registering them in a production environment.(提供服务注册用于开发环境。)
BladeServiceProvider (新增)
The BladeServiceProvider registers any custom blade directives you want to be able to use in your blade files. BladeServiceProvider 注册了一些常用的 blade 指令用于模板渲染。
Laravel Boilerplate ships with 1 non-access related blade extensions: Laravel Boilerplate 提供了 一个 blade 相关的异常:
- A @langrtl directive(指令), which checks to see if the current language in the session wants Right-to-Left support so you can update things accordingly.
ComposerServiceProvider (新增)
The ComposerServiceProvider registers any view composers the application needs.(提供视图合成器的注册服务)
EventServiceProvider (修改)
The EventServiceProvider is extended to use the $subscribers property to register the event listeners for the system.(继承原有功能,新增了使用 $subscribers 来注册事件监听器的功能。)
RouteServiceProvider (修改)
The only addition to the RouteServiceProvider is in the boot method.
Since most of the controller/routes use Laravels Route/Model Binding, there is one instance where we need to specify a binding. 因为大多数的 contorller/routes 是使用 Laravels route/model 绑定,所以有一个实例是需要指定绑定。
We specify this binding for use in deleting/restoring a user, because the binding needs to know to have to check for deleted users as well. If you get rid of this and just use the default user binding, it will fail because it's not checking for a user id that has a non-null deleted_at timestamp. 我们指定这个绑定用于在删除/恢复用户时使用,因为绑定需要知道也必须检查已删除的用户。如果您删除了这个并且只使用默认的用户绑定,它将会失败,因为它没有检查具有非null删除时间戳的用户id。
Repositories
Repositories are a way of extracting database logic into their own classes so you can have slim easy to read controllers.
Repositories are injected into any controller that needs them via the constructor, and are resolved out of the service container.(自动注入到容器中...)
You do not have to use repositories, or repository/contract patterns, but it is good to learn ways to better structure your code instead of having bloated controllers. repositories 模式 可以有效的避免 controller 层过于臃肿。
Key Points
- Repositories extend a base repository class to get included helper methods.(Repositories\BaseRepository.php 是 repositories 的基类,可以去看下具体提供了哪些方法。)
- If you extend the base repository you must have a method called model() that returns the model that you will be working with.(扩展基类,必须要实现一个 **model() 函数用于返回 model )
Access Control (访问控制)
Role/Permission control has been replaced with spatie/laravel-permission in this version of the boilerplate. 这个版本的 Role/Permission 替换成 spatie/laravel-permission。
Laravel Boilerplate ships with other access features(特点) as well:
Features:
-
Register/Login/Logout/Password Reset
-
Third party login (Github/Facebook/Twitter/Google/Linked In/BitBucket)
-
Account Confirmation By E-mail
-
Resend Confirmation E-mail
-
Option for Manual Account Confirmation by Admin
-
Login Throttling(限制)
-
Enable/Disable Registration
-
Force Single Session(单一会话)
-
Clear User Session
-
Administrator Management
- User Index
- Activate/Deactivate Users
- Soft & Permanently(永久) Delete Users
- Resend User Confirmation E-mails
- Change Users Password
- Create/Manage Roles
- Manage Users Roles/Permissions
- "Login As" User
Configuration
/*
* Application captcha specific settings
* 是否开启验证码功能
*/
'captcha' => [
/*
* Whether the registration captcha is on or off
*/
'registration' => env('REGISTRATION_CAPTCHA_STATUS', false),
],
/*
* Whether or not registration is enabled
* 是否开放注册开关
*/
'registration' => env('ENABLE_REGISTRATION', true),
/*
* Table names for access tables
*/
'table_names' => [
'users' => 'users',
],
/*
* Configurations for the user
*/
'users' => [
/*
* Whether or not the user has to confirm their email when signing up
*/
'confirm_email' => env('CONFIRM_EMAIL', false),
/*
* Whether or not the users email can be changed on the edit profile screen
*/
'change_email' => env('CHANGE_EMAIL', false),
/*
* The name of the super administrator role
*/
'admin_role' => 'administrator',
/*
* The default role all new registered users get added to
*/
'default_role' => 'user',
/*
* Whether or not new users need to be approved by an administrator before logging in
* If this is set to true, then confirm_email is not in effect (生效)
* 用户注册审核开关,一旦开启则 确认邮件不会立即生效
*/
'requires_approval' => env('REQUIRES_APPROVAL', false),
/*
* Login username to be used by the controller.
*/
'username' => 'email',
/*
* Session Database Driver Only
* When active, a user can only have one session active at a time
* That is all other sessions for that user will be deleted when they log in
* (They can only be logged into one place at a time, all others will be logged out)
*/
'single_login' => true,
/*
* How many days before users have to change their passwords
* false is off
*/
'password_expires_days' => env('PASSWORD_EXPIRES_DAYS', 30),
],
/*
* Configuration for roles
*/
'roles' => [
/*
* Whether a role must contain a permission or can be used standalone as a label
*/
'role_must_contain_permission' => true,
],
/*
* Socialite session variable name
* Contains the name of the currently logged in provider in the users session
* Makes it so social logins can not change passwords, etc.
*/
'socialite_session_name' => 'socialite_provider',
Middleware
Both permission middleware from the spatie/laravel-permission package are included in the project. Both of which throw an UnauthorizedException which is caught in the Exceptions/Handler.php file.
Blade Extensions
See BladeServiceProvider.
Localization(地方化)
Laravel Boilerplate comes in many languages, each language has it's own folder as usual, the language files are very well organized and indented.
If you would like to contribute a language, please make a pull request based on the requirements.
The parts of the localization system are:
- The AppServiceProvider boot method
- The LocaleMiddleware
- The language files
- The language dropdowns
The language files try to be as organized as possible first by file name, then by keys frontend, backend, or global. Then by the 'type' they may be display, i.e. 'auth'. Please try to keep them organized if you are contributing to them on GitHub.
Helpers
Helper Classes
There are a few misc. helper classes we have written that we couldn't find a good place for, and didn't want messy controllers with so we extracted them out as Helper classes.
They are located in the app\Helpers directory.
Any helpers that were deemed useful globally throughtout the application have folders in the root, and helpers that were to specific to a section have Frontend or Backend folders like many other places throughout the file structure. 任何在全局上被认为是有用的帮助程序都有在根目录下的文件夹,而特定于某一节的帮助器具有前端或后端文件夹,就像在整个文件结构中的许多其他位置一样。
Helper Globals(全局帮助文件)
There is an app\helpers.php file which is autoloaded with composer that registers a few global functions for your convenience.
app_name()
{{ app_name() }} returns the config app.name value.
gravatar()
Global function for the Gravatar:: facade.
return gravatar()->get($user->email, ['size' => 50]);
include_route_files($folder)
Loops through a folder and requires all PHP files - See Routes.
home_route()
Gets the home route of the user based off of their authentication level.
style()
Include an HTML style call.
script()
Include an HTML javascript call.
form_cancel()
Creates a styled form cancel button that is used throughout.
form_submit()
Creates a styles form submit button that is used throughout.
get_user_timezone()
Gets the logged in users current timezone.
Resources
The resources section has the same high level folder structure as a default installation, with many new items inside:
webpack.mix.js
The webpack.mix.js file that ships with the project is well documented and pretty self-explanatory, it currently looks like: 这个项目的webpack.mix.js文件已经有了很好的文档说明,而且很有说明性,它现在看起来是这样的:
let mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.sass('resources/assets/sass/frontend/app.scss', 'public/css/frontend.css')
.sass('resources/assets/sass/backend/app.scss', 'public/css/backend.css')
.js('resources/assets/js/frontend/app.js', 'public/js/frontend.js')
.js([
'resources/assets/js/backend/before.js',
'resources/assets/js/backend/app.js',
'resources/assets/js/backend/after.js'
], 'public/js/backend.js');
if(mix.inProduction){
mix.version();
}
Assets
JS
- /backend - Contains the backend specific (Core LTE) javascripts to be compiled.
- /components - Contains the Vue components for backend and frontend.
- /frontend - Contains the frontend specific javascripts to be compiled.
- /plugin - Contains any javscript plugins that would be benificial globally.
- bootstrap.js - The Laravel javascript bootstrap file.
- plugins.js - Contains useful plugins and snippets that would be benificial globally.
Sass
- /backend - Contains backend .scss files (Core LTE) ready to be compiled into css.
- /frontend - Contains frontend .scss files ready to be compiled into css.
- /plugin - Contains plugin .scss files associated with the plugin folder in the js directory.
- _helpers.scss - A place to put helper scss methods that can be used globally.
Lang
See Localization.
Views
- /backend - The backend blade files.
- /frontend - The frontend blade files
- /includes - Blade files that are included in all master app layouts.
- /vendor - Files generated from the vendor:publish command.
Testing(测试)
(默认提供了超过100个测试和500个断言用于判断用户是否正确安装配置项目。) The test suite is currently comprised of over 100 tests and 500 assertions to test all of the sections in this documentation.
We will not go into detail here since they change so often, but with a fresh installation everything should pass if you have the right configurations.
To run the tests run phpunit from the command line in the root of the project.
Note: You will need to set up mail for all the tests to pass. I suggest a Mailtrap account for testing.
Misc.
Flash Messages
The messages.blade.php file is included after the body of all master app layouts in this project, and takes care of displaying any errors from a session, validation, etc using bootstrap alert boxes.
If not a validation error, or message bag error, then the following session variables are supported:
- flash_success - Bootstrap alert-success box.
- flash_warning - Bootstrap alert-warning box.
- flash_info - Bootstrap alert-info box.
- flash_danger - Bootstrap alert-danger box.
- flash_message - Bootstrap alert box.
You will see these used in many controller methods, or in exception handling except they use Laravel's magic method syntax.
Magic Methods(魔法方法)
This application makes use of Laravel's magic methods in many places, especially controllers.
For example, you may see a controller return that looks like this:
return redirect()->route('admin.auth.user.index')
->withFlashSuccess(trans('alerts.backend.users.created'));
or:
return view('backend.auth.show')->withUser($user);
In both of these examples you will see a compound function call, that doesn't exist in the documentation.
- withFlashSuccess
- withUser
Laravel will convert both of these methods to a default with() method when it compiles, so both of the above examples will output the following code.
// recommoned
return redirect()->route('admin.auth.user.index')
->with('flash_success', trans('alerts.backend.users.created'));
or:
return view('backend.auth.show')->with('user', $user);
Both methods work, but I think the magic makes it more elegant(优雅) so I use that throughout.
Action Buttons
Anywhere throughout the system that there is a table that allows CRUD operations, with associated buttons, the buttons (called action buttons) are held within a trait on the model.
We use this approach to make it easy to output buttons for each resource without having duplicate markup in multiple places, plus it takes care of the entity ID's and other stuff, such as converting all delete buttons for links that trigger a form instead of a form themselves. (This uses a method included in the global javascript plugin file.)
For example the buttons to the right of the users table in the backend, are generated in this trait.
public function getShowButtonAttribute()
{
return '<a href="' . route('admin.auth.user.show', $this) . '" class="btn btn-xs btn-info"><i class="fa fa-search" data-toggle="tooltip" data-placement="top" title="' . trans('buttons.general.crud.view') . '"></i></a> ';
}
This outputs the show button for each user row in the table with their respective ID and looks like this
.
On interesting button to look at is the delete button:
public function getDeleteButtonAttribute()
{
if ($this->id != auth()->id()) {
return '<a href="' . route('admin.auth.user.destroy', $this) . '"
data-method="delete"
data-trans-button-cancel="' . trans('buttons.general.cancel') . '"
data-trans-button-confirm="' . trans('buttons.general.crud.delete') . '"
data-trans-title="' . trans('strings.backend.general.are_you_sure') . '"
class="btn btn-xs btn-danger"><i class="fa fa-trash" data-toggle="tooltip" data-placement="top" title="' . trans('buttons.general.crud.delete') . '"></i></a> ';
}
return '';
}
Ignoring the if statement for this example, you can see that the delete button is actually a link. But that's insecure, isn't it? Actually no, take note of the data-method="delete" property of the link.
If you look at the source of the delete button you will see this:
<a data-method="delete" data-trans-button-cancel="Cancel" data-trans-button-confirm="Delete" data-trans-title="Are you sure?" class="btn btn-xs btn-danger" style="cursor:pointer;" onclick="$(this).find("form").submit();"><i class="fa fa-trash" data-toggle="tooltip" data-placement="top" title="" data-original-title="Delete"></i>
<form action="http://l5boilerplate.dev/admin/access/user/1" method="POST" name="delete_item" style="display:none">
<input type="hidden" name="_method" value="delete">
<input type="hidden" name="_token" value="YOUR_CSRF_TOKEN">
</form>
</a>
Because of this function, all data-method="delete" property get a form injected inside them that calls a DELETE route, it also takes care of popping up an "are you sure you want to delete?" alert box before resuming.
Note: The other data-* methods on the link take care of injecting the appropriate language into the sweetalert alert box that pops up which looks like this:
Finally each attribute trait has a last method called getActionButtonsAttribute() which concatinates all of the button methods together and displays all of the buttons at once like:
$user->action_buttons
Displays: (Actual look may vary between versions)
Note: The getActionButtonsAttribute() method is another example of Laravel magic.
Socialite(第三方登录)
To configure socialite, add your credentials to your .env file. The redirects must follow the convention http://mysite.com/login/SERVICE. Available services are github, facebook, linkedin, bitbucket, twitter, and google. The links on the login page are generated based on which credentials are provided in the .env file.
BitBucket
You must set permissions of your OAuth consumer to at least Account: Read and Repositories: Read
Also: In order for this option to work, email must be nullable on the users table, as well as the unique email table key removed. Do this at your own risk. There is no other option I know of for now.
GitHub
No known quirks, should work as is.
If you are getting an Access Not Configured error message:
Activate the Google+ API from the Google Developers Console.
For the Given URL is not allowed by the Application error message:
- Go to basic settings for your app
- Select Add Platform
- Select Website
- Put URL in the Site URL
Linked In
r_basicprofile and r_emailaddress must be selected in Default Application Permissions.
The callback URL must be submitted under the OAuth 2.0 section.
For Twitter to grab the user's email address for you application, it must be whitelisted as explained here: https://dev.twitter.com/rest/reference/get/account/verify_credentials
Other
If you are getting a cURL error 60 on localhost, follow these directions.
Troubleshooting
If for any reason something goes wrong, try each of the following:
Delete the composer.lock file
Run the dumpautoload command
$ composer dumpautoload -o
If the above fails to fix, and the command line is referencing errors in compiled.php, do the following:
Delete the storage/framework/compiled.php file
If all of the above don't work please report here.
Deployment
When pushing your app to production, you should make sure you run the following:
yarn prod
Compress all of you assets into a single file specified in webpack.mix.js.
Config Caching php artisan config:cache
Caches all of your configuration files into a single file. (生产环节运行 php artisan config:cache 合并配置文件到一个文件,可以加速访问。)
Route Caching php artisan route:cache
(同上 php artisan route:cache 也可以合并路由文件)
If your application is exclusively using controller based routes, you should take advantage of Laravel's route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. In some cases, your route registration may even be up to 100x faster! 如果你的应用程序只使用基于控制器的路由,你应该利用Laravel的 route cache 。使用 route cache 将极大地减少注册应用程序所有路由所需的时间。在某些情况下,你的 路由注册 甚至可能会快100倍!