PHP框架 - laravel

一、依赖注入实现原理

依赖注入不是让对象创建一个依赖关系,也不是让工厂对象去创建对象,而是将所需的依赖变成一个外部对象,使之成为一个"某些人的问题”,你为"某些人的问题”注入了类的依赖关系。在Laravel中,这个"某人”是服务容器,服务容器负责通过构造函数注入类的依赖关系。

概述

任何时候,你在一个控制器类中请求一个依赖,这个服务容器负责:
1.自动地在构造函数中检测依赖关系
2.如果需要构建这个依赖关系
3.通过构造函数创建对象形成依赖关系

来看一个非常简单的例子。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
    
    
    protected $userRepository;
    public function __construct(UserRepository $userRepository)
    {
    
    
        $this->userRepository = $userRepository;
    }
    public function show($id)
    {
    
    
        $user = $this->userRepository->find($id);
        return view('user.profile', ['user' => $user]);
    }
}

假如,你有一个UserController类需要UserRepository作为一个构造函数依赖。
1.服务容器使用PHP的反射类来检测,事实UserRepository需要被优先解析。
2.然后,它构造UserRepository实例。
3.然后,它构造UserController类实例。

依赖关系是如何被解析和注入的,我被很多Laravel开发人员不知道这个简单而强大的技术感到迷惑。 这是一个非常强大的技术,它可以被用来解决复杂对象的依赖关系。

如果由于某种原因,您不希望Laravel自动构建一个对象,您还可以通过传递一个可用于创建依赖关系的回调来告诉Laravel Service Container如何构造该对象。

<?php
$container->bind('My\Service', function($container) {
    
    
return new My\Service($container->make('My\AnotherService'));
});

您需要创建一个服务提供商来注册上述服务。

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider
{
    
    
    public function register()
    {
    
    
        $this->app->singleton(\My\Service::class, function ($app) {
    
    
            return new \My\Service($app->make('My\AnotherService'));
        });
    }
}

当My\Service需要被解析的时候,负责返回一个对象的回调函数就会被调用。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class MyController extends Controller
{
    
    
    protected $myService;
    public function __construct(\My\Service $myService)
    {
    
    
        $this->myService = $myService;
    }
    // .. 方法
}
真实的例子

假设你的应用需要Facebook的PHP SDK来访问Facebook的API,你的控制器就是这样的:

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
use Facebook\Facebook;
class FacebookApiAccessController extends Controller
{
    
    
    protected $facebook;
    public function __construct(Facebook\Facebook $facebook)
    {
    
    
        $this->facebook = $facebook;
    }
    //.. action methods here
}

现在,您需要告诉Service Container如何构建Facebook\Facebook的实例.

<?php
$container->singleton('Facebook\Facebook', function() {
    
    
  return new \Facebook\Facebook([
    'app_id' => config('services.facebook.app_id'),
    'app_secret' => config('services.facebook.app_secret'),
    'default_graph_version' => 'v2.10',
  ]);
});

注意,我已经调用了方法singleton而不是bind。 唯一的区别是用singleton注册的服务被缓存,随后的解析服务调用返回缓存的服务。

方法

构造方法注入、方法注入、接口注入

结论

依赖注入是一种强大的技术,你可以在Laravel中用来简化对象的创建. 默认情况下,Laravel的服务容器会自动的用反射去检测和解决依赖关系. 但是, 你可以指定回调来解析服务

二、常用集合方法

Laravel 集合是 Laravel 框架中一个十分有用的工具。Laravel 集合就像是在 PHP 中的数组,集合的目的在于让我们轻松的对数组进行处理。laravel查找出来的数据,一般都是集合形式。

  • filter()
    filter,最有用的 laravel 集合方法之一,允许您使用回调过滤集合。它只传递那些返回true的项。所有其他项目都被删除。 filter 返回一个新实例而不更改原始实例。它接受 value 和 key 作为回调中的两个参数。
  • search()
    search 方法可以用给定的值查找集合。如果这个值在集合中,会返回对应的键。如果没有数据项匹配对应的值,会返回 false。
  • chunk()
    chunk 方法将集合分割为多个给定大小的较小集合。将集合显示到网格中非常有用。
  • dump()
    dump 打印集合的方法。它可用于在任何位置的调试和查找集合内的内容。
  • map()
    map 方法用于遍历整个集合。它接受回调作为参数。
  • zip()
    Zip 方法会将给定数组的值与集合的值合并在一起。相同索引的值会添加在一起,这意味着,数组的第一个值会与集合的第一个值合并。在这里,我会使用我们在上面刚刚创建的集合。这对 Eloquent 集合同样有效。
  • whereNotIn()
    您可以使用 whereNotIn 方法简单地按照给定数组中未包含的键值过滤集合。它基本上与 whereIn 相反。
  • max()
    max 方法返回给定键的最大值。你可以通过调用max来找到最大的 user_id。
  • pluck()
    pluck 方法返回指定键的所有值。它对于提取一列的值很有用。
  • each()
    each 是一种迭代整个集合的简单方法。它接受一个带有两个参数的回调:它正在迭代的项和键。Key 是基于 0 的索引。
  • tap()
    tap() 方法允许你随时加入集合。它接受回调并传递并将集合传递给它。您可以对项目执行任何操作,而无需更改集合本身。因此,您可以在任何时候使用tap来加入集合,而不会改变集合。
  • pipe()
    pipe 方法非常类似于 tap 方法,因为它们都在集合管道中使用。 pipe 方法将集合传递给回调并返回结果。
  • contains()
    contains 方法只检查集合是否包含给定值。只传递一个参数时才会出现这种情况。
  • forget()
    forget 只是从集合中删除该项。您只需传递一个键,它就会从集合中删除该项目。
  • avg()
    avg 方法返回平均值。你只需传递一个键作为参数,avg 方法返回平均值。你也可以使用 average 方法,它基本上是 avg 的别名。

三、常用辅助函数

Laravel包含各种全局辅助函数(helper), 你可以使用它们使开发工作流程更加轻松。在这里,我将会列一下10个最好的 Laravel helpers,它们使我的开发更容易。必要时大家也考虑使用它们。
更多的辅助函数,可以看 Laravel官方文档

1、array_dot()
array_dot()辅助函数允许你将多维数组转换为使用点符号的一维数组。

$array = [ 'user' => ['username' => 'something'], 'app' => ['creator' => ['name' => 'someone'], 'created' => 'today']];$dot_array = array_dot($array);// [user.username] => something, [app.creator.name] => someone, [app.created] => today

2、array_get()
array_get() 函数使用点符号从多维数组中检索值。

$array = [ 'user' => ['username' => 'something'], 'app' => ['creator' => ['name' => 'someone'], 'created' => 'today']];$name = array_get($array, 'app.creator.name');// someone

如果key不存在,array_get()函数还接受可选的第三个参数作为默认值。

$name = array_get($array, 'app.created.name', 'anonymous');// anonymous

3、public_path()
public_path() 返回 Laravel 应用程序中公共目录的完全限定的绝对路径。你还可以将路径传递到公共目录中的文件或目录以获取该资源的绝对路径。它将简单地将 public_path() 添加到你的参数中。

$public_path = public_path();$path = public_path('js/app.js');

4、Str::orderedUuid()
Str::orderedUuid() 函数首先生成一个时间戳uuid。这个uuid可以存储在索引数据库列中。这些uuid是基于时间戳创建的,因此它们会保留你的内容索引。在Laravel 5.6中使用它时,会引发 RamseyUuidExceptionUnsatisfiedDependencyException。要解决此问题,只需运行以下命令即可使用 moontoast/math 包:

composer require "moontoast/math"

use IlluminateSupportStr;return (string) Str::orderByUuid()// A timestamp first uuid

5、str_plural()
str_plural() 函数将字符串转换为复数形式。该功能只支持英文。

echo str_plural('bank');// banksecho str_plural('developer');// developers

6、route()
route() 函数为指定的路由生成路由URL。

$url = route('login');

如果路由接受参数,你可以简单地将它们作为第二个参数传递给一个数组。

$url = route('products', ['id' => 1]);

如果你想产生一个相对的URL而不是一个绝对的URL,你可以传递false作为第三个参数。

$url = route('products', ['id' => 1], false);

7、tap()
tap() 函数接受两个参数:一个值和一个闭包。该值将被传递给闭包,然后该值将被返回。闭包返回值无关紧要。

$user = AppUser::find(1);return tap($user, function($user) {
    
     $user->update([ 'name' => 'Random' ]);});

它不会返回布尔值,而是返回 UserModel 。

如果你没有传递闭包,你也可以使用 UserModel 的任何方法。无论实际返回的方法如何,返回值都将始终为值。在下面的例子中,它将返回 UserModel 而不是布尔值。更新方法返回布尔值,但由于用了 tap ,所以它将返回 UserModel。

$user = AppUser::find(1);return tap($user)->update([ 'name' => 'SomeName']);

8、dump()
dump() 函数会dump给定的变量,同时也支持同时传入多个变量。这对调试非常有用。

dump($var1);dump($var1, $var2, $var3);

9、str_slug()
str_slug() 函数根据给定的字符串生成一个友好的URL。你可以使用此功能为你的帖子或产品标题创建一个 slug 。

$slug = str_slug('Helpers in Laravel', '-');// helpers-in-laravel

10、optional()
optional() 函数接受一个参数,你可以调用它的方法或访问属性。如果传递的对象为null,则方法和属性将返回null而不是导致错误或抛出异常。

$user = User::find(1);return optional($user)->name;

四、中间件

中间件在 laravel 中的作用就是过滤 HTTP 请求,根据不同的请求来执行不同的逻辑操作。

我们可以通过中间件实现以下功能:

  • 指定某些路由
  • 设置 HTTP 响应头
  • 记录请求
  • 过滤请求的参数
  • 决定是否启用站点维护模式
  • 响应前后做一些必要的操作

要创建中间件,请执行以下过程:

  • 使用 artisan 命令创建中间件 php artisan make:middleware中间件名
  • 在app→Http文件夹中的 kernel.php 中注册中间件
  • 在创建的中间件中编写逻辑
  • 将中间件分配给路由或控制器
常用中间件

Laravel 自带了一些中间件,包括身份验证、CSRF 保护等。Laravel 具体启用了哪些中间件,可通过 app\Http\Kernel.php 文件查看。对于以 \App\Http\Middleware\ 开头的中间件(位于 app/Http/Middleware 目录)是我们可以对其行为进行定制的中间件。

1、Authenticate 中间件

源文件:app\Http\Middleware\Http\Middleware\Authenticate.php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    
    
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     */
    protected function redirectTo($request)
    {
    
    
        if (! $request->expectsJson()) {
    
    
            return route('login');
        }
    }
}

作用:
用户身份验证。可修改 redirectTo 方法,返回未经身份验证的用户应该重定向到的路径。

2、CheckForMaintenanceMode 中间件

源文件 :app\Http\Middleware\CheckForMaintenanceMode.php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;

class CheckForMaintenanceMode extends Middleware
{
    
    
    /**
     * The URIs that should be reachable while maintenance mode is enabled.
     *
     * @var array
     */
    protected $except = [
        //
    ];
}

作用:
检测项目是否处于 维护模式。可通过 $except 数组属性设置在维护模式下仍能访问的网址。

3、EncryptCookies 中间件

源文件:app\Http\Middleware\EncryptCookies.php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    
    
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        //
    ];
}

作用:
对 Cookie 进行加解密处理与验证。可通过 $except 数组属性设置不做加密处理的 cookie。

4、RedirectIfAuthenticated 中间件

源文件:app\Http\Middleware\RedirectIfAuthenticated.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    
    
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
    
    
        if (Auth::guard($guard)->check()) {
    
    
            return redirect('/home');
        }
        return $next($request);
    }
}

作用:
当请求页是 注册、登录、忘记密码 时,检测用户是否已经登录,如果已经登录,那么就重定向到首页,如果没有就打开相应界面。可以在 handle 方法中定制重定向到的路径。

5、TrimStrings 中间件

源文件:app\Http\Middleware\TrimStrings.php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;

class TrimStrings extends Middleware
{
    
    
    /**
     * The names of the attributes that should not be trimmed.
     *
     * @var array
     */
    protected $except = [
        'password',
        'password_confirmation',
    ];
}

作用:
对请求参数内容进行 前后空白字符清理。可通过 $except 数组属性设置不做处理的参数。

6、TrustProxies 中间件

源文件:app\Http\Middleware\TrustProxies.php

namespace App\Http\Middleware;

use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;

class TrustProxies extends Middleware
{
    
    
    /**
     * The trusted proxies for this application.
     *
     * @var array|string
     */
    protected $proxies;
    
    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_ALL;
}

作用:
配置可信代理。可通过 p r o x i e s 属性设置可信代理列表, proxies 属性设置可信代理列表, proxies属性设置可信代理列表,headers 属性设置用来检测代理的 HTTP 头字段。

7、VerifyCsrfToken 中间件

源文件:app\Http\Middleware\VerifyCsrfToken.php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    
    
    /**
     * Indicates whether the XSRF-TOKEN cookie should be set on the response.
     *
     * @var bool
     */
     
    protected $addHttpCookie = true;
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */

    protected $except = [
        //
    ];
}

作用:
验证请求里的令牌是否与存储在会话中令牌匹配。可通过 $except 数组属性设置不做 CSRF 验证的网址。

五、composer常用扩展包

guzzle/guzzle HTTP 请求套件,需要获取其它 url 的数据可用
predis/predis Redis 扩展
intervention/image 图片剪裁处理
overtrue/easy-sms API 短信服务商,支持多家接入
gregwar/captcha 图片验证码

barryvdh/laravel-debugbar 页面调试工具
doctrine/dbal 数据库修改必须使用
overtrue/pinyin 中文转拼音工具
dingo/api 接口开发工具(似乎当前原版更好用了)
Horizon 队列任务
mewebstudio/Purifier XSS 输入攻击过滤
Passport API 授权认证方案
doctrine/dbal 修改数据库要用的扩展
jpush/jpush 极光推送
overtrue/laravel-query-logger 输出日志,api 有效(作用必要性不明,后面查看)

六、laravel有那些特点?

回答一:
1.强大的rest router:用简单的回调函数就可以调用,快速绑定controller和router
2.artisan:命令行工具,很多手动的工作都自动化
3.可继承的模板,简化view的开发和管理
4.blade模板:渲染速度更快
5.ORM操作数据库
6.migration:管理数据库和版本控制
7.测试功能也很强大
8.composer也是亮点
回答二:
laravel框架引入了门面,依赖注入,Ioc模式,以及各种各样的设计模式等

七、生命周期

Laravel的生命周期开始于 public/index.php,结束于 public/index.php。

客户端的所有请求都经由Web服务器引导到这个文件中。

以下是public/index.php 文件的源码和注释:

#public/index.php 文件

// 定义了laravel一个请求的开始时间
define('LARAVEL_START', microtime(true));

// composer自动加载机制,加载项目依赖
require __DIR__.'/../vendor/autoload.php';

// 这句话你就可以理解laravel,在最开始引入了一个ioc容器。
$app = require_once __DIR__.'/../bootstrap/app.php';

// 这个相当于我们创建了Kernel::class的服务提供者
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 获取一个 Request ,返回一个 Response。以把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应
// 处理请求
$response = $kernel->handle(
    // 创建请求实例
    $request = Illuminate\Http\Request::capture()
);

// 发送响应,就是把我们服务器的结果返回给浏览器。
$response->send();

// 终止程序,这个就是执行我们比较耗时的请求,
$kernel->terminate($request, $response);

由上可知 Laravel 的生命周期分为以下3个主要阶段:

  • 加载项目依赖。
  • 创建Laravel应用实例
  • 接收请求并响应。

接下来我们根据 public/index.php 文件,开始 Laravel 的生命周期之旅吧!

1、Composer 自动加载项目依赖
// composer自动加载机制
require __DIR__.'/../vendor/autoload.php';

Composer 是 PHP 的包管理器,用来管理项目依赖的工具,autoload.php 中引入了 Composer 自动生成的加载程序,实现加载第三方依赖。

autoload.php 文件代码:

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit101671ca9bbc2f62f8335eb842637291::getLoader();
2、创建应用实例
$app = require_once __DIR__.'/../bootstrap/app.php';

bootstrap/app.php 文件代码:

// 创建Laravel应用的ioc容器,
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
// 完成内核的绑定
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);
// 返回实例
return $app;

如果你理解了 Ioc(控制反转),那么对于以上代码你一定很熟悉。

Ioc 容器(服务容器) 是 Laravel 的核心,这一阶段主要实现了 2大功能:
1、创建容器
2、绑定内核

创建容器
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

我们进入 Illuminate\Foundation\Application 容器中,以下是构造函数的分析:

/**
     * Create a new Illuminate application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
    
    
        if ($basePath) {
    
    
            // 1、路径绑定
            $this->setBasePath($basePath);
        }
		// 2、基础绑定
        $this->registerBaseBindings();
        // 3、基础服务提供者绑定(事件,日志,路由)
        $this->registerBaseServiceProviders();
        // 4、核心别名绑定,目的是给核心的类命名空间设置别名,以便后续使用
        $this->registerCoreContainerAliases();
    }
绑定内核
// 完成内核的绑定
// HTTP内核
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// Console内核
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
// 绑定异常处理
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

在 Laravel 中只要是通过 public/index.php 来启动框架的,都会用到 Http Kernel(主要作用就是接受请求并返回响应),而其他的例如通过 artisan 命令、计划任务、队列等启动框架进行处理的都会用到 Console 内核。

其中 HTTP 内核类继承 Illuminate\Foundation\Http\Kernel ,HTTP内核类中定义了中间件相关数组,中间件主要是提供了一种方便机制用来过滤进入应用的请求和处理HTTP响应。

HTTP 内核类:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    
    
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

    /**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

HTTP内核类的父类:Illuminate\Foundation\Http\Kernel 提供了名为 $bootstrappers 的引导程序数组,包括了环境检测、加载配置、处理异常、Facades注册、服务提供者注册、启动服务这6个 程序。

/**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];

Console 内核:

一个健壮的应用程序除了满足网络请求外,还应该包括执行计划任务、异步队列等,Laravel中通过artisan工具定义各种命令来完成非 HTTP 请求的各种场景。artisan命令通过 Laravel 的 Console 内核完成对应用核心组件的调度来完成任务。

Console 内核在这不做详细介绍。

绑定异常处理

异常处理由 App\Exceptions\Handler 类完成,所有的异常处理都由其处理,在这里同样不做详细介绍。

3、接收请求并响应
解析内核
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

这里将 HTTP 内核解析出来,我们进一步查看Illuminate\Contracts\Http\Kernel::class 的内部代码,

构造函数中注入了app容器和路由器2个函数,在实例化内核的过程中,将内核中定义的中间件注册到路由器中,注册完成后便可以处理HTTP请求前调用中间件 实现过滤请求的目的。

Illuminate\Foundation\Http\Kernel 的构造函数:

 /**
     * Create a new HTTP kernel instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Routing\Router  $router
     * @return void
     */
    public function __construct(Application $app, Router $router)
    {
    
    
        $this->app = $app;
        $this->router = $router;

        $router->middlewarePriority = $this->middlewarePriority;

        foreach ($this->middlewareGroups as $key => $middleware) {
    
    
            $router->middlewareGroup($key, $middleware);
        }

        foreach ($this->routeMiddleware as $key => $middleware) {
    
    
            $router->aliasMiddleware($key, $middleware);
        }
    }
// Illuminate\Routing\Router 类中

/**
     * Register a group of middleware.
     *
     * @param  string  $name
     * @param  array  $middleware
     * @return $this
     */
    public function middlewareGroup($name, array $middleware)
    {
    
    
        $this->middlewareGroups[$name] = $middleware;

        return $this;
    }

/**
     * Register a short-hand name for a middleware.
     *
     * @param  string  $name
     * @param  string  $class
     * @return $this
     */
    public function aliasMiddleware($name, $class)
    {
    
    
        $this->middleware[$name] = $class;

        return $this;
    }
处理 HTTP 请求
// handle 方法处理请求
$response = $kernel->handle(
    // 创建请求实例
    $request = Illuminate\Http\Request::capture()
);

创建请求实例:在处理请求过程之前会通过 Illuminate\Http\Request 的 capture 方法,以进入应用的HTTP请求信息为基础,创建出一个 Laravel Request请求实例,在后续应用剩余的生命周期中Request请求实例就是对本次HTTP请求的抽象。

/**
     * Create a new Illuminate HTTP request from server variables.
     *
     * @return static
     */
    public static function capture()
    {
    
    
        static::enableHttpMethodParameterOverride();

        return static::createFromBase(SymfonyRequest::createFromGlobals());
    }
 /**
     * Creates a new request with values from PHP's super globals.
     *
     * @return static
     */
    public static function createFromGlobals()
    {
    
    
        $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

        if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
            && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
        ) {
    
    
            parse_str($request->getContent(), $data);
            $request->request = new ParameterBag($data);
        }

        return $request;
    }

handle 处理请求:将 HTTP 请求抽象成 Laravel Request 请求实例后,请求实例会被传导进入到HTTP内核的 handle 方法内部,请求的处理就是由 handle 方法来完成的。

namespace Illuminate\Foundation\Http;

class Kernel implements KernelContract
{
    
    
    /**
     * Handle an incoming HTTP request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function handle($request)
    {
    
    
        try {
    
    
            $request->enableHttpMethodParameterOverride();
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
    
    
            $this->reportException($e);
            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
    
    
            $this->reportException($e = new FatalThrowableError($e));
            $response = $this->renderException($request, $e);
        }
        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );
        return $response;
    }
}

handle 方法接收了一个请求,并在最后返回了一个 HTTP响应。

接下来进入 sendRequestThroughRouter 方法,通过中间件/路由器发送给定的请求。

该方法的程序执行如下:

1、将 request 请求实例注册到 app 容器当中
2、清除之前的 request 实例缓存
3、启动引导程序:加载内核中定义的引导程序来引导启动应用
4、将请求发送到路由:通过 Pipeline 对象传输 HTTP请求对象流经框架中定义的HTTP中间件和路由器来完成过滤请求,最终将请求传递给处理程序(控制器方法或者路由中的闭包)由处理程序返回相应的响应。

/**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
    
    
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

bootstrap 引导程序

/**
     * Bootstrap the application for HTTP requests.
     *
     * @return void
     */
    public function bootstrap()
    {
    
    
        if (! $this->app->hasBeenBootstrapped()) {
    
    
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
/**
     * Get the bootstrap classes for the application.
     *
     * @return array
     */
    protected function bootstrappers()
    {
    
    
        return $this->bootstrappers;
    }

/**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
/*引导启动Laravel应用程序
1. DetectEnvironment  检查环境
2. LoadConfiguration  加载应用配置
3. ConfigureLogging   配置日至
4. HandleException    注册异常处理的Handler
5. RegisterFacades    注册Facades 
6. RegisterProviders  注册Providers 
7. BootProviders      启动Providers
*/

关于引导程序的启动原理,有时间我们再抽出来看看。

发送响应
$response->send();

经过了以上阶段,终于获取到了我们想要的数据,接下来是将数据响应到客户端。

程序由在 Illuminate\Http\Response 内部由其父类 Symfony\Component\HttpFoundation\Response 的 send() 方法实现。

/**
     * Sends HTTP headers and content.
     *
     * @return $this
     */
    public function send()
    {
    
    
        $this->sendHeaders(); // 发送头部信息
        $this->sendContent(); // 发送报文主题

        if (\function_exists('fastcgi_finish_request')) {
    
    
            fastcgi_finish_request();
        } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
    
    
            static::closeOutputBuffers(0, true);
        }
        return $this;
    }
4、终止应用程序

该过程调用终止中间件。

响应完成后,HTTP 内核会调用 terminate 中间件做一些后续的处理工作。比如,Laravel 内置的 session 中间件会在响应发送到浏览器之后将会话数据写入存储器中。

HTTP 内核的 terminate 方法会调用 terminate 中间件的 terminate 方法,调用完成后,整个生命周期结束。

k e r n e l − > t e r m i n a t e ( kernel->terminate( kernel>terminate(request, $response);

在这里插入代码片
/**
     * Call the terminate method on any terminable middleware.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
    
    
        $this->terminateMiddleware($request, $response);

        $this->app->terminate();
    }
/**
     * Call the terminate method on any terminable middleware.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */
    protected function terminateMiddleware($request, $response)
    {
    
    
        $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
            $this->gatherRouteMiddleware($request),
            $this->middleware
        );

        foreach ($middlewares as $middleware) {
    
    
            if (! is_string($middleware)) {
    
    
                continue;
            }

            [$name] = $this->parseMiddleware($middleware);

            $instance = $this->app->make($name);

            if (method_exists($instance, 'terminate')) {
    
    
                $instance->terminate($request, $response);
            }
        }
    }
5、总结

在Laravel 的整个生命周期中,加载项目依赖、创建应用实例、接收并响应请求,终止程序,内核都起到了串联作用。

首先,创建 Laravel 应用程序 阶段时,包含了注册项目基础服务、注册项目服务提供者别名、注册目录路径、异常类绑定等工作,同时在HTTP 内核中配置了引导程序。
接着,在 接收请求并响应 阶段时,会根据运行的环境解析出 HTTP 内核或 Console 内核。在 HTTP 内核中把中间件注册到路由器中。
然后,处理请求阶段的过程中,将请求的实例注册到app容器中,通过引导程序启动应用,最后发送请求到路由。
之后,通过Response类响应数据。
最后,通过terminate 方法终止程序。

八、ORM

ORM,全称 Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在操作具体的业务对象时,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法即可。

ORM 实现方式

两种最常见的实现方式是 ActiveRecord 和 DataMapper (laravel 中使用的是前者)
ActiveRecord(非常流行) 中模型与数据表一一对应,
DataMapper 中模型与数据表是完全分离的。

Laravel 的 Eloquent ORM 使用 ActiveRecord 实现方式,每一个 Eloquent 模型类对应着数据库中的一张表,我们通过调用模型类的相应方法实现对数据库的增删改查。原理:在Illuminate\Database\Eloquent\Model 中通过对 __call 和 __callStatic 两个魔术方法的应用,最终实现通过查询构造器操作数据库,Eloquent ORM 实际上就是对查询构造器进行了一次封装。

Eloquent ORM
模型检索
  • 每个 Eloquent 模型想象成一个强大的 查询构造器
  • 集合:通过all 和 get
    等方法获取的结果都是一个集合(Illuminate\Database\Eloquent\Collection)对象实例,Eloquent
    的集合对象继承了 Laravel Base Collection(Illuminate\Support\Collection)
    ,因此它自然也继承了数十种能优雅地处理 Eloquent
    模型底层数组的方法,这些方法可以处理Eloquent集合对象中的模型实例(模型实例就是一条条数据,也是一条条数据组成了集合)
  • 结果分块:在处理大型结果集时,使用 chunk 方法可以节省内存
    高级子查询:addSelect 和 orderByDesc
插入及更新模型
  • save:需要先创建模型实例,给实例设置属性,然后调用 save 方法,会自动更新create_at 或 update_at,批量更新则需要使用 update,而且不会更新create_at 或 update_at,相应的模型事件也不会触发
  • 其他创建方法:firstOrCreate/ firstOrNew / updateOrCreate
删除模型
  • destroy和delete
  • 软删除:通过 use SoftDeletes 启用
查询作用域

创建和设置指定查询条件,可以在model选择开启使用,则该查询条件会作用到该model中查询语句上,分全局作用域、局部作用域和动态作用域,withoutGlobalScope可以使作用域失效

猜你喜欢

转载自blog.csdn.net/LYidiandian/article/details/124152389