博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Laravel学习笔记
阅读量:6607 次
发布时间:2019-06-24

本文共 27342 字,大约阅读时间需要 91 分钟。

hot3.png

j. Laravel笔记

安装

composer create-project --prefer-dist laravel/laravel projectnamechmod -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()
  • HTTP异常
    • 手动抛出异常 abort(404 [,'error msg'])
    • 定义异常页面 resources/views/errors/404.blade.php
  • 浏览器输出错误细节APP_DEBUG=true
  • Monolog日志
    • 配置
      • 日志模式:APP_LOG=singlesingle、daily、syslog、errorlog
      • 日志最小级别APP_LOG_LEVEL=debug
    • 记录 \Log::debug|info|notice|warning|error|critical|alert|emergency('xxx', Array $context)(错误级别降序)

ServiceProvider

  • php artisan make:provider XxxServiceProvider(自动注册于 config/app.php
  • 所有Providerregister完毕后才调用boot
  • 延迟加载Provider
    • protected $defer = true;
    • provides方法返回延迟加载服务的类名

Service Container

绑定

* 仅当需要修改容器中绑定的对象时才进行手工绑定 *$this->app->bind(类名, 闭包); # 简单绑定$this->app->bind(接口名, 类名); # 接口实现绑定$this->app->singleton(类名, 闭包); # 单例绑定$this->app->instance(类名, 对象); # 实例绑定$this->app->when(类名)->needs(类名)->give(闭包); # 场景绑定$this->app->when(类名)->needs('$变量名')->give(变量值); # 数据绑定

解析

* $this->app->make(类名);* app(类名)* resolve(类名);* 自动依赖注入

容器事件

* 每当服务容器解析一个对象前就会触发一个事件 - 前置回调 *$this->app->resolving(function ($object, $app) {  // 解析任何类型的对象时都会调用该方法...});$this->app->resolving(HelpSpot\API::class, function ($api, $app) {  // 解析「HelpSpot\API」类型的对象时调用...});

Facades

  • 访问 Container内实例 的静态代理(虽然静态,但仍然可测试)
  • 本质和辅助函数没有区别
  • 使用模式
    • 通过绑定的别名 \Cache
    • 直接原生使用 \Illuminate\Support\Facades\Cache
  • 继承 Illuminate\Support\Facades\Facade 类,并实现getFacadeAccessor方法返回容器绑定key

HTTP 路由

路由方法

  • get、post、put、patch、delete、options - 匹配基本请求类型
Route::get(路径, 控制器@方法, ['except'=>[..], 'only'=>[..]])		->where(正则约束)		->middleware('')		->name('')
  • match - 匹配多个请求类型
  • any - 匹配所有请求类型
  • resource - 处理Rest请求
    • 资源路由Actions: index、create、store、show、edit、update、destroy
    • Route::resource(路径, 控制器, ['except'=>[..], 'only'=>[..]])
$url = route('profile', Array $context) # 从命名路由生成urlreturn redirect()->route('profile'); # 重定向到命名路由## 当前路由信息 ##$route = Route::current();$name = Route::currentRouteName();$action = Route::currentRouteAction();## 控制器中调用路由参数 ##$this->route('ParaName')## 表单方法伪造 ##{
{ method_field('PUT') }}# 生成路由缓存(仅对基于控制器实现的路由有效)php artisan route:clearphp artisan route:cache

HTTP中间件

  • 中间件用于过滤进入应用程序的 HTTP 请求
  • 中间件注册
    • Laravel app/Http/Kernel.php
    • lumen 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保护

  • input标签POST参数类型{
    { csrf_field() }}
    自动触发 VerifyCsrfToken 中间件
  • meta标签X-CSRF-TOKEN头部类型
    1. <meta name="csrf-token" content="{
      { csrf_token() }}">
    2. $.ajaxSetup({headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}});

控制器

  • 常规控制器
  • 单一行为控制器 - 实现public function __invoke方法(路由不指定Action
  • Restful资源控制器 php artisan make:controller XxxController --resource
  • 请求数据
    • $request->get|input(xxx)
    • $request->all()
  • 表单请求模拟 - 隐藏域_method指定HTTP类型 {
    { method_field('PUT') }}

响应

  • 字符串 -> 字符串
  • 数组|集合 -> JSON
  • Json响应 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代码@php xxx @endphp
  • 视图堆栈定义@push(栈名) xxx @endpush,调用@stack(栈名)
  • 服务注入@inject('varName', My::class),调用{
    { $varName }}

布局和区块

  • 继承区块@extends(区块名)
  • 定义区块内容
    • @section(区块名, 'content')
    • @section(区块名) @parent xxx @endsection #@parant指令引入继承区块的内容
  • 显示区块内容@yield(区块名)

组件和插槽

用于视图组件的重用

  1. 定义视图组件,设定插槽注入动态数据
    • 默认插槽{
      { $slot }}
    • 命名插槽{
      { $varname }}
  2. 调用视图组件
component(组件视图名 [, array $data])	@slot('varname')		命名插槽数据	@endslot		默认插槽数据endcomponent

子视图

  • 父视图变量在子视图可用
  • @include(视图名 [, $data])
  • @includeif(视图名 [, $data])
  • 集合渲染@each(视图名, $collection, 'itemName', 集合空的候选视图)

数据输出

  • 常规变量{
    { $var }}
  • 非转义变量{!! $var !!}
  • 函数结果 {
    { func() }}
  • 抑制变量解析@{
    { $var }}
    (最终结果双括号保留,@符剔除)
  • 抑制一段内容里的变量解析@verbatim xxx @endverbatim

控制流

  • 条件语句
    • @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 "
";});

本地化

  • resources/lang/zh/file.php
  • 语言配置
    • 默认配置config/app.php
    • 动态配置App::setLocale($locale)
    • 判断配置App::getLocale(); App::isLocale('en');
  • 读取(不存在则返回键名)
    • 函数式__('[filename.]lanKey' [, array $data])$data可替换翻译中的:xxx占位符)
    • Blade式@lang('[filename.]lanKey')
  • 翻译复数
    • 单复数形式用|隔开
    • 可指定不同范围的复数形式{0}xxx|[1,10]yyy|[11,*]zzz
    • 复数翻译输出trans_choice('[filename.]lanKey', $num)
  • 覆盖拓展包的翻译resources/lang/vendor/{package}/{locale}

前端资源编译 - Mix

前端移除 可选移除前端脚手架 php artisan preset none

Mix运行

npm install #安装package.json中的依赖# 执行构建任务npm run devnpm run production# 监控文件变动自动重新构建npm run watchnpm 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(顺序加载)# `npm run环境`的检测mix.inProduction()

browserSync支持

1. mix.browserSync(域名|{详细配置})  #浏览器:3000 -> browserSyn -> WebServer -> 文件监控2. npm run watch

环境变量

  • .envMIX_打头的变量
  • 使用:process.env.变量名

数据校验

  • 验证失败后
    • 常规请求返回一个redirect至先前位置
    • ajax请求返回json错误信息及422状态码
  • 校验字段可通过点语法来嵌套
  • 所有的验证错误会被自动flashsession
  • 错误信息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 # 回滚所有migrationphp artisan migrate:refresh 【--step=num】 # 先重置后重载migrationSchema::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

EloquentORM

基础查询

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字段被转换成Carbonprotected $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,timestampprotected $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::bootuse 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
    • $users->each->markAsVip();
    • $users->sum->votes;

ORM集合 - Illuminate\Database\Eloquent\Collection

  • 继承于基础集合 Illuminate\Support\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); //不存在时才更新,实际写入时返回trueCache->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())

账户认证

快速搭建

  1. php artisan make:auth生成认证相关的路由、视图、home示例
  2. php artisan migrate数据表准备
  3. config/auth.php配置
    • guards - 账号认证模式(内置 web session、api token两种)
    • providers - 持久层账号读取模式(内置Eloquent、Database两种)
      • 密码字段60+字节
      • 存在可空、100字节的remember_token字段(用于记住我)
  4. 框架已预置User模型
  5. 框架已预置4个Auth控制器
    • RegisterController
    • LoginController
      • 定制认证字段username()(默认email字段)
      • 登录限流use ThrottlesLogins(认证字段+IP限流试登次数/Min)
    • ForgotPasswordController
    • ResetPasswordController

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 CanResetPassworduse CanResetPassword来实现)
  • 创建reset token
  • 忘记密码、重置密码 的 路由/控制器/视图
  • 定制处理
    • ForgotPasswordController|ResetPasswordController::broker()定制Password Broker
    • User::sendPasswordResetNotification()定制重置邮件的通知类

加密解密

  • 基于OpenSSL提供AES加密(并使用MAC消息认证码签名)
  • 如果值不能被正确解密则抛出异常
  • 加解密(加密前准备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) #未通过则返回falseController->authorize(权限名, $model|Model::class) #未通过则抛出AuthorizationException## Blade模板鉴权 ##can|cannot(权限名, $model|Model::class)	xxxelsecan|elsecannot(权限名, $model|Model::class)	xxxendcan|endcannot

Artisan命令

创建命令

  • 新建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(命令描述);
  • 定时任务 - 注册在Kernel::schedule( )

内置服务器

php artisan serve # 启动本地开发服务器localhost:8000php artisan down --message='系统升级中' # 进入维护模式:关闭服务、队列(默认视图resources/views/errors/503.blade.php)php artisan up # 服务重启

Schedule

定义调度任务

  • 定义在App\Console\Kernel::schedule(Schedule $schedule)
  • 系统配置 * * * * * php 项目路径/artisan schedule:run >> /dev/null 2>&1

调度模式

  • 闭包模式
$schedule->call(function(){. . .})->daily();
  • Artisan命令模式
$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()

测试

覆盖面:

  • 所有http请求类型
  • 正常场景:数据 &结构&状态码
  • 异常场景:状态码
  • 筛选参数
  • 数据分页
# 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/publicphp 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. 基于新驱动配置新文件系统连接

国内云存储驱动

  • 七牛云、又拍云、OSS、COS
  • 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
  • 静态资源合并

转载于:https://my.oschina.net/u/2400083/blog/525478

你可能感兴趣的文章
temporary Object and destructor
查看>>
xcode - 移动手势
查看>>
细说浏览器特性检测(1)-jQuery1.4添加部分
查看>>
古中国数学家的计算力真是惊人
查看>>
Java基础-算术运算符(Arithmetic Operators)
查看>>
C#编程(四十七)----------集合接口和类型
查看>>
【转】关于大型网站技术演进的思考(十二)--网站静态化处理—缓存(4)
查看>>
积跬步,聚小流------Bootstrap学习记录(1)
查看>>
HDUPhysical Examination(贪心)
查看>>
HTML5 FileAPI
查看>>
使用tdcss.js轻松制作自己的style guide
查看>>
SecureCRTPortable.exe 如何上传文件
查看>>
C++中public、protected及private用法
查看>>
苹果公司的产品已用完后门与微软垄断,要检查起来,打架!
查看>>
顶级的JavaScript框架、库、工具及其使用
查看>>
AYUI -AYUI风格的 超美 百度网盘8.0
查看>>
简明 Python 教程
查看>>
用MPMoviePlayerController做在线音乐播放
查看>>
【前端笔记】彻底理解变量与函数的声明提升
查看>>
Android 反编译利器,jadx 的高级技巧
查看>>