composer create-project --prefer-dist laravel/laravel projectname chmod -R a+w storage bootstrap/cache # 设定目录权限 php artisan key:generate # 生成加密秘钥 confit/app.php配置timezone=>Asia/Shanghai, locale=>zh * 服务器解析到`public`目录上 *
.env
配置文件、phpunit.xml env
变量设定 会被加载至两处
phpunit.xml env
配置失效)PHP $_ENV
变量env()
读取的是系统环境变量.env
文件在Docker
容器启动时被加载到容器的系统环境变量App::environment()
App\Exceptions\Handler
处理
report()
(默认是记录日志)render()
abort(404 [,'error msg'])
resources/views/errors/404.blade.php
APP_DEBUG=true
Monolog
日志
APP_LOG=single
(single、daily、syslog、errorlog
)APP_LOG_LEVEL=debug
\Log::debug|info|notice|warning|error|critical|alert|emergency('xxx', Array $context)
(错误级别降序)php artisan make:provider XxxServiceProvider
(自动注册于 config/app.php
)Provider
的register
完毕后才调用boot
Provider
protected $defer = true;
provides
方法返回延迟加载服务的类名绑定javascript
* 仅当须要修改容器中绑定的对象时才进行手工绑定 * $this->app->bind(类名, 闭包); # 简单绑定 $this->app->bind(接口名, 类名); # 接口实现绑定 $this->app->singleton(类名, 闭包); # 单例绑定 $this->app->instance(类名, 对象); # 实例绑定 $this->app->when(类名)->needs(类名)->give(闭包); # 场景绑定 $this->app->when(类名)->needs('$变量名')->give(变量值); # 数据绑定
解析php
* $this->app->make(类名); * app(类名) * resolve(类名); * 自动依赖注入
容器事件css
* 每当服务容器解析一个对象前就会触发一个事件 - 前置回调 * $this->app->resolving(function ($object, $app) { // 解析任何类型的对象时都会调用该方法... }); $this->app->resolving(HelpSpot\API::class, function ($api, $app) { // 解析「HelpSpot\API」类型的对象时调用... });
\Cache
\Illuminate\Support\Facades\Cache
Illuminate\Support\Facades\Facade
类,并实现getFacadeAccessor
方法返回容器绑定key路由方法html
get、post、put、patch、delete、options
- 匹配基本请求类型Route::get(路径, 控制器@方法, ['except'=>[..], 'only'=>[..]]) ->where(正则约束) ->middleware('') ->name('')
match
- 匹配多个请求类型any
- 匹配全部请求类型resource
- 处理Rest请求
index、create、store、show、edit、update、destroy
Route::resource(路径, 控制器, ['except'=>[..], 'only'=>[..]])
$url = route('profile', Array $context) # 从命名路由生成url return redirect()->route('profile'); # 重定向到命名路由 ## 当前路由信息 ## $route = Route::current(); $name = Route::currentRouteName(); $action = Route::currentRouteAction(); ## 控制器中调用路由参数 ## $this->route('ParaName') ## 表单方法伪造 ## {{ method_field('PUT') }} # 生成路由缓存(仅对基于控制器实现的路由有效) php artisan route:clear php artisan route:cache
app/Http/Kernel.php
bootstrap/app.php
BeforeMiddleware
BeforeMiddleware
$router->middleware('xxx', ...)
(别名或全名)Route::group(['middleware' => ['xxx', ...]], Closure);
$this->middleware('xxx')->only()->except()
terminate($request, $response)
方法(响应被发送到浏览器以后才运行)$middlewareGroups
属性->middleware('中间件名:参数1,参数2,...');
{{ csrf_field() }}
自动触发 VerifyCsrfToken
中间件<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}});
public function __invoke
方法(路由不指定Action
)php artisan make:controller XxxController --resource
$request->get|input(xxx)
$request->all()
_method
指定HTTP类型 {{ method_field('PUT') }}
return response()->json(array $data)[->withCallback($jsonpCallbackName)]
return response()->download($pathToFile [, $name, $headers])->deleteFileAfterSend(true) #文件名必须ascii
return response()->file($pathToFile [, $headers])
# 嵌套视图路径用点隔开 - 常规视图:return view($viewName, $data)[->with($key, $value)](或者View::make()) - 定制头部:return response()->view($viewName, $data, $statuCode)->header($field, $value) View::exists($viewName) #检查视图是否存在 View::share($key, $value) #设定所有视图共享的数据,AppServiceProvider::boot()中 ## 视图编排ViewComposers(在视图输出前作修饰)## # 定义composer类 class MyComposer { public function compose(View $view) { $view->.... } } # 视图绑定composer View::composer($viewName|$viewNames|*, $composer::Class|function($view){}) ## ViewCreator(视图一建立就执行修饰) ## View::creator($viewName|$viewNames|*, $creator::Class|function($view){})
$response = response($content, $statusCode) $response->header($field, $value)->withHeaders(array $headers) $response->cookies($name, $value, $minutes [, $path, $domain, $secure, $httpOnly]) #中间件EncryptCookies::$except中配置不加密签名的cookie项
- 常规跳转:return redirect($url) - 带session闪存跳转:return redirect($url)->with($name, $value) - 跳至命名路由:return redirect()->route($routeName [, array $routeParams]) - 回到上一页:return back()->withInput(); #基于session实现的 - 跳至控制器方法:return redirect()->action('Controller@action' [, array $params])
细节前端
blade
注释不会html
输出{{-- 注释内容 --}}
@php xxx @endphp
@push(栈名) xxx @endpush
,调用@stack(栈名)
@inject('varName', My::class)
,调用{{ $varName }}
布局和区块vue
@extends(区块名)
@section(区块名, 'content')
@section(区块名) @parent xxx @endsection
#@parant
指令引入继承区块的内容@yield(区块名)
组件和插槽
用于视图组件的重用java
{{ $slot }}
{{ $varname }}
component(组件视图名 [, array $data]) @slot('varname') 命名插槽数据 @endslot 默认插槽数据 endcomponent
子视图webpack
@include(视图名 [, $data])
@includeif(视图名 [, $data])
@each(视图名, $collection, 'itemName', 集合空的候选视图)
数据输出laravel
{{ $var }}
{!! $var !!}
{{ func() }}
@{{ $var }}
(最终结果双括号保留,@符剔除)@verbatim xxx @endverbatim
控制流web
@if (bool) xxx @elseif (bool) xxx @else xxx @endif
@unless (bool) xxx @endunless
@isset(bool) xxx @endisset
@empty(bool) xxx @endempty
@for ($i = 0; $i < 10; $i++) xxx @endfor
@foreach () xxx @endforeach
@forelse () xxx @empty yyy @endforelse
@while (bool) xxx @endwhile
@continue
@continue(bool)
@break
@break(bool)
*循环变量$loop->index
$loop->first
$loop->last
$loop->count
$loop->parent
自定义指令
php artisan view:clear
Blade::directive('directName', function ($expression) { return "<?php echo xxx; ?>"; });
resources/lang/zh/file.php
config/app.php
App::setLocale($locale)
App::getLocale(); App::isLocale('en');
__('[filename.]lanKey' [, array $data])
($data
可替换翻译中的:xxx
占位符)@lang('[filename.]lanKey')
|
隔开{0}xxx|[1,10]yyy|[11,*]zzz
trans_choice('[filename.]lanKey', $num)
resources/lang/vendor/{package}/{locale}
前端移除 可选移除前端脚手架 php artisan preset none
Mix运行
npm install #安装package.json中的依赖 # 执行构建任务 npm run dev npm run production # 监控文件变更自动从新构建 npm run watch npm run watch-poll #自动监控无效可用这个命令
Mix构建定义 - 基于webpack
定义构建任务(webpack.mix.js
)
# 构建基础 mix.webpackConfig({}) #部分调整webpack配置(亦可整个重置webpack.config.js) .less|sass|js($from, $to, {$settings}) .options({processCssUrls:false}) .extract(['vue', ...]) #将指定依赖库导出到vendor.js文件 .version() #version后变名资源加载可经过 `mix(资源路径)` .disableNotifications() #关闭编译通知 .sourceMaps() # css|js文件合并 mix.styles|scripts([$fromPaths], $targetPath) # 文件|目录拷贝 mix.copy|copyDirectory($from, $to) # 使用编译的js(顺序加载) <script src="/js/manifest.js"></script> <script src="/js/vendor.js"></script> <script src="/js/app.js"></script> # `npm run环境`的检测 mix.inProduction()
browserSync支持
1. mix.browserSync(域名|{详细配置}) #浏览器:3000 -> browserSyn -> WebServer -> 文件监控 2. npm run watch
环境变量
.env
中MIX_
打头的变量process.env.变量名
redirect
至先前位置ajax
请求返回json错误信息
及422状态码flash
至 session
MessageBag $errors
自动被 ShareErrorsFromSession
中间件绑定到视图bail
先决规则(任意规则校验失败后,该字段后续规则再也不进行)sometimes
规则(有则校验)校验方式
$this->validate($request, array $rules)
$validator = Validator::make($request->all(), array $rules); $validator->validate(); # 校验失败将自动跳转 $validator->fails(); $validator->after(function($validator){}); #验证后钩子
Request
php artisan make:request MyRequest
- 路径app/Http/Requests
MyRequet
实现rules()+messages()、authorize()
逻辑MyRequest
后会自动在方法执行前进行验证错误消息
/** $return Illuminate\Support\MessageBag */ $errors = $validator->errors(); $errors->has('FieldName'); # 检查指定字段时候出错 $errors->first('FieldName'); # 指定字段第一个错误提示 $errors->get('FieldName'); # 指定字段全部错误提示 $errors->all(); # 所有错误
底层
# 读写分离配置 database链接配置下新增read、write键并配置相应db集群 DB::connection(链接名)->select(...); # 数据库切换 DB::connection()->getPdo(); # 得到底层pdo实例 ## 数据库事务 ## - 自动模式 DB::transaction(Closure); - 手动模式 DB::beginTransaction(); DB::rollBack(); DB::commit(); ## 数据库锁 ## $builder->sharedLock(); # 共享锁(锁住写入) $builder->lockForUpdate(); # 悲观所(锁住读写)
QueryBuilder
* 查询结果`Illuminate\Support\Collection`集合中的每一个实例都是`StdClass`类型 * DB::get|first|select|insert|update|delete|statement($sql, array $params); # 原生sql查询 $builder = DB::table('tableName'); # 查询构造器 $builder->insert($row | $rows); # 批量插入 $builder->chunk($perPage, function($records){...}); # 查询结果分块,闭包若返回false则将中止处理后续结果 $builder->distinct(); # 去重 $builder->->increment('field' [, $step]; # 递增 $builder->whereColumn('field1', '=', 'field2'); # 列比较 # 字段值 $builder->value(FieldName); # 获取一行记录的字段值 $builder->pluck(FieldName); # 获取集合的一列字段字段值 $builder->pluck(KeyField, $ValueField); # 获取集合的两列键值字段 # 字段选取 $builder->select('field1', 'xxx as filed2'); $builder->select(DB::raw('field1, xxx as filed2')); # 参数分组(经过where的闭包进行) $builder->where(function($query){ $query->where ... }); # exist查询 $builder->whereExists(function($query){ $query->select('xx')->from('xx')->where('child.parent_id = parent.id')... }); ## 子查询构建 $builder = DB::table(DB::raw("({$childQuery->toSql()}) as tmp"))->->mergeBindings($childQuery->getQuery());
分页
# 系统默认当前页码?page=参数 # 直接返回分页器, 将被框架自动转成JSON # 建立分页 $builder|Model->paginate($perPage) // Illuminate\Pagination\LengthAwarePaginator 带完整分页信息 $builder|Model->simplePaginate($perPage) // Illuminate\Pagination\Paginator 不查询分页状况(仅知道先后页的简单分页) # 内部集合数据 $paginator->getCollection() $paginator->setCollection($collection) # 分页助手方法 ->links(【'view.自定义分页模板'】) # 分页连接(php artisan vendor:publish --tag=laravel-pagination) ->setPath($uri) # 设定基础uri ->appends(Array) # 分页连接加参数
Migration
php artisan make:migration create_xxxs_table --create|table=xxxs # migration建表 php artisan migrate:rollback 【--step=num】 # 回滚上次数据库变更涉及的migration操做 php artisan migrate:reset # 回滚全部migration php artisan migrate:refresh 【--step=num】 # 先重置后重载migration Schema::hasTable($tableName) # 结构检查 Schema::hasColumn($colName1, ...) Schema::connection('foo') # 切换连接 Schema::rename($from, $to) # 重命名 Schema::drop|dropIfExists($tableName) # 删除 # 表定义 $table->increments('id'); $table->字段类型(字段名)->unsigned()->nullable()->default(默认值)->after(列名)->comment(注释); #字段定义 $table->timestamps(); #created_at & updated_at $table->softDeletes(); #软删除deleted_at $table->索引类型(字段名 [, 索引名称]); #索引定义(复合索引则传入字段数组) # 杂项表处理 $table->engine = 'InnoDB'; $table->....->change(); #更新已有字段 $table->renameColumn('from', 'to'); #重命名字段 $table->dropColumn($colName); #删除字段 $table->dropIndex($index); #删除索引 # !注意事项! # SQLite 在单个Schema下只支持处理一个Column
基础查询
php artisan make:model Models/ModelName -m # 查 $builder = Model::query() | $model->newQuery() $builder->get() $builder->first|firstOrFail() $builder->chunk($num, function($rows{ }); Model::all() #表中全部记录 Model::find|findOrFail($id | $ids) # 游标查询 foreach($builder->cursor() as $item) # 一次查一条,节约内存 # 增 $model=new MyModel;$model->save(); Model::create($data); Model::firstOrCreate($data); Model::firstOrNew($data); # 删 $builder->delete(); # 批量删除 $model->delete(); Model::destroy($ids); # 改 $model->save(); $builder->update($data); # 批量更新
模型定制属性
protected $connection = 'connectionName'; # 重定向链接名 protected $table = 'tableName'; # 重定向表名(默认使用模型的SnakeCase复数形式为表名) protected $primaryKey = 'fieldName'; # 重定向主键名(默认为整型id) public $incrementing = false; # 声明主键为非递增、非数字 # 默认的created_at、updated_at字段被转换成Carbon protected $dates = [created_at', 'updated_at', 'deleted_at']; # 指定哪些字段被自动Carbon转换 protected $dateFormat = 'Y-m-d H:i:s'; # 设定日期字段存储或序列化的格式 const CREATED_AT = 'createtimeName'; # 重指定默认建立时间字段 const UPDATED_AT = 'updatetimeName'; # 重指定默认ge时间字段 public $timestamps = false; # 禁止自动维护时间字段(默认的单个模型save()方法调用时自动更新两个时间字段) # 调用create()批量赋值前配置下面属性之一(Mass-Assignment批量赋值保护) protected $guarded = []; # 黑名单,空数组则全部属性可被批量赋值 protected $fillable = []; # 白名单 # belongsTo、belongsToMany关系 更新父级时间戳 protected $touches = [关联名]; # 属性类型转换,支持integer,real,float,double,string,boolean,object,array,collection,date,datetime,timestamp protected $casts = [字段 => 目标数据类型]; # 特点使用:json字段array类型转换为数组 # 字段显示与隐藏(影响toArray、toJson方法) protected $hidden = ['password']; protected $visible = ['id', 'name']; # 临时修改字段可见性 $model->makeVisible/makeHidden('field')->toArray(); # array/json输出追加访问器字段 protected $appends = ['is_admin']; public function getIsAdminAttribute() { return (bool)this->attributes['admin']; } # 动态重定向链接名 public function getConnectionName() { return app()->environment('testing') ? 'DatabaseName' : config('database.default'); } # 字段值修饰器 - 区别于`eloquence`中的`Mappable, Mutable` - accessor —— 修饰器方法取名为 “get字段驼峰式Attribute($value)” - mutator —— 修饰器方法取名为 “get字段驼峰式Attribute($value)”
软删除
# migration建立软删除字段 $table->softDeletes(); # 模型声明软删除(deleted_at非空时认定为记录已被删除) use SoftDeletes; protected $dates = ['deleted_at']; $model->trashed(); # 判断是否被软删除 $model->forceDelete(); # 强制彻底删除 # 恢复被软删除的数据 $model->restore(); # 单个恢复 $builder->restore(); # 批量恢复 # 默认软删除数据不在查询结果中 Model::withTrashed() # 声明包含软删除数据 Model::onlyTrashed() # 声明只查询软删除数据
查询做用域(增长查询约束条件)
## 全局做用域 ## class CustomScope implenments Scope { public function apply(Builder $builder, Model $model) { return $builder->where... } } class CustomModel extends Model { protected static function boot() { parent::boot(); # 模型绑定全局做用域类 static::addGlobalScope(new AgeScope); # 模型绑定全局域闭包 static::addGlobalScope('age', function(Builder $builder) { $builder->where... }); } } # 临时解除全局做用域 Model::withoutGlobalScope(...) ## 本地做用域 ## class CustomModel extends Model { public function scopeXxx($query【, $params】) { return $query->where... } } # 本地做用域(临时做用) $builder->Xxx([$params])... CustomModel::Xxx([$params])
打印SQL
/* @var \Illuminate\Database\Eloquent\Builder $query */ $query->toSql(); \DB::enableQueryLog(); # SQL查询 dd(\DB::getQueryLog());
模型事件
# 模型生命周期事件 creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored # 监听模型事件 AppServiceProvider->boot() { # 监听sql查询事件 \DB::listen(function ($query) { dump($query->sql, $query->bindings); }); # 注册模型单一事件监听器 XxxModel::creating(function ($xxxModel) { # 返回false将取消 save / update 操做 return boolean; }); # 用observer管理模型一组事件监听器 XxxModel::observe(XxxObserver::class); } class XxxObserver { public function created(XxxModel $Xxx){ ... } }
关联定义
多对多关联是个绑定关系 & 其余关联是个衍生关系 ## 正向关系 ## - 子模型外键参考为“snake父模型名_主键” - 父模型本地键参考为“主键” $this->hasOne(子模型, 外键, 本地键) #一对一 $this->hasMany(子模型, 外键, 本地键) #一对多 ## 反向关系 ## - 子模型外键参考为“snake关联方法名_主键” - 父模型其余键参考为“主键” $this->belongsTo(父模型, 外键, 其余键) #一对一 $this->belongsTo(父模型, 外键, 其余键) #多对一 ## 多对多 ## - 中间表名参考为“字母顺序排列组合的下划线表名” - 中间表上的外键: * 当前模型外键参考为“snake当前模型名_主键” * 关联模型外键参考为“snake关联模型名_主键” $this->belongsToMany(关联模型, 中间表, 当前模型外键, 关联模型外键) ->using(中间表模型) #可选,自定义中间表模型(继承Illuminate\Database\Eloquent\Relations\Pivot) ->wherePivot|wherePivotIn() #可选,过滤中间表字段 ->withPivot($field1, ...) #可选,声明中间表包含的外键之外的字段 ## 远程一对多 ## 一对多两级放大 - 中间模型外键参考为“snake模型名_主键” - 远程模型外键参考为“snake模型名_主键” - 当前模型本地键参考为“snake模型名_主键” $this->hasManyThrough(远程模型, 中间模型, 中建模型外键, 远程模型外键, 本地键) ## 一对多morph多态 ## 多个一衍生针对同组多 - 可选关联名参考为“关联方法名”,必选关联名建议为“子模型名able” - type字段名参考为“关联名_type” - id字段名参考为“关联名_id” - 本地键参考为“主键” 正向s: $this->morphMany(子模型, 必选关联名, [type字段名, id字段名, 本地键]) 反向able:$this->morphTo([可选关联名, type字段名, id字段名]) - 默认“type字段值”参考为“完整命名空间指定模型名” - 自定义type字段值需注册多态映射到AppServiceProvider::boot use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([type => 模型]); ## 多对多morph多态 ## 多个一跨中间表绑定共享同一组多 多对多关联基础上,其中一端多进行多态化 正向s: $this->morphToMany(父模型, 必选关联名) 多个反向s: $this->morphedByMany(子模型, 必选关联名)
查询关联
################ 查询关联数据 - 懒加载 ################ 直接经过动态属性访问关联是“懒加载”,在访问关联数据属性时才加载关联 查询: - $model->$relation #直接动态属性访问关联 - $model->$relation->pivot #pivot属性访问中间表(多对多关联场景,且默认只能访问到外键字段) ################ 查询关联数据 - 预加载 ################ 使用with方法“EagerLoad预加载”,在查询父数据时即加载关联(其`WhereIn`机制解决了`N+1`问题,减小总查询至2次) 声明: - 模型::with($relation1, ...) #加载多个关联 - 模型::with($relation1.$relation1_a, ...) #加载嵌套关联 - 模型::with([$relation => function($query){ $query->关联数据约束、排序等 }])->... #约束关联数据 - $collection|$model->load($relation1, ... | [$relation1 => function])#延迟预加载(手动预加载) 查询: - $model->$relation #with声明后动态属性访问关联 ################ 查询关联计数 ################ 只统计不加载数据 声明: - 单个关联计数 模型::withCount(关联名)->... - 别名关联计数 模型::withCount('关联名 AS 别名')->... - 多个关联计数 模型::withCount([关联1, 关联2])->... - 约束关联计数 模型::withCount([关联 => function($query){ $query->where(关联数据约束) }])->... 查询: - $model->$relation_count #动态属性访问计数 ######## 关联过滤(过滤条件施加到关联数据上) ######## ## 查询结果 - 关联数据 ## - 模型::with([$relation => function($query){ $query->关联数据约束、排序等 }])->... #with声明时关联过滤 - $model->$relation()->where #关联结果进一步过滤 ## 查询结果 - 主对象 ## #1. 关联数据存在性 约束 - 模型::has(关联名 [,比较符, 数量])->... #关联名中可进一步使用点语法来声明关联数据下属数据的存在性 - 模型::whereHas|orWhereHas(关联名, function($query){ $query->where(关联数据约束) })->... #2. 关联数据不存在性 约束 - 模型::doesntHave(关联名)->... - 模型::whereDoesntHave(关联名, function($query){ $query->where(关联数据约束) })->...
插入/更新 关联数据
## 正向关系 ### $parent->relations()->save($child) $parent->relations()->saveMany(array $childs) $parent->relations()->create(array $child1_data) ## 反向关系 ## $child->relation()->associate($parent) && $child->save() $child->relation()->dissociate($parent) && $child->save() ## 多对多关联 ## --经过id处理-- - $modelA->relations()->attach($modelB_ID | $modelB_IDS [, array 中间表更新]) #中间表追加绑定关系 - $modelA->relations()->detach($modelB_ID | $modelB_IDS) #中间表解除绑定关系 - $modelA->relations()->detach() #中间表解除A的全部绑定 - $modelA->relations()->sync($modelB_IDS) #中间表重置绑定关系 - $modelA->relations()->syncWithoutDetaching($modelB_IDS) #中间表重置绑定关系(可是不解除已有绑定) - $modelA->relations()->toggle($modelB_IDS) #中间表切换绑定关系(已绑的解绑, 未绑的绑定) --经过对象处理-- - 使用正向关系里的全部方法 - $modelA->relations()->save($modelB [, array 中间表更新]) #中间表追加绑定关系 - $modelA->relations()->updateExistingPivot($modelB_ID , array 中间表更新) #更新中间表某条绑定关系的数据
普通集合 - Illuminate\Support\Collection
类
$collection|$model->toArray()
$collection|$model->toJson()
collect($array)
contains,each,every,filter,first,map,partition,reject,sortBy,sortByDesc,sum
ORM集合 - Illuminate\Database\Eloquent\Collection
Cache::store($storeType)->...;# 切换缓存的store驱动 Cache->get($key【, $default】); Cache->put($key, $value, $minutes|$expiresAt); Cache::forget($key); //删除缓存 Cache::flush(); //清空全部缓存 Cache->add($key, $value, $minutes|$expiresAt); //不存在时才更新,实际写入时返回true Cache->forever($key【, $default】); //永久缓存 Cache::has($key); Cache::increment|decrement($key【, $step】); Cache::remember($key, $minutes, Closure); //获取值,不存在则闭包更新 Cache::pull($key); // 一次性获取而后删除 # 缓存标签 Cache::tags(array $tags)->put($key, $value, $minutes); Cache::tags(array $tags)->get($key); Cache::tags(array $tags)->flush(); # EventServiceProvider中可注册监听缓存事件
绑定
EventServiceProvider->listen
中注册绑定* 根据绑定配置自动建立 `php artisan event:generate` * protected $listen = [ 事件类 => [ 监听器类1, .... ], ];
EventServiceProvider::boot()
中注册public function boot() { parent::boot(); # 单一事件监听 Event::listen('event.事件名', function($data){}); # 全局事件监听 Event::listen('event.*', function($eventName, array $eventData){}); }
单事件监听器
handle
中返回false
将会ShouldQueue
接口public $connection|$queue
定制 链接&队列public function failed(OrderShipped $event, $exception)
中处理FailedJob
多事件订阅者
EventServiceProvider $subscribe
中注册
namespace App\Listeners; class MyEventSubscriber { public function onMyAction($event){...} # 处理订阅逻辑 public function subscribe($events) { $events->listen(事件类, 'MyEventSubscriber@onMyAction'); } }
派发事件 event(new MyEvent())
快速搭建
php artisan make:auth
生成认证相关的路由、视图、home示例php artisan migrate
数据表准备config/auth.php
配置
web session、api token
两种)Eloquent、Database
两种)
remember_token
字段(用于记住我)username()
(默认email
字段)use ThrottlesLogins
(认证字段+IP限流试登次数/Min)Auth控制器定制
$redirectTo | redirectTo()
(默认/home
)Guard
认证模式guard()
登陆检查
Auth::check()
auth
路由中间件检查
guard
:->middleware('auth')
guard
:->middleware('auth:api')
获取用户
$request->user(); Auth::user(); Auth::id();
自主认证
$credentials = ['email' => $email, 'password' => $password, ...] #凭据 Auth::[guard('web')]->attempt($credentials [, $boolRememberMe]) #登陆(返回bool,登陆成功则启动认证session) Auth::logout() #登出(清除session登陆信息) Auth::once($credentials) #仅认证一次当前请求(no session) return redirect()->intended(备用地址) #跳至被认证中间件截断的原先意向页面 Auth::viaRemember() #检查用户是否经过`RememberMe cookie`登陆(返回bool)
模拟身份
# 登入为指定用户 Auth::[guard('web')]->login($user [, $boolRemember]) Auth::[guard('web')]->loginUsingId($userId [, $boolRememberMe])
自定义Guard
# 定义 Illuminate\Contracts\Auth\Guard # AuthServiceProvider::boot()下注册 Auth::extend('my_guard_driver', function($app, $name, array $config){ return new MyGuard(Auth::createUserProvider($config['provider'])); }); # config('auth.guards')下配置 'guards' => [ 'my_guard' => [ 'driver' => 'my_guard_driver', 'provider' => 'users', ], ],
自定义Providor
# 定义 Illuminate\Contracts\Auth\UserProvider # AuthServiceProvider::boot()下注册 Auth::provider('my_provider_dirver', function($app, array $config) { return new MyProvider($app->make('riak.connection')); }); # config('auth.guards')下配置 'providers' => [ 'my_provider' => [ 'driver' => 'my_provider_dirver', ], ],
认证事件
# Illuminate\Auth\Events空间下事件 - Registered - Attempting - Authenticated - Login - Failed - Logout - Lockout
密码重置
内建帐户系统的重置机制
User
模型配置
use Notifiable
implement CanResetPassword
(use CanResetPassword
来实现)reset token
表ForgotPasswordController|ResetPasswordController::broker()
定制Password Broker
User::sendPasswordResetNotification()
定制重置邮件的通知类加密解密
APP_KEY
)
encrypt|decrypt()
(支持字符串、对象、数组)\Crypt::encryptString|decryptString()
(支持字符串)HASH
Hash
提供Bcrypt
散列处理(默认用于内建帐户系统的密码存储)\Hash::make($str);
\Hash::check($str, $hashedStr);
Gate鉴权 简易、闭包式、资源无关的 鉴权(定义在AuthServiceProvider::boot())
## 定义 ## Gate::define(权限名, function ($user [, $model]) { return bool }); Gate::define(权限名, '策略类@操做') Gate::resource(资源名, 策略类名) #资源式Gate(默认 资源.view、create、update、delete) ## 鉴权 ## Gate::allows|denies(权限名 [, $model]) #默认当前用户 Gate::forUser($user)->allows|denies(权限名 [, $model]) #明确指定用户
Policy鉴权 鉴权特定资源的几个操做
## 定义 ## php artisan make:policy MyPolicy [--model=My] AuthServiceProvider::$policies #注册以关联 策略&资源 MyPolicy::before($user, $ability) #策略过滤以预鉴权(返回null才进入policy鉴权) ## 鉴权 ## $user->can|cant(权限名, $model|Model::class) #未经过则返回false Controller->authorize(权限名, $model|Model::class) #未经过则抛出AuthorizationException ## Blade模板鉴权 ## can|cannot(权限名, $model|Model::class) xxx elsecan|elsecannot(权限名, $model|Model::class) xxx endcan|endcannot
建立命令
php artisan make:command 命令名
(默认目录app/Console/Commands
){name=default}
{name?}
{name*}
{--opt}
(选项简写{--O|opt}
){--opt=default}
{--opt*}
: description
null
)
$this->argument('name')
、全部参数$this->arguments()
$this->option('name')
、全部选项$this->options()
交互
$answer = $this->ask('question?');
$answer = $this->secret('question?');
if ($this->confirm('Are you sure?'))
$name = $this->anticipate('Whats your name?', ['Tom', 'Jim']);
$name = $this->choice('Whats your name?', ['Tom', 'Jim'], 'defaultName');
输出
$this->line()
$this->info()
$this->error()
$this->question()
$this->comment()
$this->table([$field1...], [$value1...])
$bar = $this->output->createProgressBar($num); $bar->advance(); $bar->finish();
注册命令
$this->load(__DIR__.'/Commands');
Kernel::$commands
编程调用命令
Artisan::call($command, array $args);
Artisan::queue($command, array $args)->onConnection(链接名)->onQueue(队列名);
$this->call($command, array $args);
$this->callSilent($command, array $args);
其余命令
routes/console.php
Artisan::command('sig:nature {arg}' function($arg){...})->describe(命令描述);
php artisan serve # 启动本地开发服务器localhost:8000 php artisan down --message='系统升级中' # 进入维护模式:关闭服务、队列(默认视图resources/views/errors/503.blade.php) php artisan up # 服务重启
App\Console\Kernel::schedule(Schedule $schedule)
* * * * * php 项目路径/artisan schedule:run >> /dev/null 2>&1
调度模式
$schedule->call(function(){. . .})->daily();
$schedule->command('xxx:yyy --force')->daily(); $schedule->command(XxxCommand::class, ['--force'])->daily();
$schedule->exec('echo HelloWorld')->daily();
调度频率
->cron('* * * * * *') #自定义频率 ->everyMinute() ->hourly() | ->hourlyAt(20) ->daily() | ->dailyAt('13:00') ->weekly() ->monthly() | ->monthlyOn(4, '15:00') ->quarterly() ->yearly()
额外约束
->when(闭包) ->at('13:00') ->between('8:00', '17:00') ->>mondays|weekdays|sundays|...() ->timezone('America/New_York') #设置时区
任务输出
# 只适用于 $schedule->command() 方法 ->appendOutputTo|sendOutputTo|emailOutputTo($filePath)
任务钩子
->before|after(闭包)
Ping
钩子 ->pingBefore|thenPing(闭包)
(依赖Guzzle
)特殊设定
->withoutOverlapping()
->evenInMaintenanceMode()
覆盖面:
# mock数据(视状况可选) $mock = \Mockery::mock(XxxRepository::class); $mock->shouldReceive($methodName)->andReturn($model); $this->app->instance(XxxRepository::class, $mock); #容器针对某类绑定到mock实例 # 发起请求 $this->get/post($api [, array $headers]); # 测试响应 $this->seeStatusCode(200); $this->seeHeader($headName【, $headVal】); $this->seeJsonStructure(array $structure); $this->seeJsonContains(array $structure); $this->seeJsonEquals(array $structure); # 要求完整数据结构 $this->seeInDatabase($tableName, array $data); $this->assertCount($num, array $testData);
config/filesystems.php
中配置文件系统链接及其相应驱动
Public文件系统 - 公共访问磁盘
local
驱动(根路径storage/app
)public/storage->storage/app/public
(php artisan storage:link
)asset('storage/myfile.txt')
FTP文件系统
'ftp' => [ 'driver' => 'ftp', 'host' => 'ftp.example.com', 'username' => 'your-username', 'password' => 'your-password', // 'port' => 21, // 'root' => '', // 'passive' => true, // 'ssl' => true, // 'timeout' => 30, ],
文件系统操做
$storage = Storage::disk(文件系统链接); ->url|get|exists|size|lastModified|getVisibility($path) ->copy|move($fromPath, $toPath) ->delete($filePath|$files) ->put|prepend|append($path, $content|$resource) //大文件建议使用资源句柄 # 自动流式处理,返回完整文件名路径 * ->putFile($saveDir, Illuminate\Http\File|Illuminate\Http\UploadedFile) #自动生成文件名 * ->putFileAs($saveDir, Illuminate\Http\File|Illuminate\Http\UploadedFile,$saveName) #指定文件存储名 # 上传文件处理,返回完整文件名路径 * Illuminate\Http\UploadedFile $request->file($name)->store($saveDir [, 文件系统链接]) #自动生成文件名 * Illuminate\Http\UploadedFile $request->file($name)->storeAs($saveDir, $saveName [, 文件系统链接]) #指定文件存储名 # 目录 ->files|directories($dir) #不含子目录 ->allFiles|allDirectories($dir) #包含子目录 ->makeDirectory|deleteDirectory($dir) #目录增删
增长文件系统驱动
1. composer 安装驱动包 2. 新建ServiceProvidor,boot方法中拓展文件系统驱动 public function boot() { Storage::extend($fileDriverName, function ($app, $config) { $client = new XxxClient(); return new Filesystem(new XxxAdapter($client)); }); }、 3. 基于新驱动配置新文件系统链接
国内云存储驱动
composer require yangyifan/upload:v0.2.1
php artisan cache:clear
(注意把redis
缓存库 和 队列、session
库分离开)php artisan config:cache
php artisan route:cache
classmap
生成php artisan optimize --force
composer dumpautoload --optimize
Redis
存储Session
OpCache