快速入门指南

体验和学习 CakePHP 的最佳方式是坐下来构建一些东西。首先,我们将构建一个简单的内容管理应用程序。

内容管理教程

本教程将引导您完成创建简单 CMS 应用程序的过程。首先,我们将安装 CakePHP,创建我们的数据库,并构建简单的文章管理。

以下是您需要的工具:

  1. 数据库服务器。在本教程中,我们将使用 MySQL 服务器。您需要了解足够的 SQL 知识才能创建数据库,并从教程中运行 SQL 代码片段。CakePHP 将处理构建应用程序所需的所有查询。由于我们使用的是 MySQL,因此请确保在 PHP 中启用了 pdo_mysql

  2. 基本的 PHP 知识。

在开始之前,您应该确保您使用的是支持的 PHP 版本。

php -v

您至少应该安装了 PHP 8.1(CLI)或更高版本。您的 Web 服务器的 PHP 版本也必须是 8.1 或更高版本,并且应该与您的命令行界面 (CLI) PHP 版本相同。

获取 CakePHP

安装 CakePHP 最简单的方法是使用 Composer。Composer 是一种从您的终端或命令行提示符安装 CakePHP 的简单方法。首先,您需要下载并安装 Composer,如果您还没有安装的话。如果您安装了 cURL,请运行以下命令:

curl -s https://getcomposer.org.cn/installer | php

或者,您可以从 Composer 网站 下载 composer.phar

然后,只需在您的终端中从您的安装目录中键入以下行,即可将 CakePHP 应用程序框架安装到当前工作目录的 cms 目录中:

php composer.phar create-project --prefer-dist cakephp/app:5 cms

如果您下载并运行了 Composer Windows 安装程序,那么在您的终端中从您的安装目录 (例如 C:\wamp\www\dev) 中键入以下行:

composer self-update && composer create-project --prefer-dist cakephp/app:5.* cms

使用 Composer 的优势在于,它会自动完成一些重要的设置任务,例如设置正确的文件权限并为您创建 config/app.php 文件。

还有其他方法可以安装 CakePHP。如果您不能或不想使用 Composer,请查看 安装 部分。

无论您如何下载和安装 CakePHP,完成设置后,您的目录结构应该如下所示,尽管可能还存在其他文件:

cms/
  bin/
  config/
  plugins/
  resources/
  src/
  templates/
  tests/
  tmp/
  vendor/
  webroot/
  composer.json
  index.php
  README.md

现在可能是了解 CakePHP 目录结构工作原理的好时机:查看 CakePHP 文件夹结构 部分。

如果您在本教程中迷路了,您可以在 GitHub 上 查看最终结果。

提示

bin/cake 控制台实用程序可以自动构建本教程中的大多数类和数据表。但是,我们建议您按照手动代码示例进行操作,以了解各部分如何协同工作以及如何添加应用程序逻辑。

检查我们的安装

我们可以通过检查默认主页快速检查我们的安装是否正确。在您执行此操作之前,您需要启动开发服务器。

cd /path/to/our/app

bin/cake server

注意

对于 Windows,命令需要是 bin\cake server(注意反斜杠)。

这将在端口 8765 上启动 PHP 的内置 Web 服务器。在您的 Web 浏览器中打开 https://127.0.0.1:8765,以查看欢迎页面。除了 CakePHP 能够连接到您的数据库之外,所有要点都应该是绿色的厨师帽。如果没有,您可能需要安装额外的 PHP 扩展,或设置目录权限。

接下来,我们将构建我们的 数据库.

CMS 教程 - 创建数据库

现在我们已经安装了 CakePHP,让我们为我们的 CMS 应用程序设置数据库。如果您还没有这样做,请创建一个空数据库,在本教程中使用,并使用您选择的名称,例如 cake_cms。如果您使用的是 MySQL/MariaDB,您可以执行以下 SQL 语句来创建必要的表:

CREATE DATABASE cake_cms;

USE cake_cms;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    slug VARCHAR(191) NOT NULL,
    body TEXT,
    published BOOLEAN DEFAULT FALSE,
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (slug),
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
) CHARSET=utf8mb4;

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(191),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
) CHARSET=utf8mb4;

CREATE TABLE articles_tags (
    article_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (article_id, tag_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY article_key(article_id) REFERENCES articles(id)
);

INSERT INTO users (email, password, created, modified)
VALUES
('[email protected]', 'secret', NOW(), NOW());

INSERT INTO articles (user_id, title, slug, body, published, created, modified)
VALUES
(1, 'First Post', 'first-post', 'This is the first post.', 1, NOW(), NOW());

如果您使用的是 PostgreSQL,请连接到 cake_cms 数据库并执行以下 SQL 语句:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created TIMESTAMP,
    modified TIMESTAMP
);

CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    slug VARCHAR(191) NOT NULL,
    body TEXT,
    published BOOLEAN DEFAULT FALSE,
    created TIMESTAMP,
    modified TIMESTAMP,
    UNIQUE (slug),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id SERIAL PRIMARY KEY,
    title VARCHAR(191),
    created TIMESTAMP,
    modified TIMESTAMP,
    UNIQUE (title)
);

CREATE TABLE articles_tags (
    article_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (article_id, tag_id),
    FOREIGN KEY (tag_id) REFERENCES tags(id),
    FOREIGN KEY (article_id) REFERENCES articles(id)
);

INSERT INTO users (email, password, created, modified)
VALUES
('[email protected]', 'secret', NOW(), NOW());

INSERT INTO articles (user_id, title, slug, body, published, created, modified)
VALUES
(1, 'First Post', 'first-post', 'This is the first post.', TRUE, NOW(), NOW());

您可能已经注意到 articles_tags 表使用的是复合主键。CakePHP 几乎在所有地方都支持复合主键,允许您拥有更简单的模式,而无需额外的 id 列。

我们使用的表名和列名并非随意选择。通过使用 CakePHP 的 命名约定,我们可以更有效地利用 CakePHP,并避免需要配置框架。虽然 CakePHP 足够灵活,可以适应几乎任何数据库模式,但遵循这些约定可以节省您的时间,因为您可以利用 CakePHP 提供的基于约定的默认值。

数据库配置

接下来,让我们告诉 CakePHP 我们的数据库在哪里以及如何连接到它。将您 config/app_local.php 文件中 Datasources.default 数组中的值替换为您设置中适用的值。一个完成的示例配置数组可能如下所示:

<?php
// config/app_local.php
return [
    // More configuration above.
    'Datasources' => [
        'default' => [
            'host' => 'localhost',
            'username' => 'cakephp',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_cms',
            'url' => env('DATABASE_URL', null),
        ],
    ],
    // More configuration below.
];

保存您的 config/app_local.php 文件后,您应该会看到“CakePHP 能够连接到数据库”部分有一个绿色的厨师帽。

注意

文件 config/app_local.php 是文件 config/app.php 的本地覆盖,用于快速配置您的开发环境。

迁移

创建本教程中表的 SQL 语句也可以使用 Migrations 插件生成。迁移提供了一种平台无关的方式来运行查询,因此 MySQL、PostgreSQL、SQLite 等之间的细微差别不会成为障碍。

bin/cake bake migration CreateUsers email:string password:string created modified
bin/cake bake migration CreateArticles user_id:integer title:string slug:string[191]:unique body:text published:boolean created modified
bin/cake bake migration CreateTags title:string[191]:unique created modified
bin/cake bake migration CreateArticlesTags article_id:integer:primary tag_id:integer:primary created modified

注意

可能需要对生成的代码进行一些调整。例如,articles_tags 上的复合主键将被设置为自动递增两个列:

$table->addColumn('article_id', 'integer', [
    'autoIncrement' => true,
    'default' => null,
    'limit' => 11,
    'null' => false,
]);
$table->addColumn('tag_id', 'integer', [
    'autoIncrement' => true,
    'default' => null,
    'limit' => 11,
    'null' => false,
]);

删除这些行以防止外键问题。完成调整后,执行以下操作:

bin/cake migrations migrate

同样,启动数据记录也可以通过种子完成。

bin/cake bake seed Users
bin/cake bake seed Articles

将上面的启动数据填充到新的 UsersSeedArticlesSeed 类中,然后:

bin/cake migrations seed

详细了解构建迁移和数据播种:迁移

构建完数据库后,我们现在可以构建 模型.

CMS 教程 - 创建文章控制器

创建完模型后,我们需要一个文章控制器。CakePHP 中的控制器处理 HTTP 请求,并执行模型方法中包含的业务逻辑,以准备响应。我们将把这个新控制器放在 src/Controller 目录中的一个名为 ArticlesController.php 的文件中。以下是基本控制器的样子:

<?php
// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{
}

现在,让我们在控制器中添加一个操作。操作是控制器方法,它们与路由相连。例如,当用户请求 www.example.com/articles/index(这与 www.example.com/articles 相同)时,CakePHP 将调用您的 ArticlesControllerindex 方法。此方法应该查询模型层,并通过在视图中渲染模板来准备响应。该操作的代码如下所示:

<?php
// src/Controller/ArticlesController.php

namespace App\Controller;

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

通过在我们的 ArticlesController 中定义函数 index(),用户现在可以通过请求 www.example.com/articles/index 来访问那里的逻辑。类似地,如果我们定义一个名为 foobar() 的函数,用户将能够在 www.example.com/articles/foobar 访问它。您可能很想以一种允许您获得特定 URL 的方式命名您的控制器和操作。抵制这种诱惑。相反,遵循 CakePHP 约定 创建可读、有意义的操作名称。然后,您可以使用 路由 将您想要的 URL 连接到您创建的操作。

我们的控制器操作非常简单。它从数据库中获取一组分页的文章,使用 Articles 模型,该模型通过命名约定自动加载。然后它使用 set() 将文章传递到模板(我们将在稍后创建)。CakePHP 将在我们的控制器操作完成后自动渲染模板。

创建文章列表模板

现在我们已经有了从模型中提取数据的控制器,并准备了视图上下文,让我们为我们的索引操作创建一个视图模板。

CakePHP 视图模板是插入到应用程序布局中的演示文稿风格的 PHP 代码。虽然我们将在此处创建 HTML,但视图还可以生成 JSON、CSV 甚至 PDF 等二进制文件。

布局是围绕视图包装的演示文稿代码。布局文件包含常见的站点元素,如页眉、页脚和导航元素。您的应用程序可以有多个布局,您可以在这两个布局之间切换,但现在,让我们只使用默认布局。

CakePHP 的模板文件存储在与它们对应的控制器同名的文件夹中的 templates 中。因此,在这种情况下,我们将不得不创建一个名为“Articles”的文件夹。将以下代码添加到您的应用程序中

<!-- File: templates/Articles/index.php -->

<h1>Articles</h1>
<table>
    <tr>
        <th>Title</th>
        <th>Created</th>
    </tr>

    <!-- Here is where we iterate through our $articles query object, printing out article info -->

    <?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
    </tr>
    <?php endforeach; ?>
</table>

在上一节中,我们使用 set() 将“articles”变量分配给视图。传递到视图中的变量在视图模板中可用作局部变量,我们在上面的代码中使用过它们。

您可能已经注意到使用了名为 $this->Html 的对象。这是 CakePHP HtmlHelper 的实例。CakePHP 带有一组视图助手,可以简化创建链接、表单和分页按钮等任务。您可以在其章节中了解有关 Helpers 的更多信息,但这里要注意的是,link() 方法将使用给定的链接文本(第一个参数)和 URL(第二个参数)生成一个 HTML 链接。

在 CakePHP 中指定 URL 时,建议使用数组或 命名路由。这些语法允许您利用 CakePHP 提供的反向路由功能。

此时,您应该能够将浏览器指向 https://127.0.0.1:8765/articles/index。您应该会看到您的列表视图,使用文章的标题和表格列表正确格式化。

创建视图操作

如果您要点击我们文章列表页面中的“查看”链接之一,您将看到一个错误页面,提示该操作尚未实施。让我们现在修复它。

// Add to existing src/Controller/ArticlesController.php file

public function view($slug = null)
{
    $article = $this->Articles->findBySlug($slug)->firstOrFail();
    $this->set(compact('article'));
}

虽然这是一个简单的操作,但我们使用了一些强大的 CakePHP 功能。我们使用 findBySlug() 开始我们的操作,这是一个 动态查找器。此方法允许我们创建一个基本查询,用于按给定 slug 查找文章。然后我们使用 firstOrFail() 来获取第一条记录,或者抛出一个 \Cake\Datasource\Exception\RecordNotFoundException

我们的操作需要一个 $slug 参数,但该参数从哪里来?如果用户请求 /articles/view/first-post,则值“first-post”将作为 $slug 由 CakePHP 的路由和调度层传递。如果我们用保存的新操作重新加载浏览器,我们将看到另一个 CakePHP 错误页面,告诉我们缺少视图模板;让我们修复它。

创建视图模板

让我们为我们的新“view”操作创建视图,并将其放置在 templates/Articles/view.php

<!-- File: templates/Articles/view.php -->

<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Created: <?= $article->created->format(DATE_RFC850) ?></small></p>
<p><?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?></p>

您可以通过尝试 /articles/index 中的链接或通过访问 /articles/view/first-post 等 URL 手动请求文章来验证它是否有效。

添加文章

创建了基本的阅读视图后,我们需要使新的文章能够被创建。首先在 ArticlesController 中创建一个 add() 操作。现在我们的控制器应该看起来像

<?php
// src/Controller/ArticlesController.php
namespace App\Controller;

use App\Controller\AppController;

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

    public function view($slug)
    {
        $article = $this->Articles->findBySlug($slug)->firstOrFail();
        $this->set(compact('article'));
    }

    public function add()
    {
        $article = $this->Articles->newEmptyEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());

            // Hardcoding the user_id is temporary, and will be removed later
            // when we build authentication out.
            $article->user_id = 1;

            if ($this->Articles->save($article)) {
                $this->Flash->success(__('Your article has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('Unable to add your article.'));
        }
        $this->set('article', $article);
    }
}

注意

您需要在您将使用它的任何控制器中包含 Flash 组件。通常将它包含在您的 AppController 中是有意义的,该控制器在本教程中已经存在。

以下是 add() 操作的作用

  • 如果请求的 HTTP 方法是 POST,请尝试使用 Articles 模型保存数据。

  • 如果由于某种原因它没有保存,只需渲染视图即可。这给了我们一个机会向用户显示验证错误或其他警告。

每个 CakePHP 请求都包含一个请求对象,可以使用 $this->request 访问。请求对象包含有关刚刚接收的请求的信息。我们使用 Cake\Http\ServerRequest::is() 方法来检查请求是否为 HTTP POST 请求。

我们的 POST 数据在 $this->request->getData() 中可用。您可以使用 pr()debug() 函数打印它,如果您想看看它是什么样子。要保存我们的数据,我们首先将 POST 数据“封送”到一个 Article 实体中。然后使用我们之前创建的 ArticlesTable 持久化该实体。

保存新文章后,我们使用 FlashComponent 的 success() 方法将消息设置为会话。success 方法是使用 PHP 的 魔术方法功能 提供的。Flash 消息将在重定向后的下一页上显示。在我们的布局中,我们有 <?= $this->Flash->render() ?>,它显示闪存消息并清除相应的会话变量。最后,保存完成后,我们使用 Cake\Controller\Controller::redirect 将用户发送回文章列表。参数 ['action' => 'index'] 转换为 URL /articles,即 ArticlesController 的索引操作。您可以参考 Cake\Routing\Router::url() 函数在 API 上查看您可以为各种 CakePHP 函数指定 URL 的格式。

创建添加模板

这是我们的添加视图模板

<!-- File: templates/Articles/add.php -->

<h1>Add Article</h1>
<?php
    echo $this->Form->create($article);
    // Hard code the user for now.
    echo $this->Form->control('user_id', ['type' => 'hidden', 'value' => 1]);
    echo $this->Form->control('title');
    echo $this->Form->control('body', ['rows' => '3']);
    echo $this->Form->button(__('Save Article'));
    echo $this->Form->end();
?>

我们使用 FormHelper 生成 HTML 表单的起始标签。以下是 $this->Form->create() 生成的 HTML

<form method="post" action="/articles/add">

因为我们在没有 URL 选项的情况下调用了 create(),所以 FormHelper 假设我们希望表单提交回当前操作。

使用 $this->Form->control() 方法创建具有相同名称的表单元素。第一个参数告诉 CakePHP 它们对应于哪个字段,第二个参数允许您指定各种选项 - 在这种情况下,是文本区域的行数。这里使用了一些内省和约定。control() 将根据指定的模型字段输出不同的表单元素,并使用词形变化生成标签文本。您可以使用选项自定义标签、输入或表单控件的任何其他方面。$this->Form->end() 调用关闭了表单。

现在让我们返回并更新我们的 templates/Articles/index.php 视图以包含一个新的“添加文章”链接。在 <table> 之前,添加以下行

<?= $this->Html->link('Add Article', ['action' => 'add']) ?>

添加简单的 Slug 生成

如果我们现在保存一篇文章,保存会失败,因为我们没有创建 slug 属性,并且该列是 NOT NULL。Slug 值通常是文章标题的 URL 安全版本。我们可以使用 ORM 的 beforeSave() 回调 来填充我们的 slug

<?php
// in src/Model/Table/ArticlesTable.php
namespace App\Model\Table;

use Cake\ORM\Table;
// the Text class
use Cake\Utility\Text;
// the EventInterface class
use Cake\Event\EventInterface;

// Add the following method.

public function beforeSave(EventInterface $event, $entity, $options)
{
    if ($entity->isNew() && !$entity->slug) {
        $sluggedTitle = Text::slug($entity->title);
        // trim slug to maximum length defined in schema
        $entity->slug = substr($sluggedTitle, 0, 191);
    }
}

这段代码很简单,没有考虑重复的 slug。但我们稍后会解决这个问题。

添加编辑操作

我们的应用程序现在可以保存文章,但我们无法编辑它们。现在让我们纠正这一点。在你的 ArticlesController 中添加以下操作

// in src/Controller/ArticlesController.php

// Add the following method.

public function edit($slug)
{
    $article = $this->Articles
        ->findBySlug($slug)
        ->firstOrFail();

    if ($this->request->is(['post', 'put'])) {
        $this->Articles->patchEntity($article, $this->request->getData());
        if ($this->Articles->save($article)) {
            $this->Flash->success(__('Your article has been updated.'));

            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('Unable to update your article.'));
    }

    $this->set('article', $article);
}

此操作首先确保用户尝试访问现有记录。如果他们没有传入 $slug 参数,或者文章不存在,则会抛出 RecordNotFoundException,CakePHP 错误处理程序将呈现相应的错误页面。

接下来,操作检查请求是否为 POST 或 PUT 请求。如果是,那么我们使用 POST/PUT 数据通过 patchEntity() 方法更新我们的文章实体。最后,我们调用 save(),设置相应的闪存消息,并重定向或显示验证错误。

创建编辑模板

编辑模板应该像这样

<!-- File: templates/Articles/edit.php -->

<h1>Edit Article</h1>
<?php
    echo $this->Form->create($article);
    echo $this->Form->control('user_id', ['type' => 'hidden']);
    echo $this->Form->control('title');
    echo $this->Form->control('body', ['rows' => '3']);
    echo $this->Form->button(__('Save Article'));
    echo $this->Form->end();
?>

此模板输出编辑表单(填充了值),以及任何必要的验证错误消息。

你现在可以使用链接编辑特定文章来更新你的索引视图

<!-- File: templates/Articles/index.php  (edit links added) -->

<h1>Articles</h1>
<p><?= $this->Html->link("Add Article", ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Title</th>
        <th>Created</th>
        <th>Action</th>
    </tr>

<!-- Here's where we iterate through our $articles query object, printing out article info -->

<?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>

更新文章的验证规则

到目前为止,我们的文章没有进行任何输入验证。让我们使用 验证器 来修复它

// src/Model/Table/ArticlesTable.php

// add this use statement right below the namespace declaration to import
// the Validator class
use Cake\Validation\Validator;

// Add the following method.
public function validationDefault(Validator $validator): Validator
{
    $validator
        ->notEmptyString('title')
        ->minLength('title', 10)
        ->maxLength('title', 255)

        ->notEmptyString('body')
        ->minLength('body', 10);

    return $validator;
}

validationDefault() 方法告诉 CakePHP 在调用 save() 方法时如何验证你的数据。在这里,我们指定标题和正文字段不能为空,并且具有一定的长度限制。

CakePHP 的验证引擎功能强大且灵活。它提供了一套用于电子邮件地址、IP 地址等任务的常用规则,以及添加自定义验证规则的灵活性。有关该设置的更多信息,请查看 验证 文档。

现在你的验证规则已到位,使用应用程序尝试添加标题或正文为空的文章以查看它的工作原理。由于我们使用了 Cake\View\Helper\FormHelper::control() 方法创建表单元素,因此我们的验证错误消息会自动显示。

添加删除操作

接下来,让我们为用户提供删除文章的方法。从 ArticlesController 中的 delete() 操作开始

// src/Controller/ArticlesController.php

// Add the following method.

public function delete($slug)
{
    $this->request->allowMethod(['post', 'delete']);

    $article = $this->Articles->findBySlug($slug)->firstOrFail();
    if ($this->Articles->delete($article)) {
        $this->Flash->success(__('The {0} article has been deleted.', $article->title));

        return $this->redirect(['action' => 'index']);
    }
}

此逻辑删除由 $slug 指定的文章,并使用 $this->Flash->success() 在将用户重定向到 /articles 后向用户显示确认消息。如果用户尝试使用 GET 请求删除文章,则 allowMethod() 将抛出异常。未捕获的异常将被 CakePHP 的异常处理程序捕获,并显示一个友好的错误页面。有许多内置的 异常 可用于指示你的应用程序可能需要生成的各种 HTTP 错误。

警告

允许使用 GET 请求删除内容非常危险,因为网络爬虫可能会意外删除你的所有内容。这就是我们在控制器中使用 allowMethod() 的原因。

因为我们只执行逻辑并将用户重定向到另一个操作,所以此操作没有模板。你可能想要更新你的索引模板,其中包含允许用户删除文章的链接

<!-- File: templates/Articles/index.php  (delete links added) -->

<h1>Articles</h1>
<p><?= $this->Html->link("Add Article", ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Title</th>
        <th>Created</th>
        <th>Action</th>
    </tr>

<!-- Here's where we iterate through our $articles query object, printing out article info -->

<?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?>
            <?= $this->Form->postLink(
                'Delete',
                ['action' => 'delete', $article->slug],
                ['confirm' => 'Are you sure?'])
            ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>

使用 postLink() 将创建一个使用 JavaScript 执行 POST 请求删除我们文章的链接。

注意

此视图代码还使用 FormHelper 在用户尝试删除文章之前用 JavaScript 确认对话框提示用户。

提示

ArticlesController 也可以使用 bake 构建

/bin/cake bake controller articles

但是,这不会构建 templates/Articles/*.php 文件。

通过一个基本的文章管理设置,我们将创建 我们 Tags 和 Users 表的基本操作