🔥

CodeIgniter4入門 公式チュートリアル (3)DBへのデータの追加

2021/12/07に公開

CodeIgniter Advent Calendar 2021

CodeIgniter4入門 公式チュートリアル (2)DBデータの表示
の続きです。

データベースへの書き込みを実装します。

目次

CSRFフィルターの設定

CSRF対策のための設定を少し変更します。

app/Config/Filters.php を以下のように変更してください。

--- a/app/Config/Filters.php
+++ b/app/Config/Filters.php
@@ -30,7 +30,7 @@ class Filters extends BaseConfig
     public $globals = [
         'before' => [
             // 'honeypot',
-            'csrf',
+            // 'csrf',
         ],
         'after' => [
             'toolbar',
@@ -47,7 +47,9 @@ class Filters extends BaseConfig
      *
      * @var array
      */
-    public $methods = [];
+    public $methods = [
+        'post' => ['csrf'],
+    ];
 
     /**
      * List of filter aliases that should run on any

全てのリクエストで csrf フィルターを有効にしていたものを、POSTメソッドのリクエストに対してのみ有効にするように変更しました。

フォームの作成

記事を書き込むためのビューファイル app/Views/news/create.php を作成します。

<h2><?= esc($title) ?></h2>

<?= session()->getFlashdata('error') ?>
<?= service('validation')->listErrors() ?>

<form action="/news/create" method="post">
    <?= csrf_field() ?>

    <label for="title">Title</label>
    <input type="input" name="title" /><br />

    <label for="body">Text</label>
    <textarea name="body" cols="45" rows="4"></textarea><br />

    <input type="submit" name="submit" value="Create news item" />
</form>

session() 関数は、セッションオブジェクトを返します。
session()->getFlashdata('error') は、セッションに保存されているフラッシュデータ error を返します。
これは、csrfフィルターがトークンの検証に失敗した場合に、エラーメッセージを表示します。

service() 関数は、CodeIgniter4のサービスロケータ(\Config\Services)へのラッパーです。
CodeIgniter4では、フレームワークのクラスはサービスとして、サービスロケータから取得できます。

service('validation') は、CodeIgniter4の Validation オブジェクトを返します。
Validation クラスの listErrors() メソッドは、検証エラーをHTMLのリストで返します。

csrf_field() 関数は、CSRF対策のためのトークンをフォームに追加します。

フォームを受け取るためのコントローラメソッドの追加

News コントローラにフォームを受け取るための create() メソッドを追加します。

--- a/app/Controllers/News.php
+++ b/app/Controllers/News.php
@@ -37,4 +37,26 @@ class News extends Controller
         echo view('news/view', $data);
         echo view('templates/footer', $data);
     }
+
+    public function create()
+    {
+        $model = model(NewsModel::class);
+
+        if ($this->request->getMethod() === 'post' && $this->validate([
+            'title' => 'required|min_length[3]|max_length[255]',
+            'body'  => 'required',
+        ])) {
+            $model->save([
+                'title' => $this->request->getPost('title'),
+                'slug'  => url_title($this->request->getPost('title'), '-', true),
+                'body'  => $this->request->getPost('body'),
+            ]);
+
+            echo view('news/success');
+        } else {
+            echo view('templates/header', ['title' => 'Create a news item']);
+            echo view('news/create');
+            echo view('templates/footer');
+        }
+    }
 }

コントローラの $this->validate() メソッドは、リクエストのデータを検証するための便利なメソッドです。

CodeIgniter\Model クラスの save() メソッドは、データを挿入または更新するメソッドです。今回は、プライマリキーの指定がないため全て挿入処理になります。

書き込み成功時のビューの追加

書き込みが成功した旨のメッセージを表示するための app/Views/news/success.php を作成します。

News item created successfully.

NewsModelの変更

NewsModel がデータをDBに保存できるように、$allowedFields プロパティに変更可能なカラム名を指定します。

--- a/app/Models/NewsModel.php
+++ b/app/Models/NewsModel.php
@@ -8,6 +8,8 @@ class NewsModel extends Model
 {
     protected $table = 'news';
 
+    protected $allowedFields = ['title', 'slug', 'body'];
+
     public function getNews($slug = false)
     {
         if ($slug === false) {

ルーティングの追加

News コントローラの create() メソッドへのルートを追加します。

--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -33,6 +33,7 @@ $routes->setAutoRoute(true);
 // route since we don't have to scan directories.
 $routes->get('/', 'Home::index');
 
+$routes->match(['get', 'post'], 'news/create', 'News::create');
 $routes->get('news/(:segment)', 'News::view/$1');
 $routes->get('news', 'News::index');
 $routes->get('(:any)', 'Pages::view/$1');

news/create にGETまたはPOSTメソッドでアクセスされた場合に、News::create() メソッドを呼び出します。

記事の追加

では、http://localhost/news/create にアクセスしてみましょう。

記事を入力します。

「Create news item」ボタンを押します。

書き込みに成功しました。

右下のプロファイラから、データベースクエリを確認してみます。

ニュースの一覧を表示してみます。

一番下に追加した記事が表示されています。

以上で公式チュートリアルの内容は完了しました。データベースからの読み込みと書き込み処理までが含まれています。

NewsコントローラとNewsModel

コントローラとモデルのコードは以下のようになっています。

app/Controllers/News.php

<?php

namespace App\Controllers;

use App\Models\NewsModel;
use CodeIgniter\Controller;

class News extends Controller
{
    public function index()
    {
        $model = model(NewsModel::class);

        $data = [
            'news'  => $model->getNews(),
            'title' => 'News archive',
        ];

        echo view('templates/header', $data);
        echo view('news/overview', $data);
        echo view('templates/footer', $data);
    }

    public function view($slug = null)
    {
        $model = model(NewsModel::class);

        $data['news'] = $model->getNews($slug);

        if (empty($data['news'])) {
            throw new \CodeIgniter\Exceptions\PageNotFoundException('Cannot find the news item: ' . $slug);
        }

        $data['title'] = $data['news']['title'];

        echo view('templates/header', $data);
        echo view('news/view', $data);
        echo view('templates/footer', $data);
    }

    public function create()
    {
        $model = model(NewsModel::class);

        if ($this->request->getMethod() === 'post' && $this->validate([
            'title' => 'required|min_length[3]|max_length[255]',
            'body'  => 'required',
        ])) {
            $model->save([
                'title' => $this->request->getPost('title'),
                'slug'  => url_title($this->request->getPost('title'), '-', true),
                'body'  => $this->request->getPost('body'),
            ]);

            echo view('news/success');
        } else {
            echo view('templates/header', ['title' => 'Create a news item']);
            echo view('news/create');
            echo view('templates/footer');
        }
    }
}

app/Models/NewsModel.php

<?php

namespace App\Models;

use CodeIgniter\Model;

class NewsModel extends Model
{
    protected $table = 'news';

    protected $allowedFields = ['title', 'slug', 'body'];

    public function getNews($slug = false)
    {
        if ($slug === false) {
            return $this->findAll();
        }

        return $this->where(['slug' => $slug])->first();
    }
}

参考

Discussion