Cakephp 3 – Hướng dẫn tạo Articles Controller

Ở bài này chúng ta cùng tạo ra các chức năng cơ bản: thêm, sửa, xoá, xem các bài viết trong bảng Articles đã tạo trong Bài 1.

Các file controller trong CakePHP 3 được tạo trong src/Controller, một controller cơ bản sẽ giống như bên dưới:


<?php
namespace App\Controller;

class ArticlesController extends AppController
{
}

Bây giờ chúng ta tạo action cho controller trên, đầu tiên là action index tương đương với url: http://localhost:8888/cakephp_3_7_2/articles/index(http://localhost:8888/cakephp_3_7_2/articles). Và trong action này sẽ là các truy vấn với Model và trả dữ liệu ra Template trong View.


<?php
namespace App\Controller;
class ArticlesController extends AppController
{
public function index()
{
$this->loadComponent('Paginator');
$articles = $this->Paginator->paginate($this->Articles->find());
$this->set(compact('articles'));
}
}

Action mặc định trong một controller sẽ là index(), chúng ta có thể chỉnh sửa lại url trên thông qua Routing, việc đó sẽ được tìm hiểu ở các bài sau. Trong action trên chúng ta sẽ lấy tất cả các bài viết thông qua find() và sử dụng component Paginator để phân trang dữ liệu. Sau đó, sử dụng phương thức set() để chuyển dữ liệu ra ngoài view với biến $articles.

Tạo Template danh sách Articles.

Nơi để lưu các file ngoài view là src/Template bên trong 1 thư mục tương ứng với Controller, ở đây ArticlesController thì thư mục này sẽ tên là Articles. Lần lượt tạo thư mục Articles và file index.ctp có nội dung như sau:


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

<h1>Articles</h1>
<table>
<tr>
<th>Title</th>
<th>Created</th>
</tr>
<?php foreach ($articles as $article): ?>
<tr>
<td>
<?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
</td>
<td>
<?= $article->created->format('Y-m-d H:i:s') ?>
</td>
</tr>
<?php endforeach; ?>
</table>

Trong đoạn trên sử dụng HTMLHelper để thực hiện tạo ra các liên kết cụ thể đây là thẻ <a> bằng link(), tham số thứ nhất là text để hiển thị và tham số thứ 2 là URL chuyển hướng. Với Helpers chúng ta cũng sẽ tìm hiểu kỉ hơn ở bài khác.

Bây giờ tiến hành chạy url: http://localhost:8888/cakephp_3_7_2/articles chúng ta sẽ thấy danh sách các bài trong bảng articles, như hình sau:

Tạo action View để xem chi tiết 1 bài viết.

Khi click vào tên bài viết ở trang danh sách chúng ta sẽ chuyển sang action view này với url: http://localhost:8888/cakephp_3_7_2/articles/view/first-post, ‘first-post’ là slug


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

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

Với việc sử dụng slug của tiêu đề bài viết, chúng ta sẽ dựa vào nó để truy vấn dữ liệu bằng findBySlug(),  lấy ra phần tử đầu tiên hoặc ngoại lệ NotFoundException bằng firstOrFail().

Phương thức set() để đưa dữ liệu ra ngoài view. Các bạn có thể thay thế slug bằng id của bài viết để lấy dữ liệu. Còn nếu sử dụng slug thì khi insert, update phải kiểm tra, chuyển nó về dạng slug(đặt biệt đối với tiếng việt có dấu).

Tạo Template xem chi tiết bài viết

Chúng ta sẽ tạo file view.ctp trong 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('Y-m-d H:i:s') ?></small></p>
<p><?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?></p>

Truy cập lại vào danh sách và click các bài viết để xem chi tiết.

Tạo action thêm bài viết

Chúng ta sẽ tạo thêm bài viết với action add() trong ArticlesController.php, đến đây nội dung của nó như sau:


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

namespace App\Controller;

class ArticlesController extends AppController
{
public function initialize()
{
parent::initialize();

$this->loadComponent('Paginator');
$this->loadComponent('Flash'); // Include the FlashComponent
}
public function index()
{
$articles = $this->Paginator->paginate($this->Articles->find());
$this->set(compact('articles'));
}
public function view($slug = null)
{
$article = $this->Articles->findBySlug($slug)->firstOrFail();
$this->set(compact('article'));
}
public function add()
{
$article = $this->Articles->newEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->getData());
$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);
}

Ở đoạn trên có sử dụng thêm Component Flash để show thông báo ra ngoài view, action add() hoạt động như sau:

Nếu là phương thức POST được sử dụng thì tiến hành lưu dữ liệu thông qua model Articles, nếu không lưu được thì trả về form thêm dữ liệu, chúng ta có thể hiển thị lỗi với trường hợp này.

$this->request->getData(); được sử dụng để lấy dữ liệu được post qua

Để lưu được dữ liệu, đưa các dữ liệu thông qua Article Entity( $this->Articles->patchEntity()) . Nếu có một trường nào đó trong bảng dữ liệu không được post qua chúng ta có thể default lại giá trị giống như code ở trên ( $article->user_id = 1; ) user_id hiện tại chưa có nên mặc định cho bằng 1.

Tiến hành save lại dữ liệu $this->Articles->save($articles); thành công thì dùng $this->Flash->success() để hiện thị thông báo. Layout hiển thị thông báo là <?= $this->Flash->render() ?> ở file src/Template/Layout/default.ctp.

Cuối cùng sử dụng $this->redirect để di chuyển về action cần thiết.

Tạo Template thêm bài viết


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

<h1>Add Article</h1>
<p><?= $this->Html->link('Articles', ['action' => 'index']) ?></p>
<?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();
?>

Cách thức trên cũng chính là FormHelper được CakePHP hỗ trợ:

$this->Form->create(); tạo form

$this->Form->control(); tạo các input

$this->Form->button(); tạo các button trong form

$this->Form->end(); kết thúc form

Quay trở lại src/Template/Articles/index.ctp, tạo nút Add để có thể truy cập vào chức năng thêm bài viết, bằng cách thêm <p><?= $this->Html->link(‘Add Article’, [‘action’ => ‘add’]) ?></p>

Thêm Slug trong model Articles

Nếu hiện tại tiến hành thêm dữ liệu sẽ không thành công, vì giá trị slug trong dữ liệu được thêm vào không tồn tại và ở table Articles có trường slug NOT NULL. Giá trị slug được lấy dựa vào title sau khi xử lý, ở đây quá trình xử lý chỉ làm ở mức đơn giản, nó sẽ có lỗi với tiếng việt có dấu.


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

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

// Add the following method.

public function beforeSave($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);
}
}

Đương nhiên với đoạn code trên vẫn chưa có kiểm tra tính trùng lặp của dữ liệu.

Thêm action Edit Bài viết

Thêm đoạn sau trong ArticlesController.php


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);
}

Ở code trên nó khá giống với action Add, chỉ khác ở chổ có thêm đoạn kiểm tra $slug để lấy ra dữ liệu và đưa ra ngoài view, cùng với đó là kiểm tra xem có tồn tại 2 phương thức POST hoặc PUT, rồi tiến hành cập nhật dữ liệu.

Tạo Template Edit Bài viết

Chúng ta sẽ tiến hành tạo file edit.ctp trong src/Template/Articles/edit.ctp, có nội dung gần giống với add.ctp


<h1>Edit Article</h1>
<p><?= $this->Html->link('Articles', ['action' => 'index']) ?></p>
<?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();
?>

Việc cần làm là thêm link để từ danh sách có thể truy cập vào edit từng bài viết. Chúng ta sửa lại file danh sách (index.ctp)


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

<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 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('Y-m-d H:i:s') ?>
</td>
<td>
<?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>

Validation Articles đơn giản

Ở thời điểm hiện tại nếu thêm hoặc edit sẽ không có kiểm tra các input được thêm vào. Và để làm được điều này thì validation là cái được sử dụng:

Thêm đoạn validation đơn giản sau vào src/Model/Table/ArticlesTable.php


public function validationDefault(Validator $validator)
{
$validator
->notEmpty('title', 'Please fill this field')
->minLength('title', 10)
->maxLength('title', 255)

->requirePresence('body')
->notEmpty('body')
->add('body', 'length', [
'rule' => ['minLength', 10],
'message' => 'Articles must have a substantial body.'
]
);

return $validator;
}

validationDefault() là phương thức sẽ được gọi khi trong controller gọi save(), về cách sử dụng Validation chúng ta sẽ có một bài riêng nữa. Các bạn có thể test bằng cách nhập liệu title hoặc body dưới 10 kí tự sẽ xuất hiện thông báo lỗi tương ứng được default trong đoạn code trên.

Tạo action Xoá Bài viết

Bây giờ là action xoá một bài viết


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']);
}
}

Tương tự với các action trên là việc kiểm tra liệu dữ vào $slug, tiến hành xoá bằng delete() trong model và show thông báo đã xoá thành công trong $this->Flash->success, rồi $this->redirect về danh sách. Ở đây có sử dụng $this->request->allowMethod([‘post’, ‘delete’]); đây là cách mà CakePHP hạn chế các phương thức được truyền đến action trong controller, với đoạn này thì chỉ cho phép xoá khi có phương thức là post hoặc delete thôi, các phương thức khác sẽ có lỗi.

Cuối cùng để xoá được cũng cần thêm vào danh sách một link thực hiện tác vụ này. Nội dung file index.ctp được thay đổi như sau:


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

<h1>Articles</h1>
<p><?= $this->Html->link('Add Article', ['action' => 'add']) ?></p>
<table>
<tr>
<th>Title</th>
<th>Created</th>
<th>Action</th>
</tr>
<?php foreach ($articles as $article): ?>
<tr>
<td>
<?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
</td>
<td>
<?= $article->created->format('Y-m-d H:i:s') ?>
</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>

Chúng ta sử dụng $this->Form->postLink() là phương thức của FormHelper, với cách thức trên sẽ tạo link sử dụng Javascript để POST dữ liệu qua action delete() trong controller.

Xin được kết thúc tại đây, ở bài này chúng ta làm quen với cách tạo một controller, model, view, cách post dữ liệu, validation đơn giản. Tất cả những cái này chỉ mới làm quen thôi, mình sẽ đi sâu hơn từng cái ở các bài tiếp theo.

Kết luận

  1. Nếu có thắc mắc gì các bạn để lại comment bên dưới mình sẽ trả lời sớm nhất có thể.
  2. Cảm ơn các bạn đã đọc.

Nongdanit.info
[Cakephp 3] Bài 2 – Hướng dẫn tạo Articles Controller