JSON 和 XML 视图

JsonViewXmlView 与 CakePHP 的 内容类型协商 功能集成在一起,使您可以创建 JSON 和 XML 响应。

这些视图类通常与 CakeControllerController::viewClasses() 一起使用。

您可以通过两种方式生成数据视图。第一种是使用 serialize 选项,第二种是创建普通的模板文件。

定义用于协商的视图类

在您的 AppController 或某个单独的控制器中,您可以实现 viewClasses() 方法,并提供您想要支持的所有视图。

use Cake\View\JsonView;
use Cake\View\XmlView;

public function viewClasses(): array
{
    return [JsonView::class, XmlView::class];
}

您可以选择使用 路由文件扩展名 启用 json 和/或 xml 扩展名。这将允许您使用自定义 URL(以响应类型的名称作为文件扩展名结尾)访问 JSONXML 或任何其他特殊格式的视图,例如 http://example.com/articles.json

默认情况下,如果未启用 路由文件扩展名,则会使用请求的 Accept 标头来选择应向用户呈现的格式类型。用于呈现 JSON 响应的示例 Accept 格式是 application/json

使用数据视图与序列化键

serialize 选项指示使用数据视图时应序列化哪些视图变量。如果您不需要在数据转换为 json/xml 之前进行任何自定义格式化,那么这可以让您跳过为控制器操作定义模板文件。

如果您需要在生成响应之前对视图变量进行任何格式化或操作,则应使用模板文件。的 serialize 值可以是字符串,也可以是要序列化的视图变量数组。

namespace App\Controller;

use Cake\View\JsonView;

class ArticlesController extends AppController
{
    public function viewClasses(): array
    {
        return [JsonView::class];
    }

    public function index()
    {
        // Set the view vars
        $this->set('articles', $this->paginate());
        // Specify which view vars JsonView should serialize.
        $this->viewBuilder()->setOption('serialize', 'articles');
    }
}

您也可以将 serialize 定义为要组合的视图变量数组。

namespace App\Controller;

use Cake\View\JsonView;

class ArticlesController extends AppController
{
    public function viewClasses(): array
    {
        return [JsonView::class];
    }

    public function index()
    {
        // Some code that created $articles and $comments

        // Set the view vars
        $this->set(compact('articles', 'comments'));

        // Specify which view vars JsonView should serialize.
        $this->viewBuilder()->setOption('serialize', ['articles', 'comments']);
    }
}

serialize 定义为数组具有额外的好处,即在使用 XmlView 时自动追加一个顶级的 <response> 元素。如果您使用 serialize 的字符串值和 XmlView,请确保您的视图变量具有单个顶级元素。如果没有单个顶级元素,Xml 将无法生成。

使用数据视图与模板文件

如果您需要在创建最终输出之前操作视图内容,则应使用模板文件。例如,如果我们有一篇文章,其中一个字段包含生成的 HTML,那么我们可能希望将其从 JSON 响应中省略。在这种情况下,视图文件将很有用。

// Controller code
class ArticlesController extends AppController
{
    public function index()
    {
        $articles = $this->paginate('Articles');
        $this->set(compact('articles'));
    }
}

// View code - templates/Articles/json/index.php
foreach ($articles as $article) {
    unset($article->generated_html);
}
echo json_encode(compact('articles'));

您还可以进行更复杂的操作,或使用辅助函数进行格式化。数据视图类不支持布局。它们假定视图文件将输出序列化内容。

创建 XML 视图

class XmlView

默认情况下,使用 serialize 时,XmlView 将使用 <response> 节点包装您的序列化视图变量。您可以使用 rootNode 选项为该节点设置自定义名称。

XmlView 类支持 xmlOptions 选项,该选项允许您自定义用于生成 XML 的选项,例如 tagsattributes

使用 XmlView 的一个示例是生成一个 sitemap.xml。这种文档类型要求您更改 rootNode 并设置属性。属性使用 @ 前缀定义。

use Cake\View\XmlView;

public function viewClasses(): array
{
    return [XmlView::class];
}

public function sitemap()
{
    $pages = $this->Pages->find()->all();
    $urls = [];
    foreach ($pages as $page) {
        $urls[] = [
            'loc' => Router::url(['controller' => 'Pages', 'action' => 'view', $page->slug, '_full' => true]),
            'lastmod' => $page->modified->format('Y-m-d'),
            'changefreq' => 'daily',
            'priority' => '0.5',
        ];
    }

    // Define a custom root node in the generated document.
    $this->viewBuilder()
        ->setOption('rootNode', 'urlset')
        ->setOption('serialize', ['@xmlns', 'url']);
    $this->set([
        // Define an attribute on the root node.
        '@xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
        'url' => $urls,
    ]);
}

创建 JSON 视图

class JsonView

JsonView 类支持 jsonOptions 选项,该选项允许您自定义用于生成 JSON 的位掩码。有关此选项的有效值,请参见 json_encode 文档。

例如,要以一致的 JSON 格式序列化 CakePHP 实体的验证错误输出,请执行以下操作:

// In your controller's action when saving failed
$this->set('errors', $articles->errors());
$this->viewBuilder()
    ->setOption('serialize', ['errors'])
    ->setOption('jsonOptions', JSON_FORCE_OBJECT);

JSONP 响应

使用 JsonView 时,您可以使用特殊的视图变量 jsonp 来启用返回 JSONP 响应。将其设置为 true 会使视图类检查是否设置了名为“callback”的查询字符串参数,如果设置了,则将 JSON 响应包装在提供的函数名称中。如果您想使用自定义查询字符串参数名称而不是“callback”,请将 jsonp 设置为所需的名称而不是 true

选择视图类

虽然您可以大部分时间使用 viewClasses 钩子方法,但如果您想要完全控制视图类选择,则可以直接选择视图类。

// src/Controller/VideosController.php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Http\Exception\NotFoundException;

class VideosController extends AppController
{
    public function export($format = '')
    {
        $format = strtolower($format);

        // Format to view mapping
        $formats = [
          'xml' => 'Xml',
          'json' => 'Json',
        ];

        // Error on unknown type
        if (!isset($formats[$format])) {
            throw new NotFoundException(__('Unknown format.'));
        }

        // Set Out Format View
        $this->viewBuilder()->setClassName($formats[$format]);

        // Get data
        $videos = $this->Videos->find('latest')->all();

        // Set Data View
        $this->set(compact('videos'));
        $this->viewBuilder()->setOption('serialize', ['videos']);

        // Set Force Download
        return $this->response->withDownload('report-' . date('YmdHis') . '.' . $format);
    }
}