merge from yiisoft/yii2

This commit is contained in:
cuileon
2018-11-14 11:57:32 +08:00
parent 47df8a8cc0
commit 489dd119a5
426 changed files with 18465 additions and 4215 deletions

View File

@ -7,13 +7,14 @@
モデル・クラスは、[[yii\base\Model]] またはその子クラスを拡張することによって作成することが出来ます。
基底クラス [[yii\base\Model]] は、次のような数多くの有用な機能をサポートしています。
* [属性](#attributes): ビジネス・データを表現します。通常のオブジェクト・プロパティや配列要素のようにしてアクセスすることが出来ます。
* [属性](#attributes): ビジネス・データを表現します。通常のオブジェクト・プロパティや配列要素のようにして
アクセスすることが出来ます。
* [属性のラベル](#attribute-labels): 属性の表示ラベルを指定します。
* [一括代入](#massive-assignment): 一回のステップで複数の属性にデータを投入することをサポートしています。
* [検証規則](#validation-rules): 宣言された検証規則に基いて入力されたデータの有効性を保証します。
* [データのエクスポート](#data-exporting): カスタマイズ可能な形式でモデル・データを配列にエクスポートすることが出来ます。
`Model` クラスは、[アクティブレコード](db-active-record.md) のような、更に高度なモデルの基底クラスでもあります。
`Model` クラスは、[アクティブレコード](db-active-record.md) のような、更に高度なモデルの基底クラスでもあります。
それらの高度なモデルについての詳細は、関連するドキュメントを参照してください。
> Info: あなたのモデル・クラスの基底クラスとして [[yii\base\Model]] を使うことが要求されている訳ではありません。
@ -22,8 +23,7 @@
## 属性 <span id="attributes"></span>
モデルはビジネスデータを *属性* の形式で表現します。
全ての属性はそれぞれパブリックにアクセス可能なモデルのプロパティと同様なものです。
モデルはビジネスデータを *属性* の形式で表現します。全ての属性はそれぞれパブリックにアクセス可能なモデルのプロパティと同様なものです。
[[yii\base\Model::attributes()]] メソッドが、モデルがどのような属性を持つかを指定します。
属性に対しては、通常のオブジェクト・プロパティにアクセスするのと同じようにして、アクセスすることが出来ます。
@ -37,7 +37,8 @@ echo $model->name;
```
また、配列の要素にアクセスするようして、属性にアクセスすることも出来ます。
これは、[[yii\base\Model]] が [ArrayAccess インタフェイス](http://php.net/manual/ja/class.arrayaccess.php) と [Traversable インタフェイス](http://jp2.php.net/manual/ja/class.traversable.php) をサポートしている恩恵です。
これは、[[yii\base\Model]] が [ArrayAccess インタフェイス](http://php.net/manual/ja/class.arrayaccess.php) と
[Traversable インタフェイス](http://jp2.php.net/manual/ja/class.traversable.php) をサポートしている恩恵です。
```php
$model = new \app\models\ContactForm;
@ -77,13 +78,15 @@ class ContactForm extends Model
[[yii\base\Model::attributes()]] をオーバーライドして、属性を異なる方法で定義することが出来ます。
このメソッドはモデルが持つ属性の名前を返さなくてはなりません。
例えば、[[yii\db\ActiveRecord]] は、関連付けられたデータベース・テーブルのコラム名を属性の名前として返すことによって、属性を定義しています。
ただし、これと同時に、定義された属性に対して通常のオブジェクト・プロパティと同じようにアクセスすることが出来るように、`__get()``__set()` などのマジック・メソッドをオーバーライドする必要があるかもしれないことに注意してください。
ただし、これと同時に、定義された属性に対して通常のオブジェクト・プロパティと同じようにアクセスすることが出来るように、
`__get()``__set()` などのマジック・メソッドをオーバーライドする必要があるかもしれないことに注意してください。
### 属性のラベル <span id="attribute-labels"></span>
属性の値を表示したり、入力してもらったりするときに、属性と関連付けられたラベルを表示する必要があることがよくあります。
例えば、`firstName` という名前の属性を考えたとき、入力フォームやエラー・メッセージのような箇所でエンド・ユーザに表示するときは、もっとユーザ・フレンドリーな `First Name` というラベルを表示したいと思うでしょう。
例えば、`firstName` という名前の属性を考えたとき、入力フォームやエラー・メッセージのような箇所でエンド・ユーザに表示するときは、
もっとユーザ・フレンドリーな `First Name` というラベルを表示したいと思うでしょう。
[[yii\base\Model::getAttributeLabel()]] を呼ぶことによって属性のラベルを得ることが出来ます。例えば、
@ -99,7 +102,8 @@ echo $model->getAttributeLabel('name');
このメソッドは、キャメルケースの変数名を複数の単語に分割し、各単語の最初の文字を大文字にします。
例えば、`username``Username` となり、`firstName``First Name` となります。
自動的に生成されるラベルを使用したくない場合は、[[yii\base\Model::attributeLabels()]] をオーバーライドして、属性のラベルを明示的に宣言することが出来ます。例えば、
自動的に生成されるラベルを使用したくない場合は、[[yii\base\Model::attributeLabels()]] をオーバーライドして、
属性のラベルを明示的に宣言することが出来ます。例えば、
```php
namespace app\models;
@ -168,7 +172,8 @@ $model = new User(['scenario' => User::SCENARIO_LOGIN]);
```
デフォルトでは、モデルによってサポートされるシナリオは、モデルで宣言されている [検証規則](#validation-rules) によって決定されます。
しかし、次のように、[[yii\base\Model::scenarios()]] メソッドをオーバーライドして、この振る舞いをカスタマイズすることが出来ます。
しかし、次のように、[[yii\base\Model::scenarios()]] メソッドをオーバーライドして、
この振る舞いをカスタマイズすることが出来ます。
```php
namespace app\models;
@ -191,14 +196,16 @@ class User extends ActiveRecord
```
> Info: 上記の例と後続の例では、モデル・クラスは [[yii\db\ActiveRecord]] を拡張するものとなっています。
というのは、複数のシナリオを使用することは、通常は、[アクティブレコード](db-active-record.md) クラスで発生するからです。
というのは、複数のシナリオを使用することは、通常は、[アクティブレコード](db-active-record.md) クラスで発生するからです。
`seanarios()` メソッドは、キーがシナリオの名前であり、値が対応する *アクティブな属性* である配列を返します。
アクティブな属性とは、[一括代入](#massive-assignment) することが出来て、[検証](#validation-rules) の対象になる属性です。
上記の例では、`login` シナリオにおいては `username``password` の属性がアクティブであり、一方、`register` シナリオにおいては、`username``password` に加えて `email` もアクティブです。
上記の例では、`login` シナリオにおいては `username``password` の属性がアクティブであり、
一方、`register` シナリオにおいては、`username``password` に加えて `email` もアクティブです。
`scenarios()` のデフォルトの実装は、検証規則の宣言メソッドである [[yii\base\Model::rules()]] に現れる全てのシナリオを返すものです。
`scenarios()` をオーバーライドするときに、デフォルトのシナリオに加えて新しいシナリオを導入したい場合は、次のようなコードを書きます。
`scenarios()` をオーバーライドするときに、デフォルトのシナリオに加えて新しいシナリオを導入したい場合は、
次のようなコードを書きます。
```php
namespace app\models;
@ -207,6 +214,9 @@ use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
$scenarios = parent::scenarios();
@ -218,20 +228,22 @@ class User extends ActiveRecord
```
シナリオの機能は、主として、[検証](#validation-rules) と [属性の一括代入](#massive-assignment) によって使用されます。
しかし、他の目的に使うことも可能です。例えば、現在のシナリオに基づいて異なる [属性のラベル](#attribute-labels) を宣言することも出来ます。
しかし、他の目的に使うことも可能です。例えば、現在のシナリオに基づいて異なる [属性のラベル](#attribute-labels)
を宣言することも出来ます。
## 検証規則 <span id="validation-rules"></span>
モデルのデータをエンド・ユーザから受け取ったときは、データを検証して、それが一定の規則 (*検証規則*、あるいは、いわゆる *ビジネス・ルール*) を満たしていることを確認しなければなりません。
モデルのデータをエンド・ユーザから受け取ったときは、データを検証して、
それが一定の規則 (*検証規則*、あるいは、いわゆる *ビジネス・ルール*) を満たしていることを確認しなければなりません。
`ContactForm` モデルを例に挙げるなら、全ての属性が空ではなく、`email` 属性が有効なメール・アドレスを含んでいることを確認したいでしょう。
いずれかの属性の値が対応するビジネス・ルールを満たしていないときは、ユーザがエラーを訂正するのを助ける適切なエラー・メッセージが表示されなければなりません。
いずれかの属性の値が対応するビジネス・ルールを満たしていないときは、
ユーザがエラーを訂正するのを助ける適切なエラー・メッセージが表示されなければなりません。
受信したデータを検証するために、[[yii\base\Model::validate()]] を呼ぶことが出来ます。
このメソッドは、[[yii\base\Model::rules()]] で宣言された検証規則を使って、該当するすべての属性を検証します。
エラーが見つからなければ、メソッドは `true` を返します。
そうでなければ、[[yii\base\Model::errors]] にエラーを保存して、`false` を返します。
例えば、
そうでなければ、[[yii\base\Model::errors]] にエラーを保存して、`false` を返します。例えば、
```php
$model = new \app\models\ContactForm;
@ -248,7 +260,8 @@ if ($model->validate()) {
```
モデルに関連付けられた検証規則を宣言するためには、[[yii\base\Model::rules()]] メソッドをオーバーライドして、モデルの属性が満たすべき規則を返すようにします。
モデルに関連付けられた検証規則を宣言するためには、[[yii\base\Model::rules()]] メソッドをオーバーライドして、
モデルの属性が満たすべき規則を返すようにします。
次の例は、`ContactForm` モデルのために宣言された検証規則を示します。
```php
@ -287,14 +300,16 @@ public function rules()
`on` プロパティを指定しない場合は、その規則は全てのシナリオに適用されることになります。
現在の [[yii\base\Model::scenario|シナリオ]] に適用可能な規則は *アクティブな規則* と呼ばれます。
属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、その属性が `rules()` の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。
属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、
その属性が `rules()` の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。
## 一括代入 <span id="massive-assignment"></span>
一括代入は、一行のコードを書くだけで、ユーザの入力した複数のデータをモデルに投入できる便利な方法です。
一括代入は、入力されたデータを [[yii\base\Model::$attributes]] プロパティに直接に代入することによって、モデルの属性にデータを投入します。
次の二つのコード断片は等価であり、どちらもエンド・ユーザから送信されたフォームのデータを `ContactForm` モデルの属性に割り当てようとするものです。
次の二つのコード断片は等価であり、どちらもエンド・ユーザから送信されたフォームのデータを
`ContactForm` モデルの属性に割り当てようとするものです。
明らかに、一括代入を使う前者の方が、後者よりも明快で間違いも起こりにくいでしょう。
```php
@ -314,8 +329,10 @@ $model->body = isset($data['body']) ? $data['body'] : null;
### 安全な属性 <span id="safe-attributes"></span>
一括代入は、いわゆる *安全な属性*、すなわち、[[yii\base\Model::scenarios()]] においてモデルの現在の [[yii\base\Model::scenario|シナリオ]] のためにリストされている属性に対してのみ適用されます。
例えば、`User` モデルが次のようなシナリオ宣言を持っている場合において、現在のシナリオが `login` であるときは、`username``password` のみが一括代入が可能です。
一括代入は、いわゆる *安全な属性*、すなわち、[[yii\base\Model::scenarios()]] においてモデルの現在の
[[yii\base\Model::scenario|シナリオ]] のためにリストされている属性に対してのみ適用されます。
例えば、`User` モデルが次のようなシナリオ宣言を持っている場合において、現在のシナリオが `login` であるときは、
`username``password` のみが一括代入が可能です。
その他の属性はいっさい触れられません。
```php
@ -328,13 +345,17 @@ public function scenarios()
}
```
> Info: 一括代入が安全な属性に対してのみ適用されるのは、エンド・ユーザの入力データがどの属性を修正することが出来るか、ということを制御する必要があるからです。
例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、この属性が修正できるのは、管理者がバックエンドのインタフェイスを通じてする時だけに制限したいでしょう
> Info: 一括代入が安全な属性に対してのみ適用されるのは、エンド・ユーザの入力データがどの属性を修正することが出来るか、
ということを制御する必要があるからです
例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、
この属性を修正できるのは、管理者がバックエンドのインタフェイスを通じてする時だけに制限したいでしょう。
[[yii\base\Model::scenarios()]] のデフォルトの実装は [[yii\base\Model::rules()]] に現われる全てのシナリオと属性を返すものです。
従って、このメソッドをオーバーライドしない場合は、アクティブな検証規則のどれかに出現する限り、その属性は安全である、ということになります。
従って、このメソッドをオーバーライドしない場合は、アクティブな検証規則のどれかに出現する限り、
その属性は安全である、ということになります。
このため、実際に検証することなく属性を安全であると宣言できるように、`safe` というエイリアスを与えられた特別なバリデータが提供されています。
このため、実際に検証することなく属性を安全であると宣言できるように、
`safe` というエイリアスを与えられた特別なバリデータが提供されています。
例えば、次の規則は `title``description` の両方が安全な属性であると宣言しています。
```php
@ -387,15 +408,14 @@ public function rules()
## データのエクスポート <span id="data-exporting"></span>
モデルを他の形式にエクスポートする必要が生じることはよくあります。
例えば、モデルのコレクションを JSON や Excel 形式に変換したい場合があるでしょう。
モデルを他の形式にエクスポートする必要が生じることはよくあります。例えば、モデルのコレクションを JSON や Excel 形式に変換したい場合があるでしょう。
このエクスポートのプロセスは二つの独立したステップに分割することが出来ます。
- モデルが配列に変換され、
- 配列が目的の形式に変換される。
あなたは最初のステップだけに注力することが出来ます。
と言うのは、第二のステップは汎用的なデータフォーマッタ、例えば [[yii\web\JsonResponseFormatter]] によって達成できるからです。
と言うのは、第二のステップは汎用的なデータフォーマッタ、例えば [[yii\web\JsonResponseFormatter]] によって達成できるからです。
モデルを配列に変換する最も簡単な方法は、[[yii\base\Model::$attributes]] プロパティを使うことです。
例えば、
@ -405,24 +425,28 @@ $post = \app\models\Post::findOne(100);
$array = $post->attributes;
```
デフォルトでは、[[yii\base\Model::$attributes]] プロパティは [[yii\base\Model::attributes()]] で宣言されている *全て* の属性の値を返します。
デフォルトでは、[[yii\base\Model::$attributes]] プロパティは [[yii\base\Model::attributes()]]
で宣言されている *全て* の属性の値を返します。
モデルを配列に変換するためのもっと柔軟で強力な方法は、[[yii\base\Model::toArray()]] メソッドを使うことです。
このメソッドのデフォルトの動作は [[yii\base\Model::$attributes]] のそれと同じものです。
しかしながら、このメソッドを使うと、どのデータ項目 (*フィールド* と呼ばれます) を結果の配列に入れるか、そして、その項目にどのような書式を適用するかを選ぶことが出来ます。
実際、[レスポンス形式の設定](rest-response-formatting.md) で説明されているように、RESTful ウェブサービスの開発においては、これがモデルをエクスポートするデフォルトの方法となっています。
実際、[レスポンス形式の設定](rest-response-formatting.md) で説明されているように、RESTful ウェブサービスの開発においては、
これがモデルをエクスポートするデフォルトの方法となっています。
### フィールド <span id="fields"></span>
フィールドとは、単に、モデルの [[yii\base\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる、名前付きの要素のことです。
フィールドとは、単に、モデルの [[yii\base\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる、
名前付きの要素のことです。
デフォルトでは、フィールドの名前は属性の名前と等しいものになります。
しかし、このデフォルトの動作は、[[yii\base\Model::fields()|fields()]] および/または [[yii\base\Model::extraFields()|extraFields()]] メソッドをオーバーライドして、変更することが出来ます。
どちらのメソッドも、フィールド定義のリストを返します。
どちらのメソッドも、フィールド定義のリストを返すものです。
`fields()` によって定義されるフィールドは、デフォルト・フィールドです。すなわち、`toArray()` はデフォルトでこれらのフィールドを返す、ということを意味します。
`extraFields()` メソッドは、`$expand` パラメータによって指定する限りにおいて `toArray()` によって返される、追加のフィールドを定義するものです。
例として、次のコードは、`fields()` で定義された全てのフィールドと、(`extraFields()` で定義されていれば) `prettyName``fullAddress` フィールドを返すものです。
例として、次のコードは、`fields()` で定義された全てのフィールドと、(`extraFields()` で定義されていれば)
`prettyName``fullAddress` フィールドを返すものです。
```php
$array = $model->toArray([], ['prettyName', 'fullAddress']);
@ -466,7 +490,8 @@ public function fields()
}
```
> Warning: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、公開すべきでない情報が含まれていないことを確認しなければなりません。
> Warning: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、
> 公開すべきでない情報が含まれていないことを確認しなければなりません。
> そういう情報がある場合は、`fields()` をオーバーライドして、除外しなければなりません。
> 上記の例では、`auth_key`、`password_hash` および `password_reset_token` を除外しています。
@ -488,17 +513,21 @@ public function fields()
* あまりに多くの [シナリオ](#scenarios) を一つのモデルで持つことは避けましょう。
大規模で複雑なシステムを開発するときには、たいてい、上記の最後にあげた推奨事項を考慮するのが良いでしょう。
そういうシステムでは、モデルは数多くの場所で使用され、それに従って、数多くの規則セットやビジネス・ロジックを含むため、非常に太ったものになり得ます。
そういうシステムでは、モデルは数多くの場所で使用され、それに従って、数多くの規則セットやビジネス・ロジックを含むため、
非常に太ったものになり得ます。
コードの一ヶ所に触れるだけで数ヶ所の違った場所に影響が及ぶため、ついには、モデルのコードの保守が悪夢になってしまうこともよくあります。
モデルのコードの保守性を高めるためには、以下の戦略をとることが出来ます。
* 異なる [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) によって共有される一連の基底モデル・クラスを定義します。
* 異なる [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md)
によって共有される一連の基底モデル・クラスを定義します。
これらのモデル・クラスは、すべてで共通に使用される最小限の規則セットとロジックのみを含むべきです。
* モデルを使用するそれぞれの [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) において、対応する基底モデル・クラスから拡張した具体的なモデル・クラスを定義します。
* モデルを使用するそれぞれの [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) において、
対応する基底モデル・クラスから拡張した具体的なモデル・クラスを定義します。
この具体的なモデル・クラスが、そのアプリケーションやモジュールに固有の規則やロジックを含むべきです。
例えば、[アドバンスト・プロジェクト・テンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) の中で、基底モデル・クラス `common\models\Post` を定義することが出来ます。
次に、フロントエンド・アプリケーションにおいては、`common\models\Post` から拡張した具体的なモデル・クラス `frontend\models\Post` を定義して使います。
次に、フロントエンド・アプリケーションにおいては、`common\models\Post` から拡張した具体的なモデル・クラス
`frontend\models\Post` を定義して使います。
また、バックエンド・アプリケーションにおいても、同様に、`backend\models\Post` を定義します。
この戦略を取ると、`frontend\models\Post` の中のコードはフロントエンド・アプリケーション固有のものであると保証することが出来ます。
そして、フロントエンドのコードにどのような変更を加えても、バックエンド・アプリケーションを壊すかもしれないと心配する必要がなくなります。