CodeIgniter のモデルの利用¶
手作業でのモデル作成¶
モデルの作成のために特別なクラスを継承する必要はありません。データベース接続のインスタンスさえあれば準備はできています。
<?php namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class UserModel
{
protected $db;
public function __construct(ConnectionInterface &$db)
{
$this->db =& $db;
}
}
CodeIgniter のモデル¶
CodeIgniter はモデルクラスを提供しており、機能はわずかながら良いものです。次のものです:
- 自動データベース接続
- 基本的な CRUD のメソッド
- モデルでのバリデーション
- automatic pagination
- そのほか
このクラスはモデルづくりの基盤を提供します。アプリケーションのモデルレイヤ作成を手早く行えるようになります。
モデルの作成¶
CodeIgniter のモデルを利用するには、新しいモデルクラスでシンプルに CodeIgniter\Model
を継承します:
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
}
このからっぽのクラスはデータベース接続への便利なアクセスと、クエリビルダ、そのほか多くの便利メソッドを提供しています。
データベースへの接続¶
When the class is first instantiated, if no database connection instance is passed to the constructor,
it will automatically connect to the default database group, as set in the configuration. どのグループを使うかはモデルごとに基本設定を行えます。DBGroup プロパティをクラスに追加します。
これによってモデルの $this->db
で適切な接続を利用することを確かなものにしてくれます。
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $DBGroup = 'group_name';
}
"group_name" の箇所はデータベース設定ファイルのグループ定義の名前に置き換えてください。
モデルの設定¶
モデルクラスはわずかながら設定オプションを持ち、シームレスにクラスメソッドを調整できます。まず最初の2つはすべての CRUD メソッドで使用されるもので、どのテーブルを使用しどのレコードが要求されているかを発見するためのものです:
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['name', 'email'];
protected $useTimestamps = false;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
}
$table
このモデルで主に使用するデータベーステーブルを設定します。これはビルトインの CRUD メソッドでのみ利用されます。あなた独自のクエリはこのテーブルだけに制限されるものではありません。
$primaryKey
このテーブルのレコードを一意に特定するカラム名です。This
does not necessarily have to match the primary key that is specified in the database, but
is used with methods like find()
to know what column to match the specified value to.
注釈
All Models must have a primaryKey specified to allow all of the features to work as expected.
$returnType
モデルの CRUD メソッドはあなたの作業を一歩進め、結果オブジェクトではなく結果データを自動的に返します。この設定では返すデータの型を定義します。使える値は 'array'、'object'、または完全なクラス名称で、getCustomResultObject() メソッドで使われるものです。
$useSoftDeletes
もし true なら、すべての delete* メソッド呼び出しは実際に行を削除するのではなく、フラグをデータベースに設定するだけになります。This can preserve data when it might be referenced elsewhere, or can maintain a "recycle bin" of objects that can be restored, or even simply preserve it as part of a security trail. もし true なら、find* メソッドに先立って withDeleted() を呼ばない限り、find* メソッドは削除されていない行だけを返します。
This requires an INT or TINYINT field to be present in the table for storing state. The default field name is deleted
however this name can be configured to any name of your choice by using $deletedField property.
$allowedFields
この配列は save、insert、update メソッドで操作することのできるフィールド名です。これらに設定していないフィールドの操作は破棄されます。これはフォームからの入力値を全部モデルに投げてしまったときに発生する、潜在的な mass assignment 脆弱性からの防御に役立ちます[訳注: mass assignment という名の脆弱性です。定訳なし]。
$useTimestamps
この boolean 値は、insert または update で毎回自動的に現在日時を設定するかどうかを決定します。もし true なら、$dateFormat で指定したフォーマットの現在時刻を設定します。この設定には 'created_at' カラムと 'updated_at' カラムを適切な型でテーブルに持っておくことが必要です。
$createdField
Specifies which database field should use for keep data record create timestamp. Leave it empty to avoid update it (even useTimestamps is enabled)
$updatedField
Specifies which database field should use for keep data record update timestamp. Leave it empty to avoid update it (even useTimestamps is enabled)
$dateFormat
この値は $useTimestamps と一緒に動かすもので、データベースに入れる日付値の形を正しく認識させるものです。デフォルトでは DATETIME 値になりますが、有効なオプションは datetime、date、または int(PHPのtimestamp)です。
$validationRules
Contains either an array of validation rules as described in How to save your rules or a string containing the name of a validation group, as described in the same section. より詳しくは後述を参照ください。
$validationMessages
Contains an array of custom error messages that should be used during validation, as described in Setting Custom Error Messages. より詳しくは後述を参照ください。
$skipValidation
すべての inserts
と updates
でバリデーションをスキップするかどうか。デフォルトは false で、毎回バリデーションを実施することを意味します。この設定は主に skipValidation()
メソッドで使用されますが、 true
に変更するとこのモデルは決してバリデーションされなくなります。
$beforeInsert $afterInsert $beforeUpdate $afterUpdate afterFind afterDelete
These arrays allow you to specify callback methods that will be run on the data at the time specified in the property name.
データ操作¶
データの find¶
いくつかのメソッドはテーブルの基本的な CRUD 操作を提供します。find()、insert()、update()、delete() その他です。
find()
第1引数に渡された値が主キーと一致する単一行を返します:
$user = $userModel->find($user_id);
返される値は $returnType で指定した型になります。
主キーの値には1つだけではなく複数の値を配列で渡すことができ、複数の値が返ります:
$users = $userModel->find([1,2,3]);
If no parameters are passed in, will return all rows in that model's table, effectively acting like findAll(), though less explicit.
findColumn()
Returns null or an indexed array of column values:
$user = $userModel->findColumn($column_name);$column_name should be a name of single column else you will get the DataException.
findAll()
Returns all results:
$users = $userModel->findAll();
このメソッドに先立って呼び出された Query Builder コマンドを割り込ませる形でこのクエリを編集します:
$users = $userModel->where('active', 1)
->findAll();
limit と offset をそれぞれ第1引数、第2引数に渡せます:
$users = $userModel->findAll($limit, $offset);
first()
結果セットの1行目を返します。クエリビルダとの組み合わせで使用するのが良いです。
$user = $userModel->where('deleted', 0)
->first();
withDeleted()
もし $useSoftDeletes が true なら、find* メソッドは 'deleted = 1' の行を返しません。一時的にこれを変更したいなら、withDeleted() メソッドを find* メソッドに先立って使用することができます。
// 削除されていない行だけを取得します (deleted = 0)
$activeUsers = $userModel->findAll();
// すべての行を取得します
$allUsers = $userModel->withDeleted()
->findAll();
onlyDeleted()
withDeleted() が削除行と削除されていない行の両方を返すところを、このメソッドは次回の find* メソッドで論理削除行だけを返すようにします:
$deletedUsers = $userModel->onlyDeleted()
->findAll();
データの保存¶
insert()
このメソッドの唯一の引数として、連想配列を渡すとデータベースに新しい行を作成します。The array's keys must match the name of the columns in a $table, while the array's values are the values to save for that key:
$data = [
'username' => 'ダース',
'email' => 'd.vader@theempire.com'
];
$userModel->insert($data);
update()
データベースにすでにあるレコードを更新します。第1引数の $primaryKey は更新するレコードを指します。 第2引数として連想配列をこのメソッドに渡します。The array's keys must match the name of the columns in a $table, while the array's values are the values to save for that key:
$data = [
'username' => 'ダース',
'email' => 'd.vader@theempire.com'
];
$userModel->update($id, $data);
Multiple records may be updated with a single call by passing an array of primary keys as the first parameter:
$data = [
'active' => 1
];
$userModel->update([1, 2, 3], $data);
When you need a more flexible solution, you can leave the parameters empty and it functions like the Query Builder's update command, with the added benefit of validation, events, etc:
$userModel
->whereIn('id', [1,2,3])
->set(['active' => 1]
->update();
save()
This is a wrapper around the insert() and update() methods that handle inserting or updating the record automatically, based on whether it finds an array key matching the $primaryKey value:
// モデルのプロパティとして定義します
$primaryKey = 'id';
// insert() として動作します
$data = [
'username' => 'ダース',
'email' => 'd.vader@theempire.com'
];
$userModel->save($data);
// 主キー 'id' があるので更新として動作します。
$data = [
'id' => 3,
'username' => 'ダース',
'email' => 'd.vader@theempire.com'
];
$userModel->save($data);
また、save メソッドはカスタムクラス結果オブジェクトと一緒によりシンプルに動作します。ただのオブジェクトとしてではなく認識すると、public と protected の値をかき集めて配列に入れ、適切に insert または update メソッドに渡します。これはエンティティクラスとして非常にわかりやすい方法です。エンティティクラスはシンプルなクラスで、ユーザやブログ投稿、仕事などのようなオブジェクトの型として、単一のインスタンスを表現します。このクラスはオブジェクト自体にまつわるビジネスロジックを管理する責任を持ち、要素の形式を正しく保つなどをします。それらはデータベースに保存する方法についてどのような関心も持つべきではありません。最も単純な形式として、次のようになります:
namespace App\Entities;
class Job
{
protected $id;
protected $name;
protected $description;
public function __get($key)
{
if (property_exists($this, $key))
{
return $this->$key;
}
}
public function __set($key, $value)
{
if (property_exists($this, $key))
{
$this->$key = $value;
}
}
}
非常にシンプルなモデルとして次のように操作します:
use CodeIgniter\Model;
class JobModel extends Model
{
protected $table = 'jobs';
protected $returnType = '\App\Entities\Job';
protected $allowedFields = [
'name', 'description'
];
}
このモデルは jobs
テーブルのデータを操作し、App\Entities\Job
のインスタンスとして結果を返します。
データベースのレコードとして永続化させる必要があるとき、独自のメソッドを書くか、もしくはモデルの save()
メソッドを使いクラスを解析させ、public と private のプロパティを集め、データベースにそれを保存します:
// Job のインスタンスを取得します
$job = $model->find(15);
// なんらかの変更を加えます
$job->name = "Foobar";
// 変更を保存します
$model->save($job);
注釈
If you find yourself working with Entities a lot, CodeIgniter provides a built-in Entity class that provides several handy features that make developing Entities simpler.
データの削除¶
delete()
主キーの値を第1引数として取り、モデルのテーブルの一致するレコードを削除します:
$userModel->delete(12);
モデルの $useSoftDeletes が true の場合は、削除対象の行は 'deleted = 1' に更新されます。第2引数に true を渡せば物理削除を強制できます。
An array of primary keys can be passed in as the first parameter to delete multiple records at once:
$userModel->delete([1,2,3]);
If no parameters are passed in, will act like the Query Builder's delete method, requiring a where call previously:
$userModel->where('id', 12)->delete();
purgeDeleted()
'deleted = 1' で論理削除された行を物理削除で一掃します。:
$userModel->purgeDeleted();
データのバリデーション¶
多くの人にとって、モデルでのバリデーションは重複コードをなくし、データを単一基準で維持するのに好ましい方法です。モデルクラスは insert()
、update()
、save()
メソッドでデータベースに保存するのに先立ちデータを自動的にバリデーションする方法を提供します。
最初のステップは、$validationRules
プロパティに適用ルールを埋めることです。もし独自のエラーメッセージを使いたいなら、$validationMessages
配列に入れてください:
class UserModel extends Model
{
protected $validationRules = [
'username' => 'required|alpha_numeric_space|min_length[3]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[8]',
'pass_confirm' => 'required_with[password]|matches[password]'
];
protected $validationMessages = [
'email' => [
'is_unique' => 'Sorry. そのメールアドレスはすでに使用されています。別のものにしてください。'
]
];
}
The other way to set the validation message to fields by functions,
-
setValidationMessage
($field, $fieldMessages)¶ :param string $field :param array $fieldMessages
This function will set the field wise error messages.
使用例:
$fieldName = 'name'; $fieldValidationMessage = array( 'required' => 'Your name is required here', ); $model->setValidationMessage($fieldName, $fieldValidationMessage);
-
setValidationMessages
($fieldMessages)¶ :param array $fieldMessages
This function will set the field messages.
使用例:
$fieldValidationMessage = array( 'name' => array( 'required' => 'Your baby name is missing.', 'min_length' => 'Too short, man!', ), ); $model->setValidationMessages($fieldValidationMessage);
さて、これで insert()
、update()
、save()
メソッドはどこで呼ばれようともバリデーションされます。バリデーションに失敗したら、モデルは boolean で false を返します。You can use the errors()
method to retrieve the validation errors:
if ($model->save($data) === false)
{
return view('updateUser', ['errors' => $model->errors()];
}
このメソッドはフィールド名とそれに紐づくエラーの配列を返します。フォームの上部にまとめて表示するか、それぞれ個別に表示することができます:
<?php if (!empty($errors)) : ?>
<div class="alert alert-danger">
<?php foreach ($errors as $field => $error) : ?>
<p><?= $error ?></p>
<?php endforeach ?>
</div>
<?php endif ?>
バリデーション設定ファイルでルールとエラーメッセージを整理するほうがよいなら、$validationRules
には作成したバリデーションルールグループ名を単に設定してください:
class UserModel extends Model
{
protected $validationRules = 'users';
}
Validation Placeholders¶
The model provides a simple method to replace parts of your rules based on data that's being passed into it. This
sounds fairly obscure but can be especially handy with the is_unique
validation rule. Placeholders are simply
the name of the field (or array key) that was passed in as $data surrounded by curly brackets. It will be
replaced by the value of the matched incoming field. An example should clarify this:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,{id}]'
];
In this set of rules, it states that the email address should be unique in the database, except for the row that has an id matching the placeholder's value. Assuming that the form POST data had the following:
$_POST = [
'id' => 4,
'email' => 'foo@example.com'
]
then the {id}
placeholder would be replaced with the number 4, giving this revised rule:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,4]'
];
So it will ignore the row in the database that has id=4
when it verifies the email is unique.
This can also be used to create more dynamic rules at runtime, as long as you take care that any dynamic keys passed in don't conflict with your form data.
フィールドの保護¶
mass assignment 攻撃から保護する助けとして、モデルクラスは $allowedFields
プロパティに insert または update するフィールド名をすべてリストアップすることを 要求 します。これらからはみ出るデータは、データベースに入れる前に削除されます。タイムスタンプや主キーが変わらないことを確信できる、すばらしいことです。
protected $allowedFields = ['name', 'email', 'address'];
ときには、それらの要素を変更する必要がでてくるでしょう。テストやマイグレーション、値の埋め込みの際にはよく出てきます。その際には、保護を on/off できます:
$model->protect(false)
->insert($data)
->protect(true);
クエリビルダの操作¶
必要なときにはいつでも、モデルのデータベースのクエリビルダの共有インスタンスにアクセスすることができます:
$builder = $userModel->builder();
このビルダはモデルの $table でセットアップ済みです。
また、クエリビルダメソッドおよびモデルの CRUD メソッドを同じメソッドチェーンにつなげることができ、エレガントに使えます:
$users = $userModel->where('status', 'active')
->orderBy('last_login', 'asc')
->findAll();
注釈
モデルのデータベース接続もシームレスに使用できます:
$user_name = $userModel->escape($name);
実行時の返値型の変更¶
find*() メソッドの返値型として $returnType を指定することができます。しかしながら、異なる型で取得したいときもあるでしょう。モデルが提供するメソッドでは、まさにそのようにすることができます。
注釈
これらのメソッドは次回の find*() メソッド呼び出しにのみ影響します。その後、デフォルトの値にリセットされます。
asArray()
次回の find*() メソッド呼び出しの返値を連想配列にします:
$users = $userModel->asArray()->where('status', 'active')->findAll();
asObject()
次回の find*() メソッド呼び出しの返値を標準オブジェクトまたは独自クラスのインスタンスにします:
// Return as standard objects
$users = $userModel->asObject()->where('status', 'active')->findAll();
// Return as custom class instances
$users = $userModel->asObject('User')->where('status', 'active')->findAll();
Model Events¶
There are several points within the model's execution that you can specify multiple callback methods to run. These methods can be used to normalize data, hash passwords, save related entities, and much more. The following points in the model's execution can be affected, each through a class property: $beforeInsert, $afterInsert, $beforeUpdate, afterUpdate, afterFind, and afterDelete.
Defining Callbacks¶
You specify the callbacks by first creating a new class method in your model to use. This class will always receive a $data array as its only parameter. The exact contents of the $data array will vary between events, but will always contain a key named data that contains the primary data passed to the original method. In the case of the insert* or update* methods, that will be the key/value pairs that are being inserted into the database. The main array will also contain the other values passed to the method, and be detailed later. The callback method must return the original $data array so other callbacks have the full information.
protected function hashPassword(array $data)
{
if (! isset($data['data']['password']) return $data;
$data['data']['password_hash'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
unset($data['data']['password'];
return $data;
}
Specifying Callbacks To Run¶
You specify when to run the callbacks by adding the method name to the appropriate class property (beforeInsert, afterUpdate, etc). Multiple callbacks can be added to a single event and they will be processed one after the other. You can use the same callback in multiple events:
protected $beforeInsert = ['hashPassword'];
protected $beforeUpdate = ['hashPassword'];
Event Parameters¶
Since the exact data passed to each callback varies a bit, here are the details on what is in the $data parameter passed to each event:
Event | $data contents |
---|---|
beforeInsert | data = the key/value pairs that are being inserted. If an object or Entity class is passed to the insert method, it is first converted to an array. |
afterInsert | data = the original key/value pairs being inserted. result = the results of the insert() method used through the Query Builder. |
beforeUpdate | id = the primary key of the row being updated. data = the key/value pairs that are being inserted. If an object or Entity class is passed to the insert method, it is first converted to an array. |
afterUpdate | id = the primary key of the row being updated. data = the original key/value pairs being updated. result = the results of the update() method used through the Query Builder. |
afterFind | Varies by find* method. See the following: |
|
id = the primary key of the row being searched for. data = The resulting row of data, or null if no result found. |
|
data = the resulting rows of data, or null if no result found. limit = the number of rows to find. offset = the number of rows to skip during the search. |
|
data = the resulting row found during the search, or null if none found. |
beforeDelete | Varies by delete* method. See the following: |
|
id = primary key of row being deleted. purge = boolean whether soft-delete rows should be hard deleted. |
afterDelete | Varies by delete* method. See the following: |
|
id = primary key of row being deleted. purge = boolean whether soft-delete rows should be hard deleted. result = the result of the delete() call on the Query Builder. data = unused. |