CakePHP系列(二)----博客(Blog)案例(二)

创建文章模型

模型是CakePHP应用程序的面包和黄油。 通过创建一个将与我们的数据库交互的CakePHP模型,我们将有需要的基础来执行我们的查看,添加,编辑和删除操作。


CakePHP的模型类文件分为表和实体对象。表对象提供的实体集合存储在一个特定的表,然后在src/Model/Table。文件我们会创建将保存到src/Model/Table/ArticlesTable.php。完成的文件应该如下:


// src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }
}

在CakePHP命名规则是非常重要的。通过命名我们的Table对象ArticlesTable ,CakePHP可以自动推断出这个Table对象将在ArticlesController中使用,并且将绑定到一个名为articles的数据库表。


▲ 注意: 如果CakePHP在src / Model / Table中找不到相应的文件,它将为您动态创建一个模型对象。 这也意味着如果你不小心命名你的文件错误(即articlestable.php或ArticleTable.php),CakePHP将不会识别你的任何设置,并将使用生成的模型。


有关模型的更多信息,例如回调和验证,请查看本手册的数据库访问和ORM章节。


▲ 注意: 如果您完成CakePHP系列(二)----博客(Blog)案例(一)并在我们的博客数据库中创建了articles表,您可以利用CakePHP的用Bake生成功能来创建ArticlesTable模型:


有Bake生成代码功能的更多信息,请访问Bake生成代码


创建文章控制器


接下来,我们将为我们的文章创建一个控制器。 控制器是所有与文章的交互发生的地方。 简而言之,它是你使用模型中包含的业务逻辑并获得与文章相关的工作的地方。 我们将把这个新的控制器放在一个名为ArticlesController.phpsrc / Controller目录下的文件中。 下面是基本控制器应该是什么样子:

// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{
}

现在,让我们给我们的控制器添加一个动作。 操作通常表示应用程序中的单个函数或接口。 例如,当用户请求www.example.com/articles/index(也与www.example.com/articles/相同)时,他们可能会看到文章列表。 该操作的代码如下所示:


// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{

    public function index()
    {
        $articles = $this->Articles->find('all');
        $this->set(compact('articles'));
    }
}

通过在ArticlesController中定义函数index() ,用户现在可以通过请求www.example.com/articles/index来访问逻辑。 同样,如果我们要定义一个名为foobar()的函数,用户将能够访问www.example.com/articles/foobar。


您可能会试图以某种方式命名您的控制器和操作以获取某个URL。 抵制这种诱惑。 遵循CakePHP约定 (大写,复数名称等),并创建可读,可理解的动作名称。 您可以使用稍后介绍的Routing将网址映射到您的代码。


操作中的单个指令使用set()将数据从控制器传递到视图(我们将在下面创建)。 该行将视图变量“articles”设置为等于ArticlesTable对象的find('all')方法的返回值。


▲ 注意: 如果您完成CakePHP系列(二)----博客(Blog)案例(一)并在您的博客数据库中创建了articles表,您可以利用CakePHP的Bake生成代码功能来创建ArticlesController类:


bin/cake bake controller Articles

有Bake生成代码功能的更多信息,请访问 Bake生成代码

要了解有关CakePHP控制器的更多信息,请查看控制器章节


创建文章视图


现在我们的数据来自我们的模型,我们的应用程序逻辑由我们的控制器定义,让我们为上面创建的索引操作创建一个视图。

CakePHP视图只是表示风格的片段,适合应用程序的布局。 对于大多数应用程序,它们是HTML与PHP混合的,但它们可能最终作为XML,CSV或甚至二进制数据。

布局是环绕视图的表示代码。 可以定义多个布局,您可以在它们之间切换,但现在,让我们使用默认值。

记住在最后一节中我们如何使用set()方法将“articles”变量分配给视图? 这将递归查询对象集合到要使用foreach迭代调用的视图。

CakePHP的模板文件存储在src / Template文件夹中,它们对应的控制器之后(在这种情况下我们需要创建一个名为“Articles”的文件夹)。 要在一个漂亮的表中格式化这篇文章数据,我们的视图代码可能看起来像这样:

<!-- File: src/Template/Articles/index.ctp -->

<h1>Blog articles</h1>
<table>
    <tr>
        <th>Id</th>
        <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><?= $article->id ?></td>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
    </tr>
    <?php endforeach; ?>
</table>

希望这应该看起来有点简单。

你可能已经注意到使用一个名为$this->Html的对象。 这是CakePHP Cake\View\Helper\HtmlHelper类的一个实例。 CakePHP带有一组视图助手,使得事情像链接,形式输出一个快照。 你可以更多地了解如何在Helpers中使用它们,但是需要注意的是, link()方法将生成一个带有给定标题(第一个参数)和URL(第二个参数)的HTML链接。

在CakePHP中指定URL时,建议使用数组格式。 这将在“路由”一节中更详细地解释。 使用URL的数组格式允许您利用CakePHP的反向路由功能。 您还可以以/controller/action/param1/param2的形式指定相对于应用程序基本的URL,或使用命名的路由 。

此时,您应该可以将浏览器指向http://www.example.com/articles/index  您应该看到您的视图,正确格式化的文章的标题和表列表。

如果你碰巧点击了我们在这个视图中创建的链接之一(将文章的标题链接到URL /articles/view/some\_id ),那么你可能已经被CakePHP通知,该操作尚未定义。 如果你没有这么知情,或者有什么问题,或者你实际上已经定义了它,在这种情况下你是非常鬼祟。 否则,我们将在ArticlesController创建它:

// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{

    public function index()
    {
         $this->set('articles', $this->Articles->find('all'));
    }

    public function view($id = null)
    {
        $article = $this->Articles->get($id);
        $this->set(compact('article'));
    }
}

set()调用应该看起来很熟悉。 注意,我们使用get()而不是find('all') ,因为我们只想要一篇文章的信息。

请注意,我们的查看操作需要一个参数:我们希望查看的文章的ID。 此参数通过请求的URL传递到操作。 如果用户请求/articles/view/3 ,则值“3”作为$id传递。

我们还做一些错误检查,以确保用户实际访问记录。 通过在Articles表中使用get()函数,我们确保用户已经访问了一个存在的记录。 如果请求的文章不存在于数据库中,或者id为false, get()函数将抛出一个NotFoundException 。

现在让我们为新的'view'操作创建视图,并将其放在src / Template / Articles / view.ctp中

<!-- File: src/Template/Articles/view.ctp -->

<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Created: <?= $article->created->format(DATE_RFC850) ?></small></p>

通过尝试/articles/index的链接或通过访问/articles/view/{id}手动请求文章,并用文章“id”替换{id}来验证此操作是否正常。


添加文章


从数据库中读取并显示文章是一个伟大的开始,但让我们允许添加新文章。

首先,从ArticlesController中创建一个add()动作:

// src/Controller/ArticlesController.php

namespace App\Controller;

use App\Controller\AppController;

class ArticlesController extends AppController
{

    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Flash'); // Include the FlashComponent
    }

    public function index()
    {
        $this->set('articles', $this->Articles->find('all'));
    }

    public function view($id)
    {
        $article = $this->Articles->get($id);
        $this->set(compact('article'));
    }

    public function add()
    {
        $article = $this->Articles->newEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            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->requestRequest对象。 请求对象包含有关刚刚收到的请求的有用信息,可用于控制应用程序的流程。 在这种情况下,我们使用Cake\Network\Request::is()方法来检查请求是否是HTTP POST请求。

当用户使用表单向您的应用程序POST数据时,该信息位于$this->request->getData() 。 你可以使用pr()debug()函数打印出来,如果你想看看它的外观。

我们使用FlashComponent的success()error()方法来将消息设置为会话变量。 这些方法是使用PHP的magic method特性提供的 。 重定向后,页面上将显示Flash消息。 在布局中,我们有<?= $this->Flash->render() ?> ,它显示消息并清除相应的会话变量。 控制器的Cake\Controller\Controller::redirect函数重定向到另一个URL。 param['action' => 'index']转换为URL / articles,即ArticlesController的索引操作。 您可以参考API上的Cake\Routing\Router::url()函数来查看可以为其指定各种CakePHP函数的URL的格式。

调用save()方法将检查验证错误并中止保存(如果发生)。 我们将在下面的部分讨论如何处理这些错误。


数据验证


CakePHP对于从形式输入验证中获得单调性有很大的帮助。 每个人都讨厌编码无尽的形式和他们的验证程序。 CakePHP使它更容易和更快。

要利用验证功能,您需要在视图中使用CakePHP的Form助手。 Cake\View\Helper\FormHelper默认情况下可用于$this->Form中的所有视图。

这是我们的添加视图:


<!-- File: src/Template/Articles/add.ctp -->

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

我们使用FormHelper生成HTML表单的开始标签。 这里是$this->Form->create() Form- $this->Form->create()生成的HTML:

<form method= action=>

如果在没有提供参数的情况下调用create() ,则它假定您正在构建一个表单,通过POST将其提交到当前控制器的add()动作(或在表单数据中包含id时的edit()动作)。

$this->Form->input()方法用于创建同名的表单元素。 第一个参数告诉CakePHP它们对应的字段,第二个参数允许你指定一个宽的数组 - 在这种情况下,textarea的行数。 这里有一点自省和自动: input()将根据指定的模型字段输出不同的表单元素。

$this->Form->end()调用结束表单。 如果启用CSRF /表单防篡改,则输出隐藏输入。

现在让我们回去更新我们的src / Template / Articles / index.ctp视图,包括一个新的“添加文章”链接。 <table>之前,添加以下行:

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

您可能想知道:我如何告诉CakePHP关于我的验证要求? 验证规则在模型中定义。 让我们回顾一下我们的文章模型,并做一些调整:

// src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }

    public function validationDefault(Validator $validator)
    {
        $validator
            ->notEmpty('title')
            ->requirePresence('title')
            ->notEmpty('body')
            ->requirePresence('body');

        return $validator;
    }
}

validationDefault()方法告诉CakePHP如何在调用save()方法时验证数据。 在这里,我们已经指定body和title字段不能为空,并且对于创建和更新操作都是必需的。 CakePHP的验证引擎是强大的,有一些预建的规则(信用卡号码,电子邮件地址等)和灵活性,以添加您自己的验证规则。 有关该设置的更多信息,请查看验证文档。

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


编辑文章


后编辑:这里我们去。 你现在是一个CakePHP专家,所以你应该选择一个模式。 做动作,然后视图。 这里是ArticlesControlleredit()动作将是什么样子:

// src/Controller/ArticlesController.php

public function edit($id = null)
{
    $article = $this->Articles->get($id);
    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);
}

此操作首先确保用户已尝试访问现有记录。 如果他们没有传入一个$id参数,或者文章不存在,我们抛出一个NotFoundException为CakePHP ErrorHandler照顾。

接下来,操作将检查请求是POST还是PUT请求。 如果是,那么我们使用POST数据通过使用patchEntity()方法来更新我们的article实体。 最后,我们使用表对象来保存实体或回退并显示用户验证错误。

编辑视图可能如下所示:

<!-- File: src/Template/Articles/edit.ctp -->

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

此视图输出编辑表单(带有填充的值),以及任何必要的验证错误消息。

CakePHP将确定一个save()是否基于实体的状态生成一个插入或更新语句。

您现在可以通过链接更新索引视图,以编辑特定文章:

<!-- File: src/Template/Articles/index.ctp  (edit links added) -->

<h1>Blog articles</h1>
<p><?= $this->Html->link(, ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Id</th>
        <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><?= $article->id ?></td>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Edit', ['action' => 'edit', $article->id]) ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>


删除文章


接下来,让我们让用户删除文章。 ArticlesController中的delete()动作ArticlesController :

// src/Controller/ArticlesController.php

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

    $article = $this->Articles->get($id);
    if ($this->Articles->delete($article)) {
        $this->Flash->success(__('The article with id: {0} has been deleted.', h($id)));
        return $this->redirect(['action' => 'index']);
    }
}

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

因为我们只是执行一些逻辑和重定向,这个动作没有视图。 您可能希望使用允许用户删除文章的链接更新索引视图,但是:

<!-- File: src/Template/Articles/index.ctp (delete links added) -->

<h1>Blog articles</h1>
<p><?= $this->Html->link('Add Article', ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
        <th>Actions</th>
    </tr>

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

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

</table>

使用View\Helper\FormHelper::postLink()将创建一个链接,使用JavaScript来执行POST请求删除我们的文章。


▲ 注意: 允许使用GET请求删除内容很危险,因为网络抓取工具可能会意外删除您的所有内容。此视图代码还使用FormHelper在尝试删除文章之前提示用户使用JavaScript确认对话框。


路由


对于一些,CakePHP的默认路由工作得很好。 对用户友好和一般搜索引擎兼容性敏感的开发人员将会喜欢CakePHP的URL映射到特定操作的方式。 因此,我们将在本教程中快速更改路线。

有关高级路由技术的详细信息,请参阅连接路由 。

默认情况下,CakePHP使用其PagesController响应对您网站根目录的请求(例如http://www.example.com ),渲染一个名为“home”的视图。 相反,我们将通过创建一个路由规则来替换为ArticlesController。

CakePHP的路由在config / routes.php中找到。 您需要注释掉或删除定义默认根路由的行。 它看起来像这样:

$routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);


此行将URL“/”与默认CakePHP主页连接。 我们希望它与我们自己的控制器连接,所以用这个替换线:

$routes->connect('/', ['controller' => 'Articles', 'action' => 'index']);


这应该连接用户请求'/'到我们的ArticlesControllerindex()动作。


▲ 注意: CakePHP也使用“反向路由”。 如果在定义了上述路由的情况下,将['controller' => 'Articles', 'action' => 'index']传递给需要数组的函数,则使用的结果URL将为'/'。 因此,最好始终为网址使用数组,因为这意味着您的路由会定义网址所在的位置,并确保链接指向相同的位置。

结论


以这种方式创建应用程序将赢得你的和平,荣誉,爱和金钱,甚至超出了你最疯狂的幻想。 简单,不是吗? 请记住,本教程是非常基本的。 CakePHP有更多的功能,并且灵活的方式,我们不希望在这里为简单起见。 使用本手册的其余部分作为构建更多功能丰富的应用程序的指南。

现在您已经创建了一个基本的CakePHP应用程序,您可以继续CakePHP系列(二)----博客(Blog)案例(三) ,或开始自己的项目。 您还可以阅读使用CakePHPAPI <https://api.cakephp.org/3.0>了解有关CakePHP的更多信息。

如果您需要帮助,有很多方法可以获得您需要的帮助 - 请参阅“ 在哪里获取帮助”页面。 欢迎来到CakePHP!


建议的后续阅读


这些是人们学习CakePHP通常想要学习的常见任务:

  1. 布局 :自定义您的网站布局
  2. 元素 :包含和重复使用视图片段
  3. 使用Bake生成代码:生成基本的CRUD代码
  4. 博客教程 - 认证和授权 :用户认证和授权教程

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值