13.x 以及 Framework v13.37.0 的实现,对包开发基础进行深入讲解。常规模板语法请参阅 Blade,视图的命名空间与发布请参阅包视图的覆盖与更新。
区分注册、编译与执行
传给Blade::directive() 的回调返回的不是要显示的值,而是要生成的 PHP 代码字符串。参数 $expression 也不是已求值的值,而是写在模板中的 PHP 表达式。
view:cache 也会执行编译。在指令的生成回调中,不要获取当前用户、写入数据库或调用外部 API。
使用包专属的名称注册
作为示例,我们提供一个在配送编号前显示所配置标签的@acmeCourierLabel($code)。前提是通过 Composer 的 PSR-4 将 Acme\Courier\ 映射到 src/,并通过自动发现或手动方式注册服务提供者。
src/CourierServiceProvider.php
courier.label 为字符串、courier.enabled 为布尔值。配置文件的分发与合并请参阅包配置的合并与缓存。
注册在常规服务提供者的 boot() 中进行,不要用 runningInConsole() 条件包裹。HTTP 渲染和 Artisan 的 view:cache 都需要相同的扩展。也要避免把只注册 Blade 扩展的服务提供者设为延迟提供者,因为它在其他服务被解析之前不会启动。
参数以表达式而非值的形式接收
$code。生成的 PHP 如下所示。
config() 与 $code 的求值发生在执行这段 PHP 的时刻。这与回调自身读取 config() 并返回固定字符串的实现不同。
经确认,callCustomDirective() 会去掉外层圆括号并 trim 表达式,然后传给回调。它不会把参数转换为 PHP 值,也不会拆分为单独的参数。本示例将其作为传入一个必需表达式的语法公开。
处理多个参数时,不要简单地用逗号 explode()。数组、方法调用和字符串字面量中也可能包含逗号。如果要处理属性或复杂的 HTML,作为 Blade 组件提供更容易划分职责。
名称冲突也是兼容性问题
directive() 以名称为键将处理器保存到数组中。同名注册会覆盖之前的注册,并且 compileStatement() 会优先选择自定义指令,而不是内置的转换方法。官方测试中也有通过自定义注册覆盖 foreach 的用例。
请避免使用 label、enabled 等通用名称以及内置名称,并加上包专属的前缀。将使用者在模板中使用的名称和参数视为公开 API,变更时提供迁移步骤。
条件分支使用 Blade::if 在运行时判断
上面服务提供者中注册的条件可以这样使用。Blade::if() 在保存条件回调之后,会注册用于开始、unless、else 和结束的指令。本示例的开始部分会被转换为以下 PHP。
check() 在运行时调用。这并不是在编译时固定 true 或 false 结果的处理。即使是使用已编译视图的 HTTP 进程,也需要注册条件回调。
这种语法只用于切换显示内容,并不是对配送操作的授权。访问控制也要在路由或操作一侧实施。
包更新后视图未必会自动重新生成
经确认,Compiler::isExpired() 会检查缓存是否存在,并根据配置检查原始视图与已编译文件的更新时间。它不会把服务提供者中编写的指令变更作为依赖文件进行追踪。
官方文档也建议在修改指令逻辑后,使用
view:clear 删除缓存。请在使用方应用的部署流程中,在更新的包与使用者的视图保持一致之后执行。
view:cache 在生产环境中进行预编译时执行。不要在包的 boot() 中每次都清除缓存,也不要在安装时擅自改写使用方应用的部署流程。配置缓存的更新和长驻进程的重新加载,也请遵循使用环境的常规部署步骤。
分别验证编译结果与渲染结果
首先,在注册了上述服务提供者的测试用 Laravel 应用中,验证生成的 PHP。以下是添加到继承Tests\TestCase 的 Feature 测试中的方法。
相关页面
包视图的覆盖与更新
了解命名空间的查找顺序以及维护已发布模板的方法。
包的缓存与 optimize 集成
了解将自有缓存的构建与删除集成到部署流程中的方法。