Laravel

Laravel 核心概念

配套视频地址:https://www.bilibili.com/video/av76060293


核心概念

简介、服务容器、服务提供者、facade、contract、生命周期。

简介

我叫 Laravel。我是一个装了药的 药箱,专门处理人们的问题、治病。

人们喜欢把我的药箱叫做 service container 服务容器

把我的药箱里面的一块一块的小格子叫 service provider 服务提供者。在这些小格子里可以放置不同的药。

有的人用到我的时候,会在我的小格子或者是他们自制的格子里面放置自己制作的药。有些药有副作用,比如可以治疗肚子疼又能治疗头痛,这样肚子疼、头不疼的患者吃了可能对头产生不良影响。所以我定了一份合同契约让放进来的药有个规范。并且我内置的药也是有契约来规范约束我自己。他们把我的这个合同称为 contract 契约

有些药片很难看,可以把它用糖衣包装起来,这样小孩子更容易吞食使用。我里面很多药都用了这种包装,药效没增加,但是更容易使用。人们后期添加的药也可以自制包装。这种包装称之为 facade 门面

一、药箱(服务容器)

绑定,解析,解析事件(类似于在药瓶中放药,取药,取药事项)。

放药(绑定)

基础绑定

$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});

绑定单例

$this->app->singleton('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});

绑定实例

$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);

绑定实例时给定初始化数据

$this->app->when('App\Http\Controllers\UserController')
          ->needs('$variableName')
          ->give($value);    // 利用上下文给绑定设置初始数据

举个栗子

interface Fruit
{
    public function color();
}

class Apple implements Fruit
{
    public $color;

    public function __construct($color){
        $this->color = $color;
    }

    public function color(){
        return $this->color;
    }
}

app()->bind('Fruit', 'Apple');
app()->when('Apple')->needs('$color')->give('red');
echo app('Fruit')->color();

绑定接口到实例

$this->app->bind(
    'App\Contracts\EventPusher',
    'App\Services\RedisEventPusher'
);

根据上下文提供不同的绑定

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when([VideoController::class, UploadController::class])
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

给绑定设置标签

$this->app->bind('SpeedReport', function () {
    //
});

$this->app->bind('MemoryReport', function () {
    //
});

$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

$this->app->bind('ReportAggregator', function ($app) {
    return new ReportAggregator($app->tagged('reports'));
});

Service 已经被解析,extend 方法可以用来修改解析出来的实例 $service

$this->app->extend(Service::class, function ($service, $app) {
    return new DecoratedService($service);
});
拿药(解析)
$this->app->make('HelpSpot\API');
app()->make('HelpSpot\API');
resolve('HelpSpot\API');
app('HelpSpot\API');
app()['HelpSpot\API']
app()->get('HelpSpot\API')
public function xxx(HelpSpot\API $users)
// 注入依赖
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
$api = app('HelpSpot\API', ['id' => 1]); 
$api = resolve('HelpSpot\API', ['id' => 1]); 
# laravel 实现了 PSR-11 接口,所以就可以用该接口的类型提示解析
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
    $service = $container->get('Service');
    //
});
容器事件

容器解析任何对象时调用

$this->app->resolving(function ($object, $app) {

});

容器解析 HelpSpot\API 时调用

$this->app->resolving(HelpSpot\API::class, function ($api, $app) {

});

二、药箱里的小格子(服务提供者)

制作一个服务提供者
  1. php artisan make:provider RiakServiceProvider
  2. 服务提供者主要由两个方法:registerbootregister 只负责绑定一些东西到容器(放药)。boot 可以使用类型提示解析等来完成任意你想做的事情,这些都归功于容器调用所有服务提供者的register方法之后才去调用boot方法。
  3. config/app.phpproviders数组中注册服务提供者。
制作一个延迟服务提供者

如果只是绑定服务到服务容器,可以设置为延迟(实现 DeferrableProvider 接口),在项目真正需要用到之前不会注册。

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register()
    {
        $this->app->singleton(Connection::class, function ($app) {
            return new Connection(config('riak'));
        });
    }

    public function provides()
    {
        return [Connection::class];
    }
}

三、Facades

不要在一个类中,用太多的facades。过于臃肿的情况下应该将大类分解成几个小类。

优点

方便测试(辅助函数和 facades 没什么区别,测试方法也是一样的)。

Route::get('/cache', function () {
    return Cache::get('key');     // === return cache('key');
});
public function testBasicExample()
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $this->visit('/cache')
         ->see('value');
}
实时的 facades

原生用法 vs 实时用法

# 原生用法 class Podcast
use App\Contracts\Publisher;
public function publish(Publisher $publisher)
{
    $this->update(['publishing' => now()]);
    $publisher->publish($this);
}
# 实时用法
use Facades\App\Contracts\Publisher;

Publisher::publish($this);

测试实时的 facades

use Facades\App\Contracts\Publisher;
Publisher::shouldReceive('publish')->once()->with($podcast);
facades 列表
Facade Class Service Container Binding
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory  
Broadcast (Instance) Illuminate\Contracts\Broadcasting\Broadcaster  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\CacheManager cache
Cache (Instance) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Password (Instance) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue.connection
Queue (Base Class) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis (Instance) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
Response (Instance) Illuminate\Http\Response  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Builder  
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
Storage (Instance) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (Instance) Illuminate\Validation\Validator  
View Illuminate\View\Factory view
View (Instance) Illuminate\View\View

四、Contracts

FacadesContracts 没有什么值得注意的区别,但是当你开发第三方包的时候,最好使用 Contracts,这样有利于你编写测试,否则如果使用 Facades,因为是第三方包,将不能访问 facade 测试函数。

使用方法

在构造函数中类型提示注入就行了。

Contracts 列表
Contract References Facade
Illuminate\Contracts\Auth\Access\Authorizable  
Illuminate\Contracts\Auth\Access\Gate Gate
Illuminate\Contracts\Auth\Authenticatable  
Illuminate\Contracts\Auth\CanResetPassword  
Illuminate\Contracts\Auth\Factory Auth
Illuminate\Contracts\Auth\Guard Auth::guard()
Illuminate\Contracts\Auth\PasswordBroker Password::broker()
Illuminate\Contracts\Auth\PasswordBrokerFactory Password
Illuminate\Contracts\Auth\StatefulGuard  
Illuminate\Contracts\Auth\SupportsBasicAuth  
Illuminate\Contracts\Auth\UserProvider  
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Bus\QueueingDispatcher Bus::dispatchToQueue()
Illuminate\Contracts\Broadcasting\Factory Broadcast
Illuminate\Contracts\Broadcasting\Broadcaster Broadcast::connection()
Illuminate\Contracts\Broadcasting\ShouldBroadcast  
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow  
Illuminate\Contracts\Cache\Factory Cache
Illuminate\Contracts\Cache\Lock  
Illuminate\Contracts\Cache\LockProvider  
Illuminate\Contracts\Cache\Repository Cache::driver()
Illuminate\Contracts\Cache\Store  
Illuminate\Contracts\Config\Repository Config
Illuminate\Contracts\Console\Application  
Illuminate\Contracts\Console\Kernel Artisan
Illuminate\Contracts\Container\Container App
Illuminate\Contracts\Cookie\Factory Cookie
Illuminate\Contracts\Cookie\QueueingFactory Cookie::queue()
Illuminate\Contracts\Database\ModelIdentifier  
Illuminate\Contracts\Debug\ExceptionHandler  
Illuminate\Contracts\Encryption\Encrypter Crypt
Illuminate\Contracts\Events\Dispatcher Event
Illuminate\Contracts\Filesystem\Cloud Storage::cloud()
Illuminate\Contracts\Filesystem\Factory Storage
Illuminate\Contracts\Filesystem\Filesystem Storage::disk()
Illuminate\Contracts\Foundation\Application App
Illuminate\Contracts\Hashing\Hasher Hash
Illuminate\Contracts\Http\Kernel  
Illuminate\Contracts\Mail\MailQueue Mail::queue()
Illuminate\Contracts\Mail\Mailable  
Illuminate\Contracts\Mail\Mailer Mail
Illuminate\Contracts\Notifications\Dispatcher Notification
Illuminate\Contracts\Notifications\Factory Notification
Illuminate\Contracts\Pagination\LengthAwarePaginator  
Illuminate\Contracts\Pagination\Paginator  
Illuminate\Contracts\Pipeline\Hub  
Illuminate\Contracts\Pipeline\Pipeline  
Illuminate\Contracts\Queue\EntityResolver  
Illuminate\Contracts\Queue\Factory Queue
Illuminate\Contracts\Queue\Job  
Illuminate\Contracts\Queue\Monitor Queue
Illuminate\Contracts\Queue\Queue Queue::connection()
Illuminate\Contracts\Queue\QueueableCollection  
Illuminate\Contracts\Queue\QueueableEntity  
Illuminate\Contracts\Queue\ShouldQueue  
Illuminate\Contracts\Redis\Factory Redis
Illuminate\Contracts\Routing\BindingRegistrar Route
Illuminate\Contracts\Routing\Registrar Route
Illuminate\Contracts\Routing\ResponseFactory Response
Illuminate\Contracts\Routing\UrlGenerator URL
Illuminate\Contracts\Routing\UrlRoutable  
Illuminate\Contracts\Session\Session Session::driver()
Illuminate\Contracts\Support\Arrayable  
Illuminate\Contracts\Support\Htmlable  
Illuminate\Contracts\Support\Jsonable  
Illuminate\Contracts\Support\MessageBag  
Illuminate\Contracts\Support\MessageProvider  
Illuminate\Contracts\Support\Renderable  
Illuminate\Contracts\Support\Responsable  
Illuminate\Contracts\Translation\Loader  
Illuminate\Contracts\Translation\Translator Lang
Illuminate\Contracts\Validation\Factory Validator
Illuminate\Contracts\Validation\ImplicitRule  
Illuminate\Contracts\Validation\Rule  
Illuminate\Contracts\Validation\ValidatesWhenResolved  
Illuminate\Contracts\Validation\Validator Validator::make()
Illuminate\Contracts\View\Engine  
Illuminate\Contracts\View\Factory View
Illuminate\Contracts\View\View View::make()

五、请求生命周期

本节主要概括了框架运行的生命周期。

  1. 所有请求必定首先通过 public/index.php
  2. 在上述这个文件中首先加载 composer 自动加载文件,然后从 bootstrap/app.php 实例化一个服务容器。
  3. 接下来,框架会根据请求类型传送请求至 app/Http/Kernel.php 或者 app/Console/Kernel.php
  4. app/Http/Kernel.php 扩展了Illuminate\Foundation\Http\Kernel 类,父类强制在处理请求前应该做哪些操作,操作内容都放到了 bootstrappers 数组里面(配置错误处理、配置记录日志、检测应用环境、注册和启动服务提供者等)。子类在数组 middleware 中规定了请求在被处理前必须经过的一些处理(读写 session、判断是否处于维护模式、验证 csrf 令牌等)。
  5. 处理请求,返回响应内容。

一条评论

发表评论