CodeIgniter4のModelの基礎


前回の記事、CodeIgniter4でMySQLのDB構築の続きです。

DBの操作はModelに書こう

MVCモデルにおけるModelとは、通常DBの操作や入力されたデータのやり取りや加工など所謂ビジネスロジックと呼ばれるものを担当するところとなります。CI4においては通常app/Models/配下に記述します。

まずはModelのファイルを作る。

今回はapp/Models/InitDB.phpというファイルを作って前回のDB生成、テーブル生成、接続テストを各々メソッドとして用意します。

特段の事情がない限りファイル名とClass名は一致させたほうが良いでしょう。

app/Models/InitDB.php
<?php

namespace App\Models;

use CodeIgniter\Model;
use Config\Database;
use Exception;

class InitDB extends Model{

    public function createDB(){
        try{
            $conf = [
                'hostname' => $_ENV['database.default.hostname'] ,
                'database' => '' ,
                'username' => $_ENV['database.default.username'] ,
                'password' => $_ENV['database.default.password'] ,
                'DBDriver' => $_ENV['database.default.DBDriver'] ,
                'charset'  => $_ENV['database.default.charset'] ,
                'DBCollat' => $_ENV['database.default.DBCollat'] ,
            ];

            $forge = Database::Forge( $conf );
            $forge->createDatabase( $_ENV['database.default.database'] );
        }catch( Exception $e ){
            throw $e;
        }
        return "データベース生成成功\n";
    }

    public function createTable(){
        try{
            $forge  = Database::Forge();
            $fields = [
                'id'       => [
                    'type'           => 'INT' ,
                    'unsigned'       => true ,
                    'auto_increment' => true ,
                ] ,
                'login_id' => [
                    'type'       => 'VARCHAR' ,
                    'constraint' => 100 ,
                    'null'       => false ,
                    'unique'     => true ,
                ] ,
                'login_pw' => [
                    'type'       => 'VARCHAR' ,
                    'constraint' => 100 ,
                    'null'       => false ,
                ] ,
            ];

            $forge->addField( $fields );
            $forge->addPrimaryKey( 'id' );
            $forge->createTable( 'users' );
        }catch( Exception $e ){
            throw $e;
        }
        return "テーブル生成成功\n";
    }

    public function connectionTest(){
        try{
            $db = Database::connect();
        }catch( Exception $e ){
            throw $e;
        }
        return "このDBのテーブル=" . implode( ',' , $db->listTables() );
    }

    public function dropDB(){
        try{
            $forge = Database::Forge();
            $forge->dropDatabase( $_ENV['database.default.database'] );
        }catch( Exception $e ){
            throw $e;
        }
        return "データベース削除成功\n";
    }
}

namespaceはこのファイルに使用する名前空間を指定します。ここではApp\Modelsと記述していますが、Modelsディレクトリ配下のファイルは基本的にこの名前空間で問題ないと思います。
useはインポートする名前空間を指定します。
名前空間についてはこの記事が大変わかりやすく書いてありおすすめです。

クラスは\CodeIgniter\Modelを継承して作ります。
はじめにuse CodeIgniter\Model;をしてるので
class InitDB extends \CodeIgniter\Modelとしなくても
class InitDB extends Modelだけで通じます。

各メソッドの内容はほぼ前回のものと同様なので説明は割愛します。
ソースの最尾部には前回なかったメソッドdropDB()を追加しましたが、これは見ての通りDBを削除するためのメソッドです。

次はControllerのファイル

これに合わせて、Controllerも書き換えます。前回書いていたファイルは次のものに差し替えてください。

app/Controllers/Home.php
<?php 
namespace App\Controllers;

use App\Models\InitDB;
use Exception;

class Home extends BaseController{
    public function index(){
        return view( 'welcome_message' );
    }

    public function dbinit(){
        try{
            $result = '<html lang="ja"><pre>';
            $initdb = new InitDB();
            $result .= $initdb->createDB();
            $result .= $initdb->createTable();
            $result .= $initdb->connectionTest();
            $result .= '</pre></html>';
        }catch( Exception $e ){
            return $e->getMessage();
        }
        return $result;
    }    

    public function db_delete(){
        try{
            $result = '<html lang="ja"><pre>';
            $initdb = new InitDB();
            $result .= $initdb->dropDB();
            $result .= '</pre></html>';
        }catch( Exception $e ){
            return $e->getMessage();
        }
        return $result;
    }    

}

ここではnamespaceがApp\Controllersとなっていて、利用したいModelとは名前空間が違うので、これを使えるようにuse App\Models\InitDB;で、モデルのクラスをインポートしてやります。

実際にインスタンスを作っているところは、new InitDB();です。

なおこれは、useを使わずに都度名前空間を書いてもOKなので、new InitDB();new \App\Models\InitDB();とした上でuse App\Models\InitDB;の行を取り去っても同じ内容となります。
例外の名前空間Exceptionも同様です。

app/Controllers/Home.phpでuseを使わない書き方
<?php 
namespace App\Controllers;

class Home extends BaseController{
    public function index(){
        return view( 'welcome_message' );
    }

    public function dbinit(){
        try{
            $result = '<html lang="ja"><pre>';
            $initdb = new \App\Models\InitDB(); // <--階層も記述する
            $result .= $initdb->createDB();
            $result .= $initdb->createTable();
            $result .= $initdb->connectionTest();
            $result .= '</pre></html>';
        }catch( \Exception $e ){  // <-- 頭に\がついている点に注意
            return $e->getMessage();
        }
        return $result;
    }

    public function db_delete(){
        try{
            $result = '<html lang="ja"><pre>';
            $initdb = new \App\Models\InitDB();
            $result .= $initdb->dropDB();
            $result .= '</pre></html>';
        }catch( \Exception $e ){
            return $e->getMessage();
        }
        return $result;
    }    

}

http://example.com/Home/dbinit
にアクセスすると、DBやテーブルがない場合は、

ブラウザの表示
データベース生成成功
テーブル生成成功
このDBのテーブル=users

となるはずです。
(1度目のアクセスで上記画面が出た後リロードした場合など)すでにDBとテーブルができている場合は次の表示になるはずです。

ブラウザの表示
Table 'users' already exists

再度試す場合は
http://example.com/Home/db_deleteにアクセスしてDBを削除した後、再度http://example.com/Home/dbinitにアクセスします。

テーブルにデータを出し入れするモデルも作ろう

次に、DBへのデータのInsert/Select/Update/Deleteの操作を極力簡略化した記述で書いてみます。

app/Models/User.php
<?php
namespace App\Models;

use CodeIgniter\Model;

class Users extends Model{
    protected $table = 'users';
    protected $primaryKey = 'id';
    protected $allowedFields = [ 'login_id' , 'login_pw' ];
}

たったこれだけです。

CIのモデルにはDBへの入出力をするメソッドがすでに用意されているので、簡単なDB操作ならば、ここで定義している操作するテーブル名の指定する $table PKEYのフィールド名を指定する $primaryKey 操作を許可するフィールド名を指定する $allowedFields といった親クラスに用意されているメンバ変数を上書きするだけで実現可能です。

これをコントローラから呼び出すには次のような書き方でOKです。

DBへのinsert例、app/Controllers/Home.php に追記
    public function add_test( $id , $pw ){
        try{
            $db = new \App\Models\Users();
            $db->insert( [ 'login_id' => $id , 'login_pw' => $pw ] );
        }catch( Exception $e ){
            die( $e->getMessage() );
        }
        return "Add OK";
    }

試す場合はhttp://example.com/Home/add_test/追加したいID/追加したいPWです。

以前の記事で述べたとおり、CIではメソッドの引数はURLのパスの第3階層以降と一致しますので、追加したいID$id追加したいPW$pwに渡されます。

なお、前回の記事で作ったusersテーブルではlogin_idはunique制約を付加しているので、同じURLに2度アクセスするとDBがエラーが出るはずですので、何度も試す場合は都度URLを変えてアクセスしてみてください。

このあたりのDB操作の詳細は次回の記事で、Viewの説明と共にまとめて記したいと思います。

現在の記事までのコントローラでは直接HTMLを作り、組み上がったHTMLの格納された変数や文字列をreturnでCIのエンジンに返すことで出力を行っていますが、例によって本来HTMLの組み上げはコントローラの仕事ではなくViewの仕事なので、次の記事ではViewでコンテンツを出力することを目指します。