参考 Laravel 版本为 5.4

我理解的PHP框架

  • 框架最核心的就是解决了路由 (Router)
  • 附带有 MVC/ORM/Helper/Template/Middleware 等特性

Laravel 是什么

Laravel 就是一个大的 Container, 里面装了基本上所有我们需要的东西, 如果不够, 还可以自己往里塞

Laravel 里面默认都塞了什么? 这里有介绍, 比如前面提到的我认为的最核心的路由

核心

前面写 Laravel 是一个大的 Container, 下面是一个最简陋的山寨

<?php

class Container {
    protected $instances = [];
    protected $bindings = [];

    public function bind($abstract, Closure $concrete) {
        $this->bindings[$abstract] = $concrete;
    }

    public function make($abstract) {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        $object = $this->build($this->bindings[$abstract]);
        $this->instances[$abstract] = $object;

        return $object;
    }

    public function build(Closure $concrete) {
        return $concrete($this);
    }
}

这个 class 只有三个成员函数:

  • bind 负责将实例化方法注册到 Container
  • build 负责实例化
  • make 负责返回对象

下面是使用的例子

<?php
class Foo {
}

$c = new Container();
$c->bind('foo', function($container) {
    return new Foo();
});

$f = $c->make('foo');

print_r($f);

/* 将会打印
Foo Object
(
)
*/

完全版本的 Container 参考 Illuminate\Container\Container

Service Provider

Service Provider 就是配置使用 Container 的一个实现, 基本上相当于配置了在什么时机调用 bind (singleton/instance等) 函数来绑定, 更细节的 boot/$defer/register/provides 参考文档即可

Facade

Facade 是便捷访问 Container 中资源的一种方式, 使用起来像是在使用一个类的静态方法, 但其实是通到了 Laravel 内部, 有 Facade 之后的使用方式就是简单的调用

<?php
use Foo\Bar;

function test() {
  Bar::baz();
}

自定义 Facade 的方法

<?php

namespace Foo;

use Illuminate\Support\Facades\Facade;

class Bar extends Facade
{
    protected static function getFacadeAccessor() {
        return 'router';
    }
}

这样我们使用 Foo\Bar 这个类的时候就相当于调用 app('router') ( 抛开 mock 特性 )

整个 Facade 的核心就是 Illuminate\Support\Facades\Facade 中的 __callStatic 函数, 此函数通过 getFacadeAccessor 获取到我们定制的 alias, 再从 Laravel 这个 Container 中通过 ArrayAccess 的 offsetGet 这个特性来间接的调用到了 make 函数, 整条路就打通了.

所以这里我们用 Foo\Bar 就和用 Router 是一个效果, Laravel 的路由 Facade 就是这么来的, 如果想看具体实现的类, 只需要查找 bind 或者 alias 的地方就能找到.

初始化流程

  • 入口 public/index.php
  • 实例化一个 Illuminate\Foundation\Application 为 $app 变量 (这里包含额外的一些初始化)
  • App 命名空间下的 Http\Kernel, Console\Kernel , Exceptions\Handler 注册为单例
  • 实例化 Http\Kernel, 调用 handle 函数
  • 调用 sendRequestThroughRouter , 进行关键初始化
    • DetectEnvironment
    • LoadConfiguration
    • ConfigureLogging
    • HandleExceptions
    • RegisterFacades -初始化内置的 Facade (AliasLoader 具体实现, 依靠 spl_autoload_register 和 class_alias)
    • RegisterProviders -初始化内置 Service Provider (Application 此处区分实例化时机)
    • BootProviders -调用 Service Provider 的 boot 函数
  • 通过 Pipeline 将 request 用 middleware 虑一遍, 交给 dispatchToRouter 返回的 Closure 处理

一些常用模块

路由

有了路由, 其实我们就可以完成一个网站了, 不需要其他的东西, 比如

Route::get('/', function() {
  return '<html>blah...</html>';
});

和我们了解的 Http 对应, 路由支持 GET/POST/PUT/PATCH/DELETE , 这些都对应小写的函数, 当然还有 resource 这个函数一站解决 RESTful 风格的接口

Middleware

  • 编写

    • 最重要的函数就是 handle. 放行就 return $next($request);, 否则就做其他操作.
    • 可以把 $response = $next($request); 暂存, 做完想做的操作再 return $response;
  • 注册和使用

    • Router 中, ( app/Http/Kernel.php$routeMiddleware 配置了可用的 middleware )

      Route::get('baz', 'FooController@bar')->middleware('auth');
      
    • Controller 中

      <?php
      
      class FooController extends Controller
      {
          public function __construct() {
              $this->middleware('auth');
              $this->middleware('log')->only('index');
              $this->middleware('subscribed')->except('store');
          }
      }
      
    • 整个路由规则中

      app/Http/Kernel.php 中的 $middlewareGroups 配置了对应路由文件使用的 middleware, 可见 webapi 使用的有区别, api 路由使用的很少.

      如果 api 路由需要识别 cookie, 使用 session, 添加 \App\Http\Middleware\EncryptCookies::class,\Illuminate\Session\Middleware\StartSession::class 即可

ORM

  • 基本使用

    • $table/$timestamps/$connection 等参考文档
    • $fillable - 定义使用的字段, 在使用 create 插入数据的时候会进行过滤
    • $casts - 不局限于数据库类型, 可以对 object 的字段自定义类型, 平时用的比较多的就是将 json 字符串 cast 成 array
    • $hidden - get 的时候不包含的字段, 提供 api 时尤其有用
    • setXxxAttribute - 用于设置属性时的特殊处理, 同等的 getXxxAttribute 是获取处理, ‘Xxx’ 省略的时候是全局处理
    • scopeXxx - 用来比较优雅的添加限定函数, 用来修饰查询, 使查询更直观
    • 更多的定制可以重写成员函数进行, 平时重写比较多的有 boot/create/destory
  • Relationship

    • hasOne/belongsTo/hasMany/belongsToMany/hasManyThrough 等参考文档

    • 查询时携带多重关系 Foo::with('bar.baz.tmp')->where(...)

    • 查询时关联条件

      <?php
      $posts = Post::whereHas('comments', function ($query) {
          $query->where('content', 'like', 'foo%');
      })->get();
      
  • Collections

    • 基类提供的函数可参考文档

    • first - 可以传入一个 callable 和一个 default (源码), 所以为了避免一串链式的查询之后结果为空再直接取属性的时候有 Trying to get property of non-object 这个异常, 可以这样使用

      <?php
      
      function getFoobar($default) {
          return Foo::where('id', $id)->get()->first(null, new Foo(['bar'=>$default]))->bar;
      }
      
    • 如果有特殊需求, 可以在 Model 中使用自定义的 Collection, 参考文档

本文地址 Laravel 的一些总结 转载请注明出处