Laravel框架一:原理机制篇

转载自http://www.cnblogs.com/XiongMaoMengNan/p/6644892.html

  Laravel作为在国内国外都颇为流行的PHP框架,风格优雅,其拥有自己的一些特点。

 

. 请求周期

  Laravel 采用了单一入口模式,应用的所有请求入口都是 public/index.php 文件。

  1. 注册类文件自动加载器:Laravel通过composer进行依赖管理,并在bootstrap/autoload.php中注册了Composer Auto Loader (PSR-4),应用中类的命名空间将被映射到类文件实际路径,不再需要开发者手动导入各种类文件,而由自动加载器自行导入。因此,Laravel允许你在应用中定义的类可以自由放置在Composer Auto Loader能自动加载的任何目录下,但大多数时候还是建议放置在app目录下或app的某个子目录下
  2. 创建服务容器:从 bootstrap/app.php 文件中取得 Laravel 应用实例 $app (服务容器)
  3. 创建 HTTP / Console 内核:传入的请求会被发送给 HTTP 内核或者 console 内核进行处理,HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类。它定义了一个 bootstrappers 数组,数组中的类在请求真正执行前进行前置执行,这些引导程序配置了错误处理,日志记录,检测应用程序环境,以及其他在请求被处理前需要完成的工作;HTTP 内核同时定义了一个 HTTP 中间件列表,所有的请求必须在处理前通过这些中间件处理 HTTP session 的读写,判断应用是否在维护模式, 验证 CSRF token 等等
  4. 载入服务提供者至容器:在内核引导启动的过程中最重要的动作之一就是载入服务提供者到你的应用,服务提供者负责引导启动框架的全部各种组件,例如数据库、队列、验证器以及路由组件。因为这些组件引导和配置了框架的各种功能,所以服务提供者是整个 Laravel 启动过程中最为重要的部分,所有的服务提供者都配置在 config/app.php 文件中的 providers 数组中。首先,所有提供者的 register 方法会被调用;一旦所有提供者注册完成,接下来,boot 方法将会被调用
  5. 分发请求:一旦应用完成引导和所有服务提供者都注册完成,Request 将会移交给路由进行分发。路由将分发请求给一个路由或控制器,同时运行路由指定的中间件

     

二. 服务容器和服务提供者

  服务容器是 Laravel 管理类依赖和运行依赖注入的有力工具,在类中可通过 $this->app 来访问容器,在类之外通过 $app 来访问容器;服务提供者是 Laravel 应用程序引导启动的中心,关系到服务提供者自身、事件监听器、路由以及中间件的启动运行。应用程序中注册的路由通过RouteServiceProvider实例来加载;事件监听器在EventServiceProvider类中进行注册;中间件又称路由中间件,在app/Http/Kernel.php类文件中注册,调用时与路由进行绑定。在新创建的应用中,AppServiceProvider 文件中方法实现都是空的,这个提供者是你添加应用专属的引导和服务的最佳位置,当然,对于大型应用你可能希望创建几个服务提供者,每个都具有粒度更精细的引导。服务提供者在 config/app.php 配置文件中的providers数组中进行注册

复制代码
<?phpnamespace App\Providers;use Riak\Connection;
use Illuminate\Support\ServiceProvider;class RiakServiceProvider extends ServiceProvider
{/*** 在容器中注册绑定** @return void*/public function register(){$this->app->singleton(Connection::class, function ($app) {return new Connection(config('riak'));});}
}
复制代码

  

三. 依赖注入

  Laravel 实现依赖注入方式有两种:自动注入和主动注册。自动注入通过参数类型提示由服务容器自动注入实现;主动注册则需开发人员通过绑定机制来实现,即绑定服务提供者或类(参考: http://d.laravel-china.org/docs/5.4/container )。

  1. 绑定服务提供者或类:这种方式对依赖注入的实现可以非常灵活多样
    复制代码
    use Illuminate\Support\Facades\Storage;
    use App\Http\Controllers\PhotoController;
    use App\Http\Controllers\VideoController;
    use Illuminate\Contracts\Filesystem\Filesystem;$this->app->when(PhotoController::class)->needs(Filesystem::class)->give(function () {return Storage::disk('local');});$this->app->when(VideoController::class)->needs(Filesystem::class)->give(function () {return Storage::disk('s3');});
    复制代码
  2. 参数类型声明:通过对类的构造器参数类型、类的方法参数类型、闭包的参数类型给出提示来实现
    复制代码
    <?phpnamespace App\Http\Controllers;use App\Users\Repository as UserRepository;class UserController extends Controller
    {/*** user repository 实例。*/protected $users;/*** 控制器构造方法。** @param  UserRepository  $users* @return void*/public function __construct(UserRepository $users){$this->users = $users;}/*** 储存一个新用户。** @param  Request  $request* @return Response*/public function store(Request $request){$name = $request->input('name');//}
    }
    复制代码
  3. 路由参数依赖:下边的示例使用 Illuminate\Http\Request 类型提示的同时还获取到路由参数id
    复制代码
    你的路由可能是这样定义的:
    Route::put('user/{id}', 'UserController@update');而控制器对路由参数id的依赖却可能是这样实现的:
    <?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class UserController extends Controller
    {/*** 更新指定的用户。** @param  Request  $request* @param  string  $id* @return Response*/public function update(Request $request, $id){//}
    }
    复制代码

     

四. Artisan Console 

  Laravel利用PHP的CLI构建了强大的Console工具artisan,artisan几乎能够创建任何你想要的模板类以及管理配置你的应用,在开发和运维管理中扮演着极其重要的角色,artisan是Laravel开发不可或缺的工具。在Laravel根目录下运行:PHP artisan list可查看所有命令列表。用好artisan能极大地简化开发工作,并减少错误发生的可能;另外,还可以编写自己的命令。下面列举部分比较常用的命令:

  • 启用维护模式:php artisan down --message='Upgrading Database' --retry=60
  • 关闭维护模式:php artisan up
  • 生成路由缓存:php artisan route:cache
  • 清除路由缓存:php artisan route:clear
  • 数据库迁移 Migrations:php artisan make:migration create_users_table --create=users
  • 创建资源控制器:php artisan make:controller PhotoController --resource --model=Photo
  • 创建模型及迁移:php artisan make:model User -m

 

五. 表单验证机制

  表单验证在web开发中是不可或缺的,其重要性也不言而喻,也算是每个web框架的标配部件了。Laravel表单验证拥有标准且庞大的规则集,通过规则调用来完成数据验证,多个规则组合调用须以“|”符号连接,一旦验证失败将自动回退并可自动绑定视图。

  下例中,附加bail规则至title属性,在第一次验证required失败后将立即停止验证;“.”语法符号在Laravel中通常表示嵌套包含关系,这个在其他语言或框架语法中也比较常见

$this->validate($request, ['title' => 'bail|required|unique:posts|max:255','author.name' => 'required','author.description' => 'required',
]);

Laravel验证规则参考 http://d.laravel-china.org/docs/5.4/validation#可用的验证规则 ;另外,在Laravel开发中还可采用如下扩展规则:

  1. 自定义FormRequest (须继承自 Illuminate\Foundation\Http\FormRequest )
  2. Validator::make()手动创建validator实例
  3. 创建validator实例验证后钩子
  4. 按条件增加规则
  5. 数组验证
  6. 自定义验证规则

 

六. 事件机制

  Laravel事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器。事件类 (Event) 类通常保存在 app/Events 目录下,而它们的监听类 (Listener) 类被保存在 app/Listeners 目录下,使用 Artisan 命令来生成事件和监听器时他们会被自动创建。

  1. 注册事件和监听器:EventServiceProvider的 listen 属性数组用于事件(键)到对应的监听器(值)的注册,然后运行 php artisan event:generate将自动生成EventServiceProvider中所注册的事件(类)模板和监听器模板,然后在此基础之上进行修改来实现完整事件和监听器定义;另外,你也可以在 EventServiceProvider 类的 boot 方法中通过注册闭包事件来实现
  2. 定义事件(类):事件(类)就是一个包含与事件相关信息数据的容器,不包含其它逻辑
    复制代码
     1 <?php2 3 namespace App\Events;4 5 use App\Order;6 use Illuminate\Queue\SerializesModels;7 8 class OrderShipped9 {
    10     use SerializesModels;
    11 
    12     public $order;
    13 
    14     /**
    15      * 创建一个事件实例。
    16      *
    17      * @param  Order  $order
    18      * @return void
    19      */
    20     public function __construct(Order $order)
    21     {
    22         $this->order = $order;
    23     }
    24 }
    复制代码
  3. 定义监听器:事件监听器在 handle 方法中接受了事件实例作为参数
    复制代码
     1 <?php2 3 namespace App\Listeners;4 5 use App\Events\OrderShipped;6 7 class SendShipmentNotification8 {9     /**
    10      * 创建事件监听器。
    11      *
    12      * @return void
    13      */
    14     public function __construct()
    15     {
    16         //
    17     }
    18 
    19     /**
    20      * 处理事件
    21      *
    22      * @param  OrderShipped  $event
    23      * @return void
    24      */
    25     public function handle(OrderShipped $event)
    26     {
    27         // 使用 $event->order 来访问 order ...
    28     }
    29 }
    复制代码
  4. 停止事件传播:在监听器的 handle 方法中返回 false 来停止事件传播到其他的监听器
  5. 触发事件:调用 event 辅助函数可触发事件,事件将被分发到它所有已经注册的监听器上
    复制代码
     1 <?php2 3 namespace App\Http\Controllers;4 5 use App\Order;6 use App\Events\OrderShipped;7 use App\Http\Controllers\Controller;8 9 class OrderController extends Controller
    10 {
    11     /**
    12      * 将传递过来的订单发货。
    13      *
    14      * @param  int  $orderId
    15      * @return Response
    16      */
    17     public function ship($orderId)
    18     {
    19         $order = Order::findOrFail($orderId);
    20 
    21         // 订单的发货逻辑...
    22 
    23         event(new OrderShipped($order));
    24     }
    25 }
    复制代码
  6. 队列化事件监听器:如果监听器中需要实现一些耗时的任务,比如发送邮件或者进行 HTTP 请求,那把它放到队列中处理是非常有用的。在使用队列化监听器,须在服务器或者本地环境中配置队列并开启一个队列监听器,还要增加 ShouldQueue 接口到你的监听器类;如果你想要自定义队列的连接和名称,你可以在监听器类中定义 $connection 和 $queue 属性;如果队列监听器任务执行次数超过在工作队列中定义的最大尝试次数,监听器的 failed 方法将会被自动调用
    复制代码
     1 <?php2 3 namespace App\Listeners;4 5 use App\Events\OrderShipped;6 use Illuminate\Contracts\Queue\ShouldQueue;7 8 class SendShipmentNotification implements ShouldQueue9 {
    10     /**
    11      * 队列化任务使用的连接名称。
    12      *
    13      * @var string|null
    14      */
    15     public $connection = 'sqs';
    16 
    17     /**
    18      * 队列化任务使用的队列名称。
    19      *
    20      * @var string|null
    21      */
    22     public $queue = 'listeners';
    23 
    24     public function failed(OrderShipped $event, $exception)
    25     {
    26         //
    27     }       
    28 }
    复制代码
  7. 事件订阅者:事件订阅者允许在单个类中定义多个事件处理器,还应该定义一个 subscribe 方法,这个方法接受一个事件分发器的实例,通过调用事件分发器的 listen 方法来注册事件监听器,然后在 EventServiceProvider 类的 $subscribe 属性中注册订阅者

    复制代码
     1 <?php2 3 namespace App\Listeners;4 5 class UserEventSubscriber6 {7     /**8      * 处理用户登录事件。9      */
    10     public function onUserLogin($event) {}
    11 
    12     /**
    13      * 处理用户注销事件。
    14      */
    15     public function onUserLogout($event) {}
    16 
    17     /**
    18      * 为订阅者注册监听器。
    19      *
    20      * @param  Illuminate\Events\Dispatcher  $events
    21      */
    22     public function subscribe($events)
    23     {
    24         $events->listen(
    25             'Illuminate\Auth\Events\Login',
    26             'App\Listeners\UserEventSubscriber@onUserLogin'
    27         );
    28 
    29         $events->listen(
    30             'Illuminate\Auth\Events\Logout',
    31             'App\Listeners\UserEventSubscriber@onUserLogout'
    32         );
    33     }
    34 
    35 }
    复制代码

 

七. Eloquent 模型

  Eloquent ORM 以ActiveRecord形式来和数据库进行交互,拥有全部的数据表操作定义,单个模型实例对应数据表中的一行

1 $flights = App\Flight::where('active', 1)
2                ->orderBy('name', 'desc')
3                ->take(10)
4                ->get(); 

  config/database.php中包含了模型的相关配置项。Eloquent 模型约定:

  1. 数据表名:模型以单数形式命名(CamelCase),对应的数据表为蛇形复数名(snake_cases),模型的$table属性也可用来指定自定义的数据表名称
  2. 主键:模型默认以id为主键且假定id是一个递增的整数值,也可以通过primaryKey来自定义;如果主键非递增数字值,应设置incrementing = false
  3. 时间戳:模型会默认在你的数据库表有 created_at 和 updated_at 字段,设置timestamps=false可关闭模型自动维护这两个字段;dateFormat 属性用于在模型中设置自己的时间戳格式
  4. 数据库连接:模型默认会使用应用程序中配置的数据库连接,如果你想为模型指定不同的连接,可以使用 $connection 属性自定义
  5. 批量赋值:当用户通过 HTTP 请求传入了非预期的参数,并借助这些参数 create 方法更改了数据库中你并不打算要更改的字段,这时就会出现批量赋值(Mass-Assignment)漏洞,所以你需要先在模型上定义一个 fillable(白名单,允许批量赋值字段名数组)或guarded(黑名单,禁止批量赋值字段名数组)
    1 // 用属性取回航班,当结果不存在时创建它...
    2 $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
    3 
    4 // 用属性取回航班,当结果不存在时实例化一个新实例...
    5 $flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
  6. 模型软删除:如果模型有一个非空值 deleted_at,代表模型已经被软删除了。要在模型上启动软删除,则必须在模型上使用Illuminate\Database\Eloquent\SoftDeletes trait 并添加 deleted_at 字段到你的模型 $dates 属性上和数据表中,通过调用trashed方法可查询模型是否被软删除
    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 use Illuminate\Database\Eloquent\SoftDeletes;7 8 class Flight extends Model9 {
    10     use SoftDeletes;
    11 
    12     /**
    13      * 需要被转换成日期的属性。
    14      *
    15      * @var array
    16      */
    17     protected $dates = ['deleted_at'];
    18 }
    复制代码
  7. 查询作用域:Laravel允许对模型设定全局作用域和本地作用域(包括动态范围),全局作用域允许我们为模型的所有查询添加条件约束(定义一个实现 Illuminate\Database\Eloquent\Scope 接口的类),而本地作用域允许我们在模型中定义通用的约束集合(模型方法前加上一个 scope 前缀)。作用域总是返回查询构建器
    复制代码
     1 全局作用域定义:2 <?php3 4 namespace App\Scopes;5 6 use Illuminate\Database\Eloquent\Scope;7 use Illuminate\Database\Eloquent\Model;8 use Illuminate\Database\Eloquent\Builder;9 
    10 class AgeScope implements Scope
    11 {
    12     /**
    13      * 应用作用域
    14      *
    15      * @param  \Illuminate\Database\Eloquent\Builder  $builder
    16      * @param  \Illuminate\Database\Eloquent\Model  $model
    17      * @return void
    18      */
    19     public function apply(Builder $builder, Model $model)
    20     {
    21         return $builder->where('age', '>', 200);
    22     }
    23 }
    24 
    25 本地作用域:
    26 <?php
    27 
    28 namespace App;
    29 
    30 use Illuminate\Database\Eloquent\Model;
    31 
    32 class User extends Model
    33 {
    34     /**
    35      * 限制查询只包括受欢迎的用户。
    36      *
    37      * @return \Illuminate\Database\Eloquent\Builder
    38      */
    39     public function scopePopular($query)
    40     {
    41         return $query->where('votes', '>', 100);
    42     }
    43 
    44     /**
    45      * 限制查询只包括活跃的用户。
    46      *
    47      * @return \Illuminate\Database\Eloquent\Builder
    48      */
    49     public function scopeActive($query)
    50     {
    51         return $query->where('active', 1);
    52     }
    53 }
    54 
    55 动态范围:
    56 <?php
    57 
    58 namespace App;
    59 
    60 use Illuminate\Database\Eloquent\Model;
    61 
    62 class User extends Model
    63 {
    64     /**
    65      * 限制查询只包括指定类型的用户。
    66      *
    67      * @return \Illuminate\Database\Eloquent\Builder
    68      */
    69     public function scopeOfType($query, $type)
    70     {
    71         return $query->where('type', $type);
    72     }
    73 }
    复制代码
  8. 隐藏和显示属性:模型 hidden属性用于隐藏属性和关联的输出,visible 属性用于显示属性和关联的输出,另外makeVisible()还可用来临时修改可见性。当你要对关联进行隐藏时,需使用关联的方法名称,而不是它的动态属性名称
    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {9     /**
    10      * 在数组中可见的属性。
    11      *
    12      * @var array
    13      */
    14     protected $visible = ['first_name', 'last_name'];
    15 }
    16 ?>
    17 
    18 //makeVisible()用来临时修改可见性
    19 return $user->makeVisible('attribute')->toArray();
    复制代码
  9. 访问器和修改器:访问器(getFooAttribute)和修改器(setFooAttribute)可以让你修改 Eloquent 模型中的属性或者设置它们的值,比如你想要使用 Laravel 加密器来加密一个被保存在数据库中的值,当你从 Eloquent 模型访问该属性时该值将被自动解密。访问器和修改器要遵循cameCase命名规范,修改器会设置值到 Eloquent 模型内部的 $attributes 属性上

    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {9     /**
    10      * 获取用户的名字。
    11      *
    12      * @param  string  $value
    13      * @return string
    14      */
    15     public function getFirstNameAttribute($value)
    16     {
    17         return ucfirst($value);
    18     }
    19 
    20      /**
    21      * 设定用户的名字。
    22      *
    23      * @param  string  $value
    24      * @return void
    25      */
    26     public function setFirstNameAttribute($value)
    27     {
    28         $this->attributes['first_name'] = strtolower($value);
    29     }
    30 }
    复制代码

    而对于访问器与修改器的调用将是模型对象自动进行的

    1 $user = App\User::find(1);
    2 $user->first_name = 'Sally';//将自动调用相应的修改器
    3 $firstName = $user->first_name;//将自动调用相应的访问器 
  10. 追加属性:在转换模型到数组或JSON时,你希望添加一个在数据库中没有对应字段的属性,首先你需要为这个值定义一个 访问器,然后添加该属性到改模型的 appends 属性中
    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {9      /**
    10      * 访问器被附加到模型数组的形式。
    11      *
    12      * @var array
    13      */
    14     protected $appends = ['is_admin'];
    15 
    16     /**
    17      * 为用户获取管理者的标记。
    18      *
    19      * @return bool
    20      */
    21     public function getIsAdminAttribute()
    22     {
    23         return $this->attributes['admin'] == 'yes';
    24     }
    25 }
    复制代码
  11. 属性类型转换:$casts 属性数组在模型中提供了将属性转换为常见的数据类型的方法,且键是那些需要被转换的属性名称,值则是代表字段要转换的类型。支持的转换的类型有:integer、real、float、double、string、boolean、object、array、collection、date、datetime、timestamp
    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {9     /**
    10      * 应该被转换成原生类型的属性。
    11      *
    12      * @var array
    13      */
    14     protected $casts = [
    15         'is_admin' => 'boolean',//is_admin 属性以整数(0 或 1)被保存在我们的数据库中,把它转换为布尔值
    16     ];
    17 }
    复制代码
  12. 序列化: Laravel模型及关联可递归序列化成数组或JSON

    复制代码
     1 //单个模型实例序列化成数组2 $user = App\User::with('roles')->first();3 return $user->toArray();4 //集合序列化成数组5 $users = App\User::all();6 return $users->toArray();7 8 //单个模型实例序列化成JSON9 $user = App\User::find(1);
    10 return $user->toJson();
    11 //直接进行string转换会将模型或集合序列化成JSON
    12 $user = App\User::find(1);
    13 return (string) $user;
    14 //因此你可以直接从应用程序的路由或者控制器中返回 Eloquent 对象
    15 Route::get('users', function () {
    16     return App\User::all();
    17 });
    复制代码
  13. 关联(方法)与动态属性:在 Eloquent 模型中,关联被定义成方法(methods),也可以作为强大的查询语句构造器
    1 $user->posts()->where('active', 1)->get();

    Eloquent 模型支持多种类型的关联:一对一、一对多、多对多、远层一对多、多态关联、多态多对多关联

    举个例子,一个 User 模型会关联一个 Phone 模型,一对一关联(hasOne)
    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class User extends Model8 {9     /**
    10      * 获取与用户关联的电话号码
    11      */
    12     public function phone()
    13     {
    14         return $this->hasOne('App\Phone');
    15     }
    16 }
    复制代码

    动态属性允许你访问关联方法,使用 Eloquent 的动态属性来获取关联记录,如同他们是定义在模型中的属性

    1 $phone = User::find(1)->phone;

    Eloquent 会假设对应关联的外键名称是基于模型名称的。在这个例子里,它会自动假设 Phone 模型拥有 user_id 外键。如果你想要重写这个约定,则可以传入第二个参数到 hasOne 方法里

    1 return $this->hasOne('App\Phone', 'foreign_key');

    如果你想让关联使用 id 以外的值,则可以传递第三个参数至 hasOne 方法来指定你自定义的键

    1 return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

    如果我们要在 Phone 模型上定义一个反向关联,此关联能够让我们访问拥有此电话的 User 模型。我们可以定义与 hasOne 关联相对应的 belongsTo 方法

    复制代码
     1 <?php2 3 namespace App;4 5 use Illuminate\Database\Eloquent\Model;6 7 class Phone extends Model8 {9     /**
    10      * 获取拥有该电话的用户模型。
    11      */
    12     public function user()
    13     {
    14         return $this->belongsTo('App\User');
    15     }
    16 } 
    复制代码
  14. 模型事件: Laravel为模型定义的事件包括creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。 模型上定义一个 $events 属性

    复制代码
     1 <?php2 3 namespace App;4 5 use App\Events\UserSaved;6 use App\Events\UserDeleted;7 use Illuminate\Notifications\Notifiable;8 use Illuminate\Foundation\Auth\User as Authenticatable;9 
    10 class User extends Authenticatable
    11 {
    12     use Notifiable;
    13 
    14     /**
    15      * 模型的时间映射。
    16      *
    17      * @var array
    18      */
    19     protected $events = [
    20         'saved' => UserSaved::class,
    21         'deleted' => UserDeleted::class,
    22     ];
    23 }
    复制代码

     如果你在一个给定的模型中监听许多事件,也可使用观察者将所有监听器变成一个类,类的一个方法就是一个事件监听器

    复制代码
     1 定义观察者:2 <?php3 4 namespace App\Observers;5 6 use App\User;7 8 class UserObserver9 {
    10     /**
    11      * 监听用户创建的事件。
    12      *
    13      * @param  User  $user
    14      * @return void
    15      */
    16     public function created(User $user)
    17     {
    18         //
    19     }
    20 
    21     /**
    22      * 监听用户删除事件。
    23      *
    24      * @param  User  $user
    25      * @return void
    26      */
    27     public function deleting(User $user)
    28     {
    29         //
    30     }
    31 }
    32 
    33 注册观察者:
    34 <?php
    35 
    36 namespace App\Providers;
    37 
    38 use App\User;
    39 use App\Observers\UserObserver;
    40 use Illuminate\Support\ServiceProvider;
    41 
    42 class AppServiceProvider extends ServiceProvider
    43 {
    44     /**
    45      * 运行所有应用.
    46      *
    47      * @return void
    48      */
    49     public function boot()
    50     {
    51         User::observe(UserObserver::class);
    52     }
    53 
    54     /**
    55      * 注册服务提供.
    56      *
    57      * @return void
    58      */
    59     public function register()
    60     {
    61         //
    62     }
    63 }
    复制代码

 

八. Laravel的Restful风格

  一般认为Restful风格的资源定义不包含操作,但是在Laravel中操作(动词)也可作为一种资源来定义。下图是对Laravel中资源控制器操作原理的描述,可以看到,create、edit就直接出现在了URI中,它们是一种合法的资源。对于create和edit这两种资源的访问都采用GET方法来实现,第一眼看到顿感奇怪,后来尝试通过artisan console生成资源控制器,并注意到其对create、edit给出注释“ Show the form for ”字样,方知它们只是用来展现表单而非提交表单的。

 

九. 扩展开发

  我们知道,Laravel本身是基于Composer管理的一个包,遵循Composer的相关规范,可以通过Composer来添加所依赖的其他Composer包,因此在做应用的扩展开发时,可以开发Composer包然后引入项目中即可;另外也可开发基于Laravel的专属扩展包。下面所讲的就是Laravel的专属扩展开发,最好的方式是使用 contracts ,而不是 facades,因为你开发的包并不能访问所有 Laravel 提供的测试辅助函数,模拟 contracts 要比模拟 facade 简单很多。

  • 服务提供者:服务提供者是你的扩展包与 Laravel 连接的重点,须定义自己的服务提供者并继承自 Illuminate\Support\ServiceProvider 基类
  • 路由:若要为你的扩展包定义路由,只需在包的服务提供者的 boot 方法中传递 routes 文件路径到 loadRoutesFrom 方法即可
    复制代码
    1 /**
    2  * 在注册后进行服务的启动。
    3  *
    4  * @return void
    5  */
    6 public function boot()
    7 {
    8     $this->loadRoutesFrom(__DIR__.'/path/to/routes.php');
    9 }
    复制代码
  • 配置文件:你可以选择性地将扩展包的配置文件发布(publishes)到应用程序本身的config目录上或者合并(mergeConfigFrom)到应用程序里的副本配置文件中,但不应在配置文件中定义闭包函数,当执行 config:cache Artisan命令时,它们将不能正确地序列化
    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * 用户使用 vendor:publish 命令可将扩展包的文件将会被复制到指定的位置上。5  *6  * @return void7  */8 public function boot()9 {
    10     $this->publishes([
    11         __DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
    12     ]);
    13 }
    14 
    15 $value = config('courier.option');//只要你的配置文件被发布,就可以如其它配置文件一样被访问
    16 
    17 /**
    18  * 或者选择性在容器中注册绑定。
    19  *
    20  * 此方法仅合并配置数组的第一级。如果您的用户部分定义了多维配置数组,则不会合并缺失的选项
    21  *
    22  * @return void
    23  */
    24 public function register()
    25 {
    26     $this->mergeConfigFrom(
    27         __DIR__.'/path/to/config/courier.php', 'courier'
    28     );
    29 }
    复制代码
  • 数据库迁移:如果你的扩展包包含数据库迁移,需要使用 loadMigrationsFrom 方法告知 Laravel 如何去加载它们。在运行 php artisan migrate 命令时,它们就会自动被执行,不需要把它们导出到应用程序的 database/migrations 目录
    复制代码
    1 /**
    2  * 在注册后进行服务的启动。
    3  *
    4  * @return void
    5  */
    6 public function boot()
    7 {
    8     $this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
    9 }
    复制代码
  • 语言包:如果你的扩展包里面包含了本地化,则可以使用 loadTranslationsFrom 方法来告知 Laravel 该如何加载它们。下例假设你的包名称为courier
    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * @return void5  */6 public function boot()7 {8     $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');9 
    10     //如果不想发布语言包至应用程序的 resources/lang/vendor 目录,请注销对$this->publishes()调用。运行 Laravel 的 vendor:publish Artisan 命令可将扩展包的语言包复制到指定的位置上
    11     $this->publishes([
    12         __DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),
    13     ]);
    14 }
    15 
    16 echo trans('courier::messages.welcome');//扩展包翻译参照使用了双分号 package::file.line 语法
    复制代码

     

  • 视图:若要在 Laravel 中注册扩展包 视图,则必须告诉 Laravel 你的视图位置,loadViewsFrom 方法允许传递视图模板路径与扩展包名称两个参数。需要特别指出的是,当你使用 loadViewsFrom 方法时,Laravel 实际上为你的视图注册了两个位置:一个是应用程序的 resources/views/vendor 目录,另一个是你所指定的目录。Laravel会先检查 resources/views/vendor 目录是否存在待加载视图,如果不存在,才会从指定的目录去加载,这个方法可以让用户很方便的自定义或重写扩展包视图。

    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * @return void5  */6 public function boot()7 {8     $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');9 
    10     //若要发布扩展包的视图至 resources/views/vendor 目录,则必须使用服务提供者的 publishes 方法。运行 Laravel 的 vendor:publish Artisan 命令时,扩展包的视图将会被复制到指定的位置上 
    11     $this->publishes([
    12         __DIR__.'/path/to/views' => resource_path('views/vendor/courier'),
    13     ]); 
    14 }
    15 
    16 //扩展包视图参照使用了双分号 package::view 语法
    17 Route::get('admin', function () {
    18     return view('courier::admin');
    19 });
    复制代码
  • 命令:使用 commands 方法给扩展包注册 Artisan 命令,命令的定义要遵循Laravel Artisan 命令规范

    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * @return void5  */6 public function boot()7 {8     if ($this->app->runningInConsole()) {9         $this->commands([
    10             FooCommand::class,
    11             BarCommand::class,
    12         ]);
    13     }
    14 }
    复制代码
  • 公用 Assets:你可以发布像 JavaScript、CSS 和图片这些资源文件到应用程序的 public 目录上。当用户执行 vendor:publish 命令时,您的 Assets 将被复制到指定的发布位置。由于每次更新包时通常都需要覆盖资源,因此您可以使用 --force 标志:php artisan vendor:publish --tag=public --force

    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * @return void5  */6 public function boot()7 {8     $this->publishes([9         __DIR__.'/path/to/assets' => public_path('vendor/courier'),
    10     ], 'public');
    11 }
    复制代码

     

  • 发布群组文件:你可能想让用户不用发布扩展包的所有资源文件,只需要单独发布扩展包的配置文件即可,通过在调用 publishes 方法时使用标签来实现

    复制代码
     1 /**2  * 在注册后进行服务的启动。3  *4  * @return void5  */6 public function boot()7 {8     $this->publishes([9         __DIR__.'/../config/package.php' => config_path('package.php')
    10     ], 'config');
    11 
    12     $this->publishes([
    13         __DIR__.'/../database/migrations/' => database_path('migrations')
    14     ], 'migrations');
    15 }
    复制代码

    对于上例运行命令 php artisan vendor:publish --tag=config 时将忽略掉migrations部

转载于:https://www.cnblogs.com/xuqp/p/7285161.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/428980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

L2-002 链表去重 (25 分) 船新方法

L2-002 链表去重 (25 分)给定一个带整数键值的链表 L&#xff0c;你需要把其中绝对值重复的键值结点删掉。即对每个键值 K&#xff0c;只有第一个绝对值等于 K 的结点被保留。同时&#xff0c;所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15&…

oracle中间键配置,Oracle Exalogic中间件云服务器简介及产品配置特点(2)

Oracle Exalogic中间件云服务器 —— 软硬件结合的优化平台目前&#xff0c;企业所面临的应用挑战大多集中在异构环境、复杂的多层应用以及现有环境的低利用率等。于是&#xff0c;企业开始根据实际业务需求和投资情况将系统进行整合设计&#xff0c;旨在精简架构以提高系统的灵…

System

类&#xff1a; System.TimeDate 关于时间的类转载于:https://www.cnblogs.com/Sea1ee/p/7285238.html

L2-003 月饼 (25 分)

L2-003 月饼 (25 分)月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的…

Oracle删除pk+cascade,Oracle删除表

在本教程中&#xff0c;将学习如何使用Oracle DROP TABLE语句删除现有的表。Oracle DROP TABLE语句简介要将表移动到回收站或将其从数据库中完全删除&#xff0c;请使用DROP TABLE语句&#xff1a;DROP TABLE schema_name.table_name [CASCADE CONSTRAINTS | PURGE];在这个语句…

L2-005 集合相似度 (25 分)

L2-005 集合相似度 (25 分) 给定两个整数集合&#xff0c;它们的相似度定义为&#xff1a;N ​c ​​ /N ​t ​​ 100%。其中N ​c ​​ 是两个集合都有的不相等整数的个数&#xff0c;N ​t ​​ 是两个集合一共有的不相等整数的个数。你的任务就是计算任意一对给定集合的相似…

oracle有sysdata,Oracle基础

1、默认事例&#xff1a;用户名&#xff1a;scott密码&#xff1a;tiger 主机字符串&#xff1a;本机可以为空2、启动方法&#xff1a;运行&#xff1a;sqlplusscott/tigerlhd3、SQLPLUS基本命令&#xff1a;Desc&#xff1a;显示表、视图结构 desc 表名&#xff0c;视图List&…

selenium+java初级学习笔记之单个元素定位

元素定位可以大概分为八重一般情况下常用的就是by.id、by.name、by.classname&#xff0c;如果需要定位的元素中没有这几个可以选择其他方式的定位&#xff1a;By.id(id) //&#xff08;有时元素中id是会变化的&#xff0c;可以刷新页面&#xff0c;如果发现每次的id都不一样则…

L2-004 这是二叉搜索树吗? (25 分)

L2-004 这是二叉搜索树吗&#xff1f; (25 分) 一棵二叉搜索树可被递归地定义为具有下列性质的二叉树&#xff1a;对于任一结点&#xff0c; 其左子树中所有结点的键值小于该结点的键值&#xff1b; 其右子树中所有结点的键值大于等于该结点的键值&#xff1b; 其左右子树都是…

Linux高频命令汇总,Linux高频命令

8种机械键盘轴体对比本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f;findfind path [options] params作用&#xff1a;在指定目录下查找文件1234find / -name "target.java" #在根目录下查找target.java文件find -name &qu…

深入理解javascript函数参数

arguments javascript对参数要求很随意&#xff0c;她才不管你传进来的参数是什么数据类型&#xff0c;甚至可以不传参数。实际上&#xff0c;javascript函数调用甚至不检查传入形参的个数。 1 function add(x){ 2 return x 1; 3 } 4 console.log(add(1))…

linux系统时间函数,Linux时间时区详解与常用时间函数

时间与时区整个地球分为二十四时区&#xff0c;每个时区都有自己的本地时间。UTC时间 与 GMT时间我们可以认为格林威治时间就是时间协调时间(GMT UTC)&#xff0c;格林威治时间和UTC时间都用秒数来计算的。UTC时间与本地时间UTC 时区差 本地时间时区差东为正&#xff0c;西为…

7-5 汉诺塔的非递归实现 (25 分)

7-5 汉诺塔的非递归实现 (25 分) 借助堆栈以非递归&#xff08;循环&#xff09;方式求解汉诺塔的问题&#xff08;n, a, b, c&#xff09;&#xff0c;即将N个盘子从起始柱&#xff08;标记为“a”&#xff09;通过借助柱&#xff08;标记为“b”&#xff09;移动到目标柱&…

js 异步问题

如果发现前端的异步请求&#xff0c;network有发送成功而且有返回值&#xff0c;但是没有进前端的callback成功回掉函数内&#xff0c;这时候就需要检查是不是配置文件做了语法限制&#xff0c;只能返回某些数据格式&#xff0c;不能返回某些数据格式。 一般限制最多的就是后端…

海康、大华IPC的rtsp格式

海康&#xff1a;  rtsp://[username]:[password][ip]:[port]/[codec]/[channel]/[subtype]/av_stream说明&#xff1a;username: 用户名。例如admin。password: 密码。例如12345。ip: 为设备IP。例如 192.0.0.64。port: 端口号默认为554&#xff0c;若为默认可不填写。codec…

linux awr 日志,Linux平台生成awr报告

1&#xff0e;使用Oracle用户登录应用服务器所使用的数据库所在的服务器# su –oracle2&#xff0e;输入env命令&#xff0c;查询出ORACLE_HOME 目录3&#xff0e;然后进入$ORACLE_HOME/rdbms/admin目录&#xff0c;查询awr语句(此步骤可以忽略)此处可以直接省略2&#xff0c;3…

利用一维数组输出杨辉三角

直接上代码&#xff1a; package hello;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n in.nextInt();int table [] new int [n1];table[0] 1;for (int i1;i<n;i) {for (int j0;j<…

缓存(Cache)管理 ---- 系列文章

利用Cache防止同一帐号重复登录.net中Cache管理操作系统缓存全解析 (下)系统缓存全解析 (中)系统缓存全解析 (上)出处&#xff1a;http://www.cnblogs.com/luckdv/tag/%E7%BC%93%E5%AD%98/转载于:https://www.cnblogs.com/mq0036/p/7291955.html

找到一个二位数组的鞍点

上代码&#xff1a; package hello;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int mat[][] new int[3][3];for (int i0;i<3;i) {for (int j0;j<3;j) {mat[i][j] in.nextInt();}}bool…

linux 文件按时间 函数,[Linux文件属性]使用utime函数操作文件的时间参数

//这是一个使用utime函数对文件的时间参数进行修改的实例//文件首先使用stat函数获得文件当前的时间参数&#xff0c;然后使用//Open函数对文件进行修改&#xff0c;再用utime函数对文件的时间信息//进行修改.文件名由argv参数给出#include #include #include int main(int arg…