CodeIgniter4のModelの基礎


前回の記事、[CodeIgniter4でMySQLのDB構築](https://qiita.com/kohenji01/items/c87727e553c162b72548)の続きです。

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

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

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

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

```php: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`はインポートする名前空間を指定します。
名前空間については[この記事](https://qiita.com/7968/items/1e5c61128fa495358c1f)が大変わかりやすく書いてありおすすめです。

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

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

###次はControllerのファイル

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

```php: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`も同様です。

```php: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やテーブルがない場合は、

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

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

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

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

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

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

```php: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です。

```php: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`です。

[以前の記事](https://qiita.com/kohenji01/items/ced458da42ce70e067f8#url%E3%81%AF%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B)で述べたとおり、CIではメソッドの引数はURLのパスの第3階層以降と一致しますので、`追加したいID`は`$id`に`追加したいPW`は`$pw`に渡されます。

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

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

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