delatbabel/viewpages

支持从数据库中渲染/查看 Laravel 页面和模板。

v1.4 2017-08-18 03:53 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:52:57 UTC


README

StyleCI Latest Stable Version Total Downloads

支持从数据库中查看/渲染 Laravel 页面和模板。

可用于内容管理、管理界面(例如使用 AdminLTE 或其他前端框架)等。

支持使用与现有视图加载相同的接口加载 Blade 和 Twig 模板,例如

    return View::make("dashboard.sysadmin")
        ->with('page_title', 'System Administrator Dashboard')
        ->with('tasks', $tasks);

原理

无法使用数据库支持的视图、模板和布局是阻止 Laravel 被用来创建真正动态 CMS 的缺失功能之一。本软件包旨在解决这个问题。

TerrePorter 通过他的 StringBladeCompiler 软件包部分解决了这个问题。他的软件包最初基于 Flynsarmy/laravel-db-blade-compiler,该软件包支持从模型对象中获取 blade,但现已不再维护。

安装

使用命令行通过 composer 添加软件包

    composer require delatbabel/viewpages

或者,通过将这些行添加到您的 composer.json 文件中手动拉取软件包

    "require": {
        "delatbabel/viewpages": "~1.0"
    },

完成后,运行 composer update 命令

    composer update

注册服务提供者

在 composer update 完成后,从您的 config/app.php 文件中的 'providers' 数组中删除此行(或将其注释掉)

    Illuminate\View\ViewServiceProvider::class

替换为以下行

    Delatbabel\ViewPages\ViewPagesServiceProvider::class,

整合并运行迁移

最后,整合并运行以下迁移脚本来创建数据库表

    php artisan vendor:publish --provider='Delatbabel\ViewPages\ViewPagesServiceProvider' --force
    php artisan migrate

在运行迁移脚本之前,您可能想修改脚本本身,或者修改数据库/seeds/examples 中包含的基本模板。提供的只是一些基于 AdminLTE 的示例。

如何使用此软件包

创建视图

  • 按上述说明安装和运行迁移。
  • 将模板填充到 vpages 表中。它们不必与标准 Laravel blade 模板有任何不同 - 请参阅以下关于 Blade 编译 的部分。
  • 除了包含模板或页面内容的 content 列之外,还应填充以下列
  • pagekey -- 页面查找键。
  • url -- 页面查找 URL,当您想通过 URL 查找页面内容时可能很有用。
  • name -- 页面的描述性名称,例如 "主网站主页"。
  • description -- 页面的更长时间描述。
  • pagetype -- 根据视图语言为 blade.phptwig

这里重要的是 pagekey。它基本上取代了现有 Laravel View 门面中用于查找视图的视图名称。例如,如果您通常使用 View::make("dashboard.sysadmin"); 来查找视图,您通常会将视图存储在磁盘上的 resources/views/dashboard/sysadmin.blade.php 中。相反,您会将视图存储在 vpages 表中,如下所示

  • pagekey = "dashboard.sysadmin"
  • url = "dashboard/sysadmin"
  • name = 您喜欢作为名称的任何内容,例如 "sysadmin dashboard"
  • description = 您喜欢的任何内容,例如 "我的系统管理员仪表板"
  • content = 您通常存储在磁盘上的确切 blade 内容
  • pagetype = blade.php

创建 Blade 模板

您仍然可以像在 Laravel 中一样使用模板(布局)。例如,您的模板可以包含以下内容

<html>
<head><title>{{ $page_title }}</title></head>
<body>
@yield('body)
</body>
</html>

然后,主体可以包含以下内容

@extends('layouts.main')

@section('body)
<p>Body text goes here</p>
@endsection

将模板存储在vpages表中,其中pagekey = 'layouts.main',它将被您的主体视图自动找到并扩展。

有关更多详细信息,请参阅模板继承

变体内容

这些是网站依赖的数据块,存储在vobjects表中,并使用可以注入页面的VojbecService服务检索,该服务使用Laravel 服务注入

示例

@inject('objects', 'Delatbabel\ViewPages\Services\VobjectService')

<!-- Using the regular make method -->
<title> {{ $objects->make('page_title') }} </title>

<!-- Using a magic getter -->
<title> {{ $objects->page_title }} </title>

Twig 视图和模板

此包现在支持使用Twig模板语言以及blade模板,通过TwigBridge类。

使用pagetype = "twig"将这些存储在数据库表中,与您的blade模板一起。

使用模板

一旦创建模板,您就可以像使用任何其他视图文件一样使用它们,例如。

    return view("dashboard.sysadmin")
        ->with('page_title', 'System Administrator Dashboard')
        ->with('tasks', $tasks);

请注意,Laravel 视图类中有一个错误,它在其所有情况下都使用其本地工厂,而不是从应用程序实例中获取工厂,因此请使用view()辅助函数而不是外观类View::make()。

基础Factory类将按以下顺序尝试查找视图,直到找到匹配项

  • 在vpages表中查找pagekey = dashboard.sysadmin的vpage。
  • 在vpages表中查找url = dashboard.sysadmin的vpage。
  • 在磁盘上查找名为resources/views/sysadmin/dashboard.blade.php的视图。
  • 在磁盘上查找名为resources/views/sysadmin/dashboard.twig的视图。
  • 在vpages表中查找pagekey = errors.410的vpage。
  • 在磁盘上查找名为resources/views/errors/410.blade.php的视图。
  • 在磁盘上查找名为resources/views/errors/410.twig的视图。
  • 在vpages表中查找pagekey = errors.404的vpage。
  • 在磁盘上查找名为resources/views/errors/404.blade.php的视图。
  • 在磁盘上查找名为resources/views/errors/404.twig的视图。

有关如何工作的详细信息,请参阅后面的“架构”部分。

CMS 使用

包含一个名为VpageController的控制器类(您欢迎扩展它),可以用作通配路由。该控制器包含一个名为index()的函数,该函数简单地使用提供的URL从数据库中加载页面。这为您提供了一个简单的基于blade的CMS。

要包含到这个控制器的路由,请在您的路由文件(s)中所有其他路由之后包含此路由规范。

Route::any('{slug}', [
    'as'    => 'vpage.make',
    'uses'  => '\Delatbabel\ViewPages\Http\Controllers\VpageController@make'
])->where('slug', '.*');

您可能需要在此路由上添加额外的where子句,以排除应用程序消耗的其他路由。例如,要排除所有位于"/admin"和"/img"下的URL,请添加以下where子句

  ->where('slug', '^(?!admin)(?!img)([A-z\d-\/_.]+)?');

待办事项

  • 从make函数中提取查找特定网站页面的逻辑,并将其放入自定义的BelongsToMany类中。
  • 更多测试。这个似乎在我的已导入示例应用程序中工作,但我还没有进行过广泛的测试或构建phpunit测试用例。
  • 更多文档。
  • 也许有一组管理控制器来更新/编辑数据库中的内容。使用浏览器内编辑器,例如HTMLiveCode
  • 修复TwigEngine的finder,使其不查找Blade视图,反之亦然。在Vpage::make()中也有相关的待办事项,以限制从数据库中检索的视图类型。
  • 向加载器添加lastModified()函数。
  • 修复BladeCompiler中的isExpired()函数。
  • 也许支持其他模板引擎,如Smarty。特别是,我更喜欢一个不编译为PHP代码而编译为内存中字符串的模板引擎。

呼叫

此包派生的原始包有呼叫的想法。这意味着视图可以包含如下的调用

    {{ __o('toolbox@functionname') }}

__o 是一个辅助函数,用于在 Laravel 3 中调用 Controller::call() 函数,以 HMVC 的方式渲染 toolbox 控制器中 "functionname" 动作的输出。实际上,Laravel 5 已经不再支持 HMVC(Taylor 认为HMVC 是一个糟糕的主意,我并不这么认为),因此我们需要找到其他方式来引入动态内容。这可能会通过某种方式通过 Repository 或 Service 类来实现。

服务注入可能已经可以工作,但我还没有测试过。

架构

我使用过一个基于 Laravel 3 的 CMS 系统,其实现相当糟糕,这个包旨在成为 Laravel 3 CMS 应该实现的最佳实践。

工厂

视图系统的根是 Factory 类。这通过 View 门面访问,并在应用中注册了一个 view 单例。

我对 Laravel 默认的 Factory 类所做的唯一修改是自动加载 errors.410 视图或 errors.404 视图,如果请求的视图未找到。这在 CMS 应用程序中很有用,其中视图可能通过 URL 路径进行搜索,用户可能会输入一个垃圾 URL,因此我们希望显示一个 404 页面而不是一个未处理的异常块。

扩展和引擎解析

Factory 类包含一个内部数组 $extensions,它是一个将文件扩展名(特别是视图路径扩展名)映射到引擎 ID 的映射。默认数组看起来像这样

    protected $extensions = ['blade.php' => 'blade', 'php' => 'php'];

所以 "blade.php" 扩展名映射到 "blade" 引擎 ID。

可以通过调用 Factory::addExtension() 向此 $extensions 数组添加额外的引擎扩展。注意,这已经被 TwigBridge 服务提供者完成。

引擎 ID 传递给一个解析器(EngineResolver),它包含从引擎 ID 到引擎本身的映射。这些映射在 EngineResolver::resolve() 函数中创建。

因此,每次我们从数据库返回一个 blade 模板路径时,都需要在模板名称中添加 "blade.php" 扩展名,以便引擎解析器可以找到正确的引擎来编译和加载 blade。

引擎

Engine 类封装了加载、编译以及评估编译后的模板(包含数据)的功能。所有这些都在 get() 中发生,对于 blade 模板来说更为具体(或任何编译为 PHP 的模板),在 PhpEngine 类中有一个名为 evaluatePath() 的函数,它直接包含模板并通过将包含语句包裹在 ob_start()ob_end_clean() 对对中(不高效)来获取输出(这是我讨厌将视图编译为 PHP 的原因之一)。

评估 Twig 模板需要不同于评估 Blade 模板,因为编译后的结果不能直接执行。注意,这已经在 TwigBridge 中的引擎中完成。

在编译期间加载 Blade 视图

Laravel 视图系统有些反直觉。每个编译器都调用 ViewFinders 来查找文件,然后在编译器中自己加载文件(而不是独立发生文件查找、加载和编译(从字符串))。

查找

这是通过以下方式实现的:

  • 添加一个 VpageViewFinder 类,该类可以确定视图是否在数据库中找到。
  • 添加了一个ChainViewFinder类,该类将VpageViewFinder和现有的Laravel FileViewFinder类(保持不变)链在一起,首先从数据库中选择视图,如果没有找到,则回退到文件系统。

EngineResolver需要在视图名称中添加扩展名,以便能够确定正确的引擎将视图传递给它,因此VpageViewFinder将文件扩展名添加到视图名称。可以剥去这个文件扩展名的Vpage类可以从数据库加载页面,在make()函数中加载页面之前。

请注意,扩展名不必以"."开头,可以有任何前缀。Vpage类定义了一个要使用的常量前缀。

加载

通过以下方式将编译器扩展到包括加载阶段:

  • 创建一个新的LoaderInterface,该接口定义了加载器的接口。加载器需要的唯一函数是get()函数,用于获取查找器找到的视图名称,并将其作为字符串加载。
  • 创建新的FilesystemLoaderVpageLoader类,可以从文件系统或数据库分别加载视图名称。
  • 创建一个ChainLoader类,可以将VpageLoaderFilesystemLoader对象链接在一起,从任何位置加载视图。

最后一步是扩展原生的Laravel BladeCompiler类,使用其中一个加载器(初始化为ChainLoader)加载视图内容,而不是使用其自己的内部Filesystem对象加载视图内容。然后使用compileString()函数按正常方式编译。

加载Twig视图

查找

ChainViewFinder类还可以由Twig加载器在TwigBridge服务提供商中创建,以便twig加载器的ViewFinder可以使用与Blade加载器相同的方法查找视图。

加载

Twig(通过TwigBridge)已经有一个独立的查找器和加载器类的概念,但是加载器必须遵循Twig的Twig_LoaderInterface。为了实现这一点,我构建了一个VpageTwigLoader类来符合接口。

我覆盖了TwigBridge ServiceProvider类,以在已用于加载twig数据的Twig_Loader_Chain对象中提供一个VpageTwigLoader对象。

其他实现

modules/backend/twig中,OctoberCMS有一系列对twig类的扩展,用于加载等。他们有一个Twig_LoaderInterface的实现,尽管它仍然本质上是一个基于文件的加载器,但它执行了一些额外的Laravely操作,例如在加载时触发事件,并使用自己的CMS类执行一些操作,例如使用Laravel的FileCache门面加载文件(从磁盘或从缓存加载,如果存在)。这根本不是我想要做的。

处理视图名称

视图可以通过名称或URL找到。CMS可能更喜欢通过URL获取视图,而仅在工作视图名称的系统可能更喜欢通过页面键(例如layouts.master)获取。工厂类首先尝试通过键查找,然后通过URL。

如果数据库中没有找到视图,则在磁盘上搜索具有该键的视图。

如果在磁盘上没有找到视图,则搜索数据库中的errors.410errors.404视图。

如果在此阶段没有找到视图,则抛出异常。

Blade编译

Blade模板的编译有点像一门黑艺术,在Laravel文档中解释得也不够清楚。基本上,所有的blade模板都会编译成磁盘上的PHP文件,然后存储在storage/framework/views目录下。一旦模板的编译版本过时,就会被新的副本替换。这些编译模板的缓存通常取决于blade模板文件的文件日期,然而在这个扩展中,我们让它取决于数据库中模板数据的updated_at日期。

当一个blade编译为PHP时,指令的编译方式如下

@extends / @section

这两个指令是配对的。@extends编译成

    echo $__env->make('layout.name', array_except(get_defined_vars(), array('__data', '__path')))->render();

@section和@endsection编译成

    $__env->startSection('section_name');
    $__env->stopSection();

注意,指令在编译文件中的顺序与在blade模板文件中的顺序相反——通常在模板文件中@extends在顶部,@section / @endsection在下面,在编译后的模板文件中@extends的编译版本在文件末尾。

@yield

@yield('section_name')在编译文件中看起来是这样的

    echo $__env->yieldContent('body');

使用$__env

全局变量$__env实际上是Illuminate\View\Factory的一个实例。

这个类实现了必要的make()startSection()stopSection()yieldContent()函数,这些函数使得内容出现在正确的位置。

关键函数是make(),它执行以下操作

  • 通过将'.'替换为'/'来标准化视图名称
  • 调用查找器来查找视图。
  • 准备数据数组。
  • 从路径中获取要使用的引擎。
  • 创建View对象

基础Factory类的问题在于它假设视图名称是一个具有扩展名的文件名,并且可以通过“php”或“blade.php”或“twig”扩展名来识别以确定视图类型。相反,我将在数据库表中存储视图类型。

查找器和加载器已被扩展,以便它们能够从数据库而不是从磁盘中提取视图。从数据库中提取视图的逻辑全部在Vpage::make()函数中。

一旦从数据库中提取了blade的内容并解决了引擎,则通过Factory::make()将视图工厂和引擎传递给视图。

渲染

View::render()通过以下方式执行实际渲染

  • 调用View::renderContents()
  • 它调用View::getContents()
  • 它将完整视图路径和数据传递给Engine::get()
  • Engine::get()调用Compiler::compile()来编译视图,如果缓存的编译视图副本已过期
  • 然后Compiler::getCompiledPath(),它返回编译视图的路径
  • 然后Engine::evaluatePath(),它将数据传递给编译后的视图

服务提供者

这里的服务提供者相当简单——然而有3个

  • ViewPagesServiceProvider——执行正常的迁移、种子注册,并调用StringBladeCompilerServiceProvider
  • IlluminateViewServiceProvider——扩展基础Laravel视图服务提供者,包括必要的附加查找器和加载器类。
  • TwigBridgeServiceProvider——扩展TwigBridge中的服务提供者,以便twig链加载器包括一个从数据库中加载twig模板的类。

模型类

模型类(Vpage)替换了模板文件的磁盘存储,这样上述讨论的Factory类就可以从数据库而不是磁盘中提取模板。

参考资料