单位内分享 Laravel 的草稿

说明: 本人也是PHP学习者 . 因工作需要要与单位同事分享关于laravel框架的认识 , 因此基于自己的学习和理解 , 仓促写了这个草稿
虽然把这个草稿拿出来与其它laravel初学者分享 , 但文中许多知识点作者本人也是一知半解 .
若存在谬误 , 请轻喷 , 多鼓励 , 欢迎指教 .

1. 关于laravel的概述

laravel是一款免费 , 开源的 , 基于PHP的MVC全栈式WEB开发框架 . 发明者是Taylor Otwell . laravel在设计理念上借鉴了ROR等先进的web开发框架 , 采用了PHP开发的各种先进理念和模式 ; 由于它的先进和功能强大 , 使之特别适合快速开发原型以及敏捷地迭代 , 因而迅速流行起来.

1.1 laravel的历史

  • laravel 1 诞生于2011年
  • laravel的流行版本laravel 4 发布于2013年 , 框架基本定型
  • laravel在2015年发布了5版本 , 加入了许多重大修改 , 并宣布进入LTS(长期维护) , 正式成为稳定版本并在继续迭代

1.2 laravel的流行情况

2015年SitePoint 关于PHP框架流程程度的调查结果中 , Laravel无论是在团队项目还是个人项目 , 无论是国家(不包括中国)或是年龄层次 , 都是最流行的一款框架.

相关链接

1.3 laravel的基本特点

  • 使用PHP新版本的最新特性 , 除了语法糖之外 , 还大大增加了代码复用性 , 扩展性 . 目前要求PHP 5.5.9以上版本 , 并支持PHP7
  • 项目基于PHP最先进的包管理工具composer构建
  • 大量使用了成熟的第三方依赖 , 如symfony , monolog等.
  • 完全使用组件式开发 , 高度灵活的文件结构 , 可以按需使用组件重构出自己的项目
  • 基于面向接口编程 , 并广泛使用依赖注入 , 使之非常适合敏捷开发的高速迭代
  • 支持迁移 , 命令行工具 , 反向路由 , 中间件 , RESTFul 等web开发先进理念
  • 特别强大的 Eloquent ORM , 对各种关联查询实现得非常全面
  • 作为全栈式框架 , 实现了队列 , 事件机制 , 异步任务 , 广播 , 统一的计划任务等各种先进功能
  • 内置登录 ,权限验证等常用功能
  • 使用了 gulp , node 等先进Web工具进行资源管理
  • 框架本身通过闭包 , 依赖注入 , IoC容器等手段 , 能够很灵活地扩展基本功能
  • 能够快速开发和植入扩展包 , 只需要composer和几条命令行
  • 真正的测试驱动 , 绝大多数底层功能为实现单元测试而构建
  • 默认支持 git , phpunit 等相关开发技术

2. laravel与composer的使用

laravel是基于composer构建的 . 因此使用 laravel 也需要了解 composer .

2.1 laravel 对 composer 的依赖

  1. laravel使用composer 进行安装和升级
  2. laravel使用composer 托管所有文件的autoload
  3. laravel使用composer 托管所有的第三方依赖

2.2 composer 是什么

composer 是PHP最先进的包管理工具 , 诞生至今已经五年 , 终于发布了 v1.0版本 .

在composer之前 , PHP是没有流行的包管理工具的 , 而官方对PHP功能的支持还停留在pecl的扩展层面 . 因此PHP的开发难免要重复造轮子 , 对第三方包的引入也不如 Ruby 等web开发语言便利.

2.3 composer的主要功能

2.3.1 托管PHP文件的autoload

可以用各种方式(文件夹,个别文件,PSR-4规范 文件路径与类的命名空间相对应)托管项目的PHP类文件(包括普通文件) , 会把这些文件的类名和路径的映射匹配起来 , 自动生成autoload.php 作为缓存文件 . 在项目启动时引入这个autoload文件 , 就会自动通过php的autoload机制注册这些映射 .

包括TP5 在内的新框架基本都用composer托管autoload .

2.3.2 直接引入第三方依赖.

代码在github上开源 , 并且发布到composer的包管理网站packagist的第三方依赖包 , 可以通过在项目中配置一个composer.json文件 , 然后用一条命令行 composer update 自动引入项目 .

composer会把第三方依赖下载到本地vendor文件夹 , 并加入autoload中 . 当然 , 引入一个依赖包的同时也会引入它自己的所有依赖 . 然后可以在项目任何地方直接调用 , 不需要再麻烦地 require 或者 import .

composer引入的第三方依赖还可以通过 -dev 等标记来区分 , 有些依赖可以只在开发环境中部署(例如在浏览器console中显示debug) , 线上并不使用 , 从而便捷地提高开发效率 .

composer引入第三方依赖 , 还可以用多种方式来指定依赖版本 , 既可以选择不断更新该依赖包的最新版本 , 也可以确定选择某一个版本 , 从而避免依赖版本迭代引发的未知问题.

2.3.3 发布自己的依赖包

使用composer , 配置好文件后 , 也可以把自己开发的包发布到packagist上 , 既可以供其它开发者分享使用 , 也可以在自己的其它项目中引用 , 从而对这些包进行稳定地迭代 . 避免简单的复制粘贴 , 在不同项目中改动导致的版本冲突问题 .

2.4 使用composer的几个要求和弊端

  • 必须使用git
  • 由于GFW的存在 , composer有时会很慢
  • 使用第三方依赖 , 在不用重复造轮子的同时 , 也会遇到出了问题不方便修改的情况
  • 对第三方依赖的版本不加约束 , 在升级依赖时可能会遇到未知问题
  • 线上部署使用了第三方依赖的项目时 , 需要用脚本管理第三方依赖的安装和升级 . 否则要对第三方依赖库也建git仓库
  • 流行的第三方依赖往往很重 , 实现的功能超过有限需求 , 引入项目后会让人感觉不能完全控制它们

这些问题应根据项目的实际情况进行取舍 . 但无论如何 , 用composer来托管项目的autoload是非常便利的 .

3. laravel框架的先进web开发理念

laravel之所以能迅速流行起来 , 也是因为它实现了整个WEB开发领域的诸多先进思想 , 使之成为PHP领域中走在WEB开发最前端的一款框架 .

PHP一直是最流行的Web开发语言 , 但早些年ROR等先进Web框架的流行 , 使得PHP在很多方面都落于人后 . 有了laravel后来居上 , 再加上PHP本身的流行性 , 庞大的社区以及不断进步的新特性 , 使得PHP在国外Web开发领域的统治力又恢复了许多 .

下面介绍laravel实现的一些Web理念.

3.1 完全基于composer的包管理和文件管理

laravel是较早使用composer托管autoload , 引入第三方依赖以及开发扩展 的一款PHP框架 . 这种做法目前是Ruby , Python , Node 等Web开发语言的标配 , 也已经成为所有现代PHP框架的共识 .

这也是建立在PHP自身发展的基础上 , 过去需要用 .class.php 等方式加载类文件 , 要手动设计 import等方法 , 还要规避无处不在的命名冲突 ; 后来PHP自己发展了autoload 机制 , 引入了namespace , 才使得包管理变得可行 .

3.2 组件式开发

传统的WEB框架是作为一个整体来设计的 . 内部各种功能相互间避免不了高耦合 .

laravel本身所有功能被拆分成了一个个的 Illuminate 系列组件 , 都通过composer引入项目 . 这些组件相互之间存在依赖关系 , 引入一个组件时 , composer也会自动引入所有相关依赖 .

而作为框架来使用的laravel , 本身是composer的一个项目 laravel/framework , 它只是提供了额外的文件架构 , 把自己依赖的所有Illuminate组件和第三方依赖组织起来 , 成为一个项目框架 .

可见laravel很好地学习了symfony框架在架构上的灵活性 , 并且它自己的底层逻辑也普遍依赖symfony .

所以laravel不仅可以作为一个完整框架使用 , 还可以自己设计一套项目架构 , 再把laravel的功能组件引入进来 , 从而获得laravel的各种功能.

3.3 测试驱动

在现代敏捷开发中 , 单元测试成为非常重要的一环 . 虽然单元测试会增加初始开发环节的代码量 , 但在后期迭代中涉及初始功能改动 , 没有单元测试时效率要差许多倍.

好的单元测试 , 不仅能对独立的功能模块进行测试 , 还可以替换掉该模块依赖的其它功能 . 例如使用假的model 提供单元测试所需的数据 , 而不需要调用真正的数据库 .

未来单元测试还会继承自动化测试用例的功能 , 可以用测试脚本直接对web页面 , api接口进行测试 .

这些都要求PHP开发框架在底层就对单元测试进行支持 . laravel 4 , 5 在这方面做了很大的努力 . 它最基本的功能 , 包括Facade(公共功能的接口)和Model 等, 都可以用很简单的方法实现mock(用假功能替代) . 而且内置了对web页面 , api接口进行测试的方法 .

所以laravel是基于测试驱动理念设计的一款现代框架.

3.4 基于bootstrap的启动流程

传统的Web框架 , 例如国产的thinkPHP , 启动代码是一步步写死的 , 还会打包成Runtime 以提高效率 . 这么做的缺点是启动流程高度耦合 , 当要在流程中嵌入一些加载逻辑 , 测试逻辑 , 默认功能 , 第三方依赖等等 , 都难免遇到修改框架代码的窘境 . 使得这些框架的扩展性差 , 有时远不如自己开发框架灵活 .

laravel把框架的功能拆分成独立的组件 , 然后有个独立的bootstrap文件夹 , 该文件夹中把启动流程拆分 , 写到了多个php文件中 . 在引导的index.php里按顺序引入这些bootstrap文件 , 就能实现不同的项目运行环境 .

laravel自己就是这么分离http请求和console命令行请求的 . laravel衍生的高性能轻框架 lumen 也是用了独立的bootstrap , 使之运行效率超过笨重的母框架.

所以 , 基于laravel的组件式开发 , 我们开发者可以在项目中按自己的意愿任意组织bootstrap文件 , 从而创建多个启动流程 , 各自对应不同的app运行环境 , 有独立的配置文件和功能文件 , 又共享项目的其它文件 .

laravel这种启动流程是非常灵活的 , 很好地适应了 web开发从 pc浏览器往多端多环境开发 , api开发的转变方向 .

3.5 基于env的基础配置

传统的php框架通常使用.php 文件来配置项目 . 这也导致一些不够严谨的团队 , 在开发环境的配置文件难免泄露线上环境的秘密信息 . 或是开发环境需要刻意模拟线上环境 .

laravel 使用了DotEnv(第三方依赖) 的思路来托管配置 . 简单来说 , 配置文件仍然写在 Config文件夹下的.php文件中 , 但许多参数都可以调用 env() 方法获取环境变量来配置参数 .

而这些环境变量 , 是在启动过程中直接读取一个隐藏的.env 文件 , 将配置写入PHP运行环境的 . 这样线上和本地可以配置完全不同的.env 文件 , 却使用相同的 Config.php 文件 , 从而既可以避免泄露线上环境的信息 , 又免去了配置文件繁琐的版本控制和冲突 .

3.6 面向接口编程

laravel 广泛地使用了PHP 的 interface , 以实现面向接口编程 . 这也成为laravel最核心的开发理念之一.

PHP用interface实现了面向接口编程 . 定义一个function或者method时 , 传入的参数可以约束为一个interface , 例如 " UserInterface " . 而实际传入的参数是一个实例化的类 , 这个类必须 implements " UserInterface " .

简单来说 , interface 好比餐馆菜单上的菜名 , 这个菜名定义了这道菜的食材和口味 , 我点菜时完全不用管厨师是谁 , 食材从哪里购买 , 也能预知它的口味和吃法 . 从而把功能和实现解耦了.

这么做的好处 , 举一个例子 , 我们要定义一个BookService , 它通过BookInterface约束 , 必须实现 getIndex , getPage , getAuthor 三个方法 . 开发初期 , 我们可以写一个假的FakeBookService 类 , 三个方法都直接返回写好的结果 . 这个service 实现了它的 interface . 我们把这个假的service 作为参数传给自己要开发的方法 , 一切也是可以运行的 .

我们自己 , 或者其他人 , 未来把这个BookService真正开发完了 , 只要把新的service实例 作为参数传给原来的方法 , 什么都不用改 , 一切代码都可以继续运行 . 因为我们只调用了interface约束好的 getIndex , getPage , getAuthor 三个方法 , 实际传入的实例都是可替换的!

面向接口编程的好处就提现出来:

  • interface 本身就是很好的类文档
  • 通过interface , 可以在实现之前定义功能 , 用OOP思路把类更加组件化
  • 通过interface , 可以让不同的开发者异步开发一个复杂功能的不同组件
  • 通过interface , 可以自由替换任何一个功能的实例

laravel普遍实现了面向接口编程 . 它的所有基本功能 , 都定义了interface , 并且在别的类使用这些功能时 , 也是用interface来约束传入参数的 . 至于如何替换传入的参数 , 则要提到laravel另一个核心理念依赖注入 .

3.7 依赖注入

依赖注入的理念基于两个需求而产生 . 一个需求是解决类的相互依赖 , 一个需求是类在不同场景下的实例化 .

简单的OOP开发中 , 会把一个功能模块做成一个类 . 例如:

class 刀 {

    public function 砍();

    public function 削();

    public function 拍();
}

但实际开发中 , 复杂的功能模块是会相互依赖的 , 例如:

class 刀 {

    public function __construct(刀柄 $刀柄 ,刀身 $刀身 , 刀锋 $刀锋)
}

同时 , 类在场景下的实例化 , 往往要通过几行 new 的代码, 例如:

$刀 = new 刀(new 木质刀柄() , new 铁质刀身() , new 钢制刀锋) ;

如果项目中一百个地方用到了$刀 , 上面这行代码就会出现在一百个地方 . 一旦$刀 想要升级 , 不再用 $铁质刀身 , 而用$钢制刀身 了, 需要修改多少个地方呢 ? 每个地方的代码也可能写法不一样 . 要分别修改一百个地方 , 不能有遗漏 , 还要保证代码的可运行 , 是存在各种风险的.

为解决这种问题 , PHP通常使用工厂模式 , 就是把类的相互依赖关系封装到工厂方法中 , 然后通过工厂方法来获取所需的实例.

laravel在全局使用IoC容器(依赖注入容器) , 相当于一个全局的大工厂 . 它通过专门的Provider类 , 可以把所有构建依赖关系的工厂方法 , 作为闭包或别的形式映射到指定的字符串 , 然后在启动时注册到IoC容器中 . 这个字符串可以是接口的类名 , 也可以是任何字符串(例如 'logger' ) . 然后在项目的任何位置 , 都可以使用IoC容器 , 通过这个字符串直接获取工厂方法的结果 . 例如

$logger = app()->make('logger'); // app() 方法可以直接调用全局的IoC容器

//或者
$logger = app()->make(LoggerInterface::class); //注册的字符串为接口类名 , 从而实现面向接口编程

//实际调用的可能是注册到IoC容器中的闭包 , 例如:

function($app) {
    $logger = new Logger($app->make('Queue') , $app->make('EventBus'));
    return $logger;
}

laravel的IoC容器不仅可以通过工厂方法生成实例 , 还可以便捷地将之单例化 , 全局化 , 或是映射为Facade 等等 . 提供了诸多的便利性 .

更强大的一点在于 , laravel的许多类 , 比如controller的方法 , 是可以直接依赖注入的 . 简单来说 , 在给controller的方法传入一个参数时 , 用一个interface来约束这个参数 , laravel 会自动找到IoC容器中绑定到这个interface上的工厂方法 , 析出实例 , 再作为参数传给该方法 . 例如:

class Login extends Controller {

    public function getLogin(RequestInterface $request )
    {
        $request instanceof RequestInterface ;//直接从IoC容器获得全局单例Request
    }

}

这使得项目代码的迭代效率大大提升 , 因为许多依赖都可以随时伪造 , 升级 , 修改 , 平行开发 ; 而不会影响这些依赖的使用 . 这使得Laravel项目的迭代变得非常便利 . 不过这种做法在项目特别庞大时 , 会增加启动时的性能开销 .

3.8 强大的artisan命令行

laravel底层使用了symfony的命令行组件 , 并做了各种封装 , 从而得到了一个比较便捷的命令行工具 . 简单来说有这些优点 :

  • 可以轻松编写linux风格的脚本命令
  • 封装了许多方法 , 可以便捷地加入人机互动的信息
  • 用artisan命令管理自带的队列 , 计划任务等
  • 用artisan命令直接管理开发环境下的数据库 , 销毁或重建 , 注入测试数据等
  • 用artisan命令直接用模板生成文件 , 例如controller , middleware , model , 都可以直接生成

用命令行直接生成代码文件 , 这个是很多现代框架的优势之一 . laravel 提供了一整套解决方案 , 可以便利地模仿 .

3.9 migration&seed 迁移和数据注入

laravel 的DB层支持各种常用数据库 , 并提供了Schema类 , 用它可以创建和修改数据表 .

在laravel中, 专门用来创建和修改数据表的脚本类被称作Migration(迁移) , 通过一行artisan命令就可以自动执行这些脚本 , 生成数据库 . 还可以用命令实现单次回滚 , 清空数据表 , 全部回滚等操作 .

使用migration的好处有:

  • migration加入版本控制 , 开发者可以共享它们 , 随时使用它们创建表 , 不受开发环境的约束
  • migration不受数据库限制 , 可以大部分通用于sqlserver , mysql 等不同类型数据库
  • 使用migration可以快速架构 , 调整 或销毁测试环境 , 在开发时比直接操作数据库要方便
  • 使用migration可以在新环境用脚本快速部署新项目 , 避免复制表结构的繁琐操作
  • migration本身可以作为数据库的文档

laravel还有专门的Seed类 , 作用是向数据表中注入数据 . 可以注入测试数据 , 或者项目所需的初始数据 . 只需要用一条命令行 . 注入和销毁数据可以反复执行 , 不受数据库类型约束 , 又可与其它开发者共享 , 为开发带来许多便利 .

migration和seed主要适用于开发 , 在正式运行时就不能轻易使用 , 破坏性很大 . 因此也会涉及一些麻烦的权限管理问题 .

3.10 灵活而强大的路由,支持RESTFul

随着移动互联的流行 , Web服务端越来越多地用于API , 使得RESTFul API风格日益流行起来 . 与传统的URL规则不同 , RESTFul直接用URI来标记资源 , 用请求的method如GET , POST , PUT , DELETE 等来区分对资源的基本操作 , 再辅以参数 . 这种做法比SOAP或xml-rpc更简洁 , 更统一 . 基于http , 对外依赖更少 .

laravel的路由和控制器都是直接支持RESTFul的 , 主要体现在对请求method的强识别上 . 也可以用命令行直接生成RESTFul风格的控制器 .

laravel的路由通过route.php文件来配置.路由也非常灵活 , 它和传统PHP框架的思路不同 , 把一个http从请求到响应拆分成了各个独立的组件:

  • url
  • 路由实体
  • 中间件
  • 请求
  • 控制器
  • 响应

路由起到的作用 , 就是把这些组件自由地拼接起来 , 然后注册到项目核心中 . 从而做到 :

  • 修改路由 , 可以随时更改url匹配的控制器
  • 请求可以先通过一个或多个中间件过滤 , 然后才访问控制器 . 中间件与路由的对应 , 可以自由组合 , 可以随时更改
  • 路由本身可以命名 , 作为一个实体操作 , 不再依赖于url
  • 可以通过控制器的方法反向获取url , 或者通过路由命名反向获取url
  • 路由获取的请求和返回的响应都是独立的 , 不与控制器耦合 , 返回的响应可根据请求的区别更改形式(返回web页面或json , 或其它结果)

总而言之 , 传统的路由难免在这里或那里有一一对应的关系 , 一旦有结构性的修改就会很麻烦 . 而laravel 的路由是把各个组件像搭积木一样有机结合在一起 , 意味着可以方便地按需求重构 , 不会导致太多的改动 , 而且大多数组件都是可复用的 . 尤其是通过控制器方法或者路由名反向获取url , 应用在模板之中 , 即便url规则有大的变化也不需要修改源码 .

在开发项目前期遇到站点访问路径大规模重构 , laravel的路由优势就会特别抢眼 .

3.11 middleware 中间件

较老的WEB框架没有中间件或拦截器 , 涉及复杂的合法性校验 , 权限判断 , 往往都丢在Controller中 . 为了这些代码的复用率 , 往往controller都会通过继承来获取公共的校验方法 .

一旦controller形成了复杂的继承关系 , 想要改动某个前置的校验 , 就牵一发动全身了 .

例如数十个controller都继承了某个父controller, 会自动执行UserLogin的校验方法 , 可后来user表被拆分成了 user和admin两个表 , 一部分controller要校验user , 一部分controller要校验admin , 这就变得棘手了 .

laravel 把这类工作都拆解出来 , 交给了中间件 . 所谓中间件就是一个独立的类 , 执行一个单一的请求过滤功能 . 通过路由 , 可以把任意组合的中间件 , 与任意的url , 任意一个控制器相匹配 .

这使得代码的复用率大大提高 , 而结构修改时的复杂度 , 改动量都大大减少 .

3.12 用php实现的队列系统与异步任务

laravel自带用redis或数据库做存储的队列系统 . 本质上也是用长进程监听队列并执行 . 因此管理进程需要用到外部软件如supervisor .

这个队列系统不比专门的消息队列功能强大 , 但作用也有所区别 . 它主要用于实现代码逻辑的异步执行 .

laravel的队列功能比较完善 , 可以延时执行 , 出错再执行 , 错误计数等等 . 通过命令行就可以进行各种管理 .

laravel的队列有一个特点 , 就是广泛用到了php类的sleep() 与 wake() 方法 , 通过把实例序列化存储来保留运行场景 , 在队列中还原后再重新运行 . 这使得laravel的异步任务开发非常简单 .

laravel的异步任务默认是job类 , 只要这个类有一个handle() 方法 , 在该方法中写好异步执行的逻辑(例如发送短信或邮件) , 这个类就可以随意复用了 . 在项目代码中只要

dispatch(new SendEmailJob()); 

就会自动把job实例压入队列 . 而在job实例化时获取的各种参数 , 都会在压入队列前通过序列化的__sleep()方法处理后保存下来 . 这使得laravel的异步任务逻辑构建和使用都很便捷.

3.13 事件驱动

laravel系统普遍使用了事件机制 . 事件机制有些类似PHP项目传统的hook(钩子)机制 , 只是更灵活 . 可以在程序的任何位置抛出一个事件(通常就是一行代码 , 例如fire('eventName',$params);) , 如果有监听者逻辑注册了这个事件在监听它 , 则会被触发执行 ; 否则什么也不发生 .

监听者本质就是把一段代码逻辑写到了一个独立的类中 . 有一个总的EventDispatcher类负责管理事件与监听者的对应关系 . 这些监听者类注册后映射到事件上 . 当一个事件被抛出 , 就会遍历它所有已注册的监听者 , 再一个一个执行 .

事件机制有许多好处:

  • 保证主干代码的简洁和健壮 , 所有非必要逻辑都解耦出去
  • 使监听者变得独立 , 可以进行单元测试
  • 使项目变得可扩展 , 增加的功能不必修改主干代码的文件
  • 可以统一管理事件与监听者的对应关系
  • 把轮询的开发思维转化为被动触发的思维 , 大大减轻系统负担

laravel的事件机制还有它独特的优点:

  • 监听者实现了依赖注入
  • 事件既可以定义成字符串 , 也可以用类来定义 . 用类来定义事件 , 对参数的传递和处理会特别灵活
  • 事件同步触发 , 但监听者可以个别的异步执行 , 非常灵活
  • laravel自身的流程普遍使用了事件 , 可以用事件机制进行扩展(例如把所有的sql记录到日志中,可通过DB的事件实现) , 不必改动源代码

基于事件机制来设计项目 , 和完全不用事件机制 , 思路是截然不同的 . 故普遍使用事件机制的项目设计可谓事件驱动 .

laravel 还通过事件机制提供了一套广播的解决方案 . 简单来说 , 现代的app越来越多地需要客户端与服务端的实时交互 . 由于http协议和PHP的先天不足 , 往往是用轮询接口的办法来实现 , 效率并不好 .

laravel 服务端通过fire event 来触发一个事件 , 同时事件机制可以自动调用某个能广播消息的工具(例如redis)将事件广播出去 , 监听这个频道的程序拿到了这个事件后 , 再自动发布给客户端 . 使用redis的话 , 可以是laravel触发事件 , 把事件数据通过redis广播 , 然后监听这个redis的node得到了事件的数据 , 再使用socket.io 通过websocket 把事件和数据传递给客户端的js , 并产生响应.

3.14 计划任务

一般lamp的计划任务都是用crontab来执行脚本 . laravel做法主要的区别是 , 把所有计划任务的脚本注册到一个类中 , 然后用crontab去频繁运行这个类 , 通过这个类来管理所有的计划任务的执行 .

这个做法的好处在于大量的crontab配置转化成了项目代码 , 在搬迁和部署新机器时变得很容易 .

3.15 支持远程文件系统

laravel提供了一套文件管理的功能模块 . 比较超前的是它默认支持国外主流云盘的接口 . 可以使用云服务来存储和调用文件 .

3.16 完善的异常机制

PHP语法的异常功能其实扩展性很强 , 但很少被使用 . laravel的核心启动流程中 , 建立了一套比较完善的异常机制 . 简单来说有两个特点 .

第一 , laravel进程抛出来的异常 , 都会在顶层被捕获 . 捕获异常之后可以用 instance of 来判断异常的类型 , 然后根据不同的异常定义来做不同的处理 . 例如写不同级别的警报日志 , 发通知给管理员等 .

这些都是开发者可以在laravel中自行定义的 , laravel 也鼓励开发者定义各种不同情形下的Exception类 .

第二 , laravel捕获异常之后 , 会执行report 和 render 两个方法 . 前者负责记录日志等内部操作 , 而后者负责根据异常类型的区别 , 渲染出页面或者其它数据结果返回给客户端.

例如抛出了一个 $httpException 异常后 , 可以根据 $httpException->getCode() 来判断渲染404页面还是跳转登陆页等等 .

如此使用异常类 , 比起过去各种错误状态码 , 或者错误页跳转 , 自然更易于理解和使用.

3.17 强大的Eloquent ORM

laravel 拥有特别强大的ORM(Eloquent) , 这也是它最大的优势之一 . laravel的ORM有哪些功能点呢?

  • 极简的配置 , 只要继承model类就可以直接操作数据表
  • 比较齐全的ORM增删改查方法
  • 批量赋值
  • 严谨的mass assignment 防护
  • 灵活的getter setter方法 , 用getter方法实现虚拟属性
  • 可以链式地组装查询条件
  • 灵活的scope方法 , 用于封装常用的查询条件
  • 完善而强大的关联查询
  • 完善的事件 , 可以通过事件触发各种关联操作(例如存储新数据时自动清空缓存)

最强大的功能则是Eloquent的关联查询 . 它在解决N+1问题的基础上 , 实现了绝大多数关联查询需求 ,如:

  • 1对1
  • 1对多
  • 多对多
  • 远程多对多
  • 多态关联(类似tag)

而Eloquent的另一个特点是 , 关联查询不需要专门的查询步骤 , 只要定义好关联关系 , 直接调用就可以自动完成查询 , 例如:

$user = User::find($id);  // 获取user表里的$id号用户数据
$realName = $user->profile->realName; // 自动得到 user表关联的user_profile表数据的realName属性 .

而涉及多条数据 , 多个关联表的复杂查询 , 也只要在查询时的链式方法中使用一个 with() 条件或load() 方法就可以自动获取所有的关联数据. 例如

$users = User::all(); //获取所有的用户数据
$users->load('profile'); //关联所有的profile表数据

foreach($users as $user){
    echo $user->profile->realName; //并不会产生任何新的查询sql
}

使用laravel的ORM可以让复杂的数据表工作变得很简单 , 缺点是学习曲线比较陡峭 , 并且在实际的代码分层中有很多问题需要解决.

3.18 结合gulp等前端资源管理工具

web页面前端资源已经越来越多地使用基于node的工具来进行管理 . 例如广泛使用的grunt 和 gulp , 可以编译less , sass生成css或js , 压缩和合并这些资源文件等等 .

laravel 鼓励这种做法 , 它自己整合了一个扩展自gulp的js包 , 叫做laravel-elixir . 把gulp最常用的功能用laravel风格的简洁代码来实现 , 不了解gulp的人只要几行代码也可以学会用它来管理 css , js为主的前端资源 .

这些前端资源的源文件则被鼓励放在 laravel项目的 resources/assets/ 路径下 , 以便纳入版本控制 .

4. laravel框架的代码特性

4.1 积极使用PHP新特性

laravel框架积极使用PHP的新特性 , 用于简化代码 , 增强效率 , 以及大大提高代码复用率和扩展性 . 这里简单说明一下:

  • laravel是基于命名空间的 , 5以后版本还使用了 ClassName::class 这类语法糖
  • 大量使用了trait , 来增强复杂类的代码继承关系
  • 普遍使用了后期静态绑定 , static关键字等语法糖
  • 无处不在地使用闭包 , 用闭包实现IoC容器 , 用闭包做管道 , 用闭包对类进行扩展等等
  • 广泛地使用PHP的魔术方法 , get set invoke sleep wake call __callStatic 等都用得很频繁且合理

学习laravel对了解PHP新语法的最佳实践有很大帮助

4.2 可读性强 , 优雅的代码

传统的PHP代码习惯传各种参数 , 可读性较差 , 不看源码不知道是什么意思.

laravel则到处有这种风格的代码:

$result = $model->where()->with()->select(); //model关联查询

$request->isAjax(); //request请求是否为ajax

return $response = response()->view('viewName')->with($data)->withError() ; //返回结果附带参数和错误信息

代码可读性强 , 强制约束少 , 更接近语义便于理解 , 给程序员一种优雅的感觉.

4.3 约定大于配置

许多现代开发框架都能快速生成功能代码 . 但习惯的开发思路是配置优先的 , 例如要生成一个数据表 , 先要写一个 yaml 文件或者是xml 文件 , 把大部分的结构和参数都定义好 , 然后生成想要的结果 . 这个过程仍然是很繁琐的 .

laravel 与之相反 , 默认定义了许多配置和关联的规则 , 从而创建新的代码非常简单 . 然后我们只需要在有改动的地方写上改动就行了 . 例如创建一个model,laravel只要一行代码:

class MyModel extends Model {

}

这个model就可以对 my_models 的数据表进行增删改查了 . 所有的配置都继承自父类 , 又可以按需修改 . 这种做法使得开发更加简洁.

5. laravel框架的其它特点

5.1 laravel的功能栈

laravel是比较重的全栈式框架 , 功能栈比较多. 例如

  • 可使用多种数据库的DB层
  • 可使用多种驱动的Cache
  • 自带的blade模板 , 更接近PHP习惯的web模板语言
  • 自带的认证模块 , 可注册 ,登录 , 找回密码 , 认证用户
  • 自带的复杂加密解密逻辑
  • 独立的Request 类和 Response类
  • 独立的View , 不与controller关联 , 因此可以用于邮件甚至短信模板
  • 自带的本地化功能 , 可以在不同语境下切换关键字的语言
  • 自带邮件和日志

5.2 laravel框架的常用组件

laravel框架是组件式开发的, 所以它所有的组件都可以抽离出来独立使用.

有些组件是特别适合拆分出来, 在任何项目都能用的 ,例如

  • laravel的IoC容器
  • EventDispatcher , 很轻的事件机制
  • Pipeline , 非常奇妙的管道机制
  • logger , 对monolog的简单封装
  • mailer , 对swift mailer 的封装 , 界面更流畅
  • support , laravel自己使用的各种辅助类

由于laravel的组件在调用依赖时 , 几乎全都是调用Interface , 所以任何一个组件只要实现了这些接口 , 都可以取代原来的组件.

5.3 laravel框架使用的优秀依赖包

laravel 通过composer使用了大量的依赖包 , 许多依赖包都有多达千万次的下载 , 是php开发者都值得了解的功能包 . 例如:

  • monolog : 强大的日志工具
  • swift mailer : 流行的邮件组件
  • symfony console : symfony的命令行工具

等等 . laravel选用的php依赖包都值得去 [packagist.org] 去了解

6. laravel实际使用中存在的问题

6.1 对laravel框架的拆分,改造和应用

laravel默认框架提供了一个简明的架构 . 它本身很灵活 , 可以随意更改 , 因此对初学者而言 , 代码结构如何分层 , 如何创建文件夹层次 , 都是很让人头疼的问题.

默认的laravel架构只能创建一个APP , 用一个public/index.php来访问 . 而实际工作中 , 一个项目可以有很多个互不干涉的访问端 . 从不同的访问端进入 , 包括配置文件和部分功能文件可能都有变化.

这就需要我们拆散默认的laravel框架结构 , 按自己的需求去重构项目 , 创建多个bootstrap文件 , 在不同的app下配置不同的config和resources等 . 这需要本身有完善的架构知识 , 并对laravel非常了解 , 也是会增添工作量的 .

6.2 laravel项目的线上部署和优化配置

laravel设计的本意是让线上部署更加方便快捷 . 但在实际应用中也存在一些问题 , 例如

  • 需要用composer频繁创建依赖库
  • 对composer加载的依赖包进行版本控制很麻烦
  • 如果把composer的依赖库加入自己的版本控制 , 更新时代码会很多 , 不能进行code review

使用composer后如何快速部署项目 , 是主要要解决的问题 .

laravel虽然项目的文件数量非常多 , 但因为有composer 存在 , 实际需要的文件都是按需加载的 . 尽管如此 , 因为IoC机制存在 , laravel的启动过程还是很复杂的 , 使得性能开销并不小 . 正确使用laravel , 需要做全面的优化配置 , 包括:

  • 配置服务器的.env文件 , 设置数据库等其它敏感数据
  • 生成composer的autoload 要用 --production 参数 , 提高性能
  • 使用artisan的route:cache , 将所有路由合并成缓存文件 , 大大提高启动效率
  • 使用artisan的config:cache , 将所有的config合并成单一缓存文件
  • 确定所有的Provider能够正确生成缓存文件 , 视图能正确生成缓存文件
  • 确认关闭功能强大的debug
  • 灵活使用php 的 opcache
  • 用命令行管理计划任务 , 队列进程
  • 严格限制服务端命令行的操作权限 , 避免出现灾难性的命令行操作失误 , 或者关闭migration命令
多轮对话机器人框架 commune/chatbot 项目发布了demo测试. 微信关注CommuneChatbot 可以测试. QQ交流群: 907985715
本帖已被设为精华帖!
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 18
Summer

:+1: 挺好的笔记

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!