Commit e63e01f1 by Alexander Makarov

Merge pull request #7105 from softark/docs-guide-ja-input-updates

Docs guide ja input updates [ci skip]
parents 6e50783e 6cf32856
......@@ -90,7 +90,8 @@ All Rights Reserved.
* [フォームを作成する](input-forms.md)
* [入力を検証する](input-validation.md)
* [ファイルをアップロードする](input-file-upload.md)
* **未定** [複数モデルのデータ取得](input-multiple-models.md)
* [表形式インプットのデータ収集](input-tabular-input.md)
* [複数モデルのデータ取得](input-multiple-models.md)
データの表示
......
フォームを扱う
==============
フォームを作成する
==================
> Note|注意: この節はまだ執筆中です。
Yii においてフォームを使用する主たる方法は [[yii\widgets\ActiveForm]] によるものです。
Yii においてフォームを使用するための主たる方法は [[yii\widgets\ActiveForm]] によるものです。
フォームがモデルに基づくものである場合はこの方法を優先すべきです。
これに加えて、[[yii\helpers\Html]] にはいくつかの有用なメソッドがあり、通常は、あらゆるフォームにボタンやヘルプテキストを追加するのに使うことが出来ます。
これに加えて、[[yii\helpers\Html]] にはいくつかの有用なメソッドがあり、どんなフォームでもボタンやヘルプテキストを追加するのには、通常はそれらが使われます。
フォームは、クライアント側で表示されるものですが、たいていの場合、サーバ側でフォームの入力を検証するために使われる、対応する [モデル](structure-models.md) を持ちます
(入力の検証の詳細については、[入力を検証する](input-validation.md) の節を参照してください)。
モデルに基づくフォームを作成する場合、最初のステップは、モデルそのものを定義することです。
モデルは、アクティブレコードクラス、あるいは、もっと汎用的な Model クラスから派生させることが出来ます。
このログインフォームの例では、汎用的なモデルを使用します。
モデルは、データベースの何らかのデータを表現するために [アクティブレコード](db-active-record.md) クラスから派生させるか、あるいは、任意の入力、例えばログインフォームの入力を捕捉するために ([[yii\base\Model]] から派生させた) 汎用的な Model クラスとすることが出来ます。
以下の例においては、ログインフォームのために汎用的なモデルを使う方法を示します。
```php
use yii\base\Model;
<?php
class LoginForm extends Model
class LoginForm extends \yii\base\Model
{
public $username;
public $password;
/**
* @return array 検証規則
*/
public function rules()
{
return [
// username と password はともに必須
[['username', 'password'], 'required'],
// password は validatePassword() によって検証される
['password', 'validatePassword'],
// 検証規則をここで定義
];
}
/**
* パスワードを検証する
* このメソッドがパスワードのインライン検証に使用される
*/
public function validatePassword()
{
$user = User::findByUsername($this->username);
if (!$user || !$user->validatePassword($this->password)) {
$this->addError('password', 'Incorrect username or password.');
}
}
/**
* 提供された username と password でユーザをログインさせる。
* @return boolean ユーザのログインが成功したかどうか
*/
public function login()
{
if ($this->validate()) {
$user = User::findByUsername($this->username);
return true;
} else {
return false;
}
}
}
```
コントローラはこのモデルのインスタンスをビューに渡し、ビューでは [[yii\widgets\ActiveForm|ActiveForm]] ウィジェットが使われます。
コントローラにおいて、このモデルのインスタンスをビューに渡し、ビューでは [[yii\widgets\ActiveForm|ActiveForm]] ウィジェットがフォームを表示するのに使われます。
```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
<?php $form = ActiveForm::begin([
$form = ActiveForm::begin([
'id' => 'login-form',
'options' => ['class' => 'form-horizontal'],
]) ?>
......@@ -83,32 +52,31 @@ use yii\widgets\ActiveForm;
上記のコードでは、[[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] がフォームのインスタンスを作成するだけでなく、フォームの開始をマークしています。
[[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] と [[yii\widgets\ActiveForm::end()|ActiveForm::end()]] の間に置かれた全てのコンテントが `<form>` タグによって囲まれます。
その他のウィジェットと同じように、ウィジェットをどのように構成すべきかに関するオプションを指定するために、`begin` メソッドに配列を渡すことが出来ます。
どのウィジェットでも同じですが、ウィジェットをどのように構成すべきかに関するオプションを指定するために、`begin` メソッドに配列を渡すことが出来ます。
この例では、追加の CSS クラスと要素を特定するための ID が渡されて、開始 `<form>` タグに適用されています。
利用できるオプションはすべて [[yii\widgets\ActiveForm]] の API ドキュメントに記されていますので参照してください。
フォームの中で、フォームの要素を作成するために、ActiveForm ウィジェットの [[yii\widgets\ActiveForm::field()|ActiveForm::field()]] メソッドが呼ばれています。このメソッドは、要素のラベルと、適用できる JavaScript の検証メソッドがあれば、それらも追加します。
フォームの中では、フォームの要素を作成し、それと一緒に要素のラベルと、そして、適用できる JavaScript の検証メソッドがあれば、それも追加するために、ActiveForm ウィジェットの [[yii\widgets\ActiveForm::field()|ActiveForm::field()]] メソッドが呼ばれています。
このメソッドは、[[yii\widgets\ActiveField]] のインスタンスを返します。
このメソッドの呼び出し結果を直接にエコーすると、結果は通常の (text の) インプットになります。
出力結果をカスタマイズするためには、このメソッドの呼び出しに追加のメソッドをチェーンします。
出力結果をカスタマイズするためには、このメソッドの呼び出しに追加の [[yii\widgets\ActiveField|ActiveField]] のメソッドをチェーンします。
```php
// パスワードのインプット
<?= $form->field($model, 'password')->passwordInput() ?>
// または
// ヒントとカスタマイズしたラベルを追加
<?= $form->field($model, 'username')->textInput()->hint('お名前を入力してください')->label('お名前') ?>
```
これで、フォームのフィールドによって定義されたテンプレートに従って、`<label>``<input>` など、全てのタグが生成されます。
これらのタグを自分で追加するためには、`Html` ヘルパクラスを使うことが出来ます。
HTML5 のフィールドを使いたい場合は、次のように、インプットのタイプを直接に指定することが出来ます。
```php
// HTML5 のメールインプット要素を作成
<?= $form->field($model, 'email')->input('email') ?>
```
モデルの属性を指定するためには、もっと洗練された方法を使うことも出来ます。
例えば、複数のファイルをアップロードしたり、複数の項目を選択したりする場合に、属性の名前に `[]` を付けて、属性が配列の値を取り得ることを指定することが出来ます。
これで、フォームのフィールドによって定義された [[yii\widgets\ActiveField::$template|テンプレート]] に従って、`<label>``<input>` など、全てのタグが生成されます。
インプットフィールドの名前は、モデルの [[yii\base\Model::formName()|フォーム名]] と属性から自動的に決定されます。
例えば、上記の例における `username` 属性のインプットフィールドの名前は `LoginForm[username]` となります。
この命名規則の結果として、ログインフォームの全ての属性が配列として、サーバ側においては `$_POST['LoginForm']` に格納されて利用できることになります。
モデルの属性を指定するために、もっと洗練された方法を使うことも出来ます。
例えば、複数のファイルをアップロードしたり、複数の項目を選択したりする場合に、属性の名前に `[]` を付けて、属性が配列の値を取ることが出来ることを指定することが出来ます。
```php
// 複数のファイルのアップロードを許可する
......@@ -118,74 +86,24 @@ echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);
echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);
```
> **tip**|**ヒント**: 必須フィールドをアスタリスク付きのスタイルにするために、次の CSS を使うことが出来ます。
>
```css
div.required label:after {
content: " *";
color: red;
}
```
一つのフォームで複数のモデルを扱う
----------------------------------
フォームに HTML タグを追加するためには、素の HTML を使うか、または、上記の例の [[yii\helpers\Html::submitButton()|Html::submitButton()]] のように、[[yii\helpers\Html|Html]] ヘルパクラスのメソッドを使うことが出来ます。
時として、一つのフォームで同じ種類の複数のモデルを扱わなければならないことがあります。
例えば、それぞれが「名前-値」の形で保存され、`Setting` モデルとして表される複数の設定項目を扱うフォームです。
以下に、これを Yii で実装する方法を示します。
> Tip|ヒント: あなたのアプリケーションで Twitter Bootstrap CSS を使っている場合は、[[yii\widgets\ActiveForm]] の代りに [[yii\bootstrap\ActiveForm]] を使うのが良いでしょう。
> これは ActiveForm クラスのエクステンションであり、bootstrap CSS フレームワークで使用するための追加のスタイルをサポートしています。
コントローラアクションから始めましよう。
```php
namespace app\controllers;
use Yii;
use yii\base\Model;
use yii\web\Controller;
use app\models\Setting;
class SettingsController extends Controller
{
// ...
public function actionUpdate()
{
$settings = Setting::find()->indexBy('id')->all();
if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {
foreach ($settings as $setting) {
$setting->save(false);
}
return $this->redirect('index');
}
return $this->render('update', ['settings' => $settings]);
}
}
```
上記のコードでは、データベースからモデルを読み出すときに `indexBy` を使って、モデルの ID でインデックスされた配列にモデルを代入しています。
このインデックスが、後で、フォームフィールドを特定するために使われます。
`loadMultiple` が POST から来るフォームデータを複数のモデルに代入し、`validateMultiple` が全てのモデルを一度に検証します。
保存するときの検証をスキップするために、`save` のパラメータに `false` を渡しています。
次に、`update` ビューにあるフォームです。
```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
$form = ActiveForm::begin();
> tip|ヒント: 必須フィールドをアスタリスク付きのスタイルにするために、次の CSS を使うことが出来ます。
>
>```css
>div.required label:after {
> content: " *";
> color: red;
>}
>```
foreach ($settings as $index => $setting) {
echo Html::encode($setting->name) . ': ' . $form->field($setting, "[$index]value");
}
次の節 [入力を検証する](input-validation.md) は、送信されたフォームデータのサーバ側でのバリデーションと、ajax バリデーションおよびクライアント側バリデーションを扱います。
ActiveForm::end();
```
フォームのもっと複雑な使用方法については、以下の節を読んで下さい。
ここで全ての設定項目について、それぞれ、名前と値を入れたインプットをレンダリングしています
インプットの名前に適切なインデックスを追加することが肝腎です
というのは、`loadMultiple` がそれを見て、どのモデルにどの値を代入するかを決定するからです
- [表形式インプットのデータ収集](input-tabular-input.md) - 同じ種類の複数のモデルのデータを収集する
- [複数のモデルを持つ複雑なフォーム](input-multiple-models.md) - 同じフォームの中で複数の異なるモデルを扱う
- [ファイルをアップロードする](input-file-upload) - フォームを使ってファイルをアップロードする方法
複数のモデルを扱う複雑なフォーム
================================
複雑なユーザインタフェイスにおいては、一つのフォームにユーザが入力したデータをデータベースの異なる複数のテーブルに保存しなければならないということが生じ得ます。
Yii のフォームの概念に従うと、単一モデルのフォームと比べても、ほとんど複雑さを加えることなく、そういうフォームを構築することが出来ます。
単一モデルの場合と同じように、サーバ側では次のような検証のスキーマに従います。
1. モデルのクラスをインスタンス化する。
2. モデルの属性に入力されたデータを投入する。
3. 全てのモデルを検証する。
4. 全てのモデルに対して検証が通った場合は、それらを保存する。
5. 検証が失敗した場合、または、データが送信されなかった場合は、全てのモデルのインスタンスをビューに渡してフォームを表示する。
次に、一つのフォームで複数のモデルを使用する例を示します ... (未定)
複数のモデルの例
----------------
> Note|注意: この節はまだ執筆中です。
>
> まだ内容がありません。
(未定)
依存するモデル
--------------
> Note|注意: この節はまだ執筆中です。
>
> まだ内容がありません。
(未定)
表形式インプットでデータを収集する
==================================
時として、一つのフォームで同じ種類の複数のモデルを扱わなければならないことがあります。
例えば、それぞれが「名前-値」の形で保存され、`Setting` [アクティブレコード](db-active-record.md) モデルとして表される複数の設定項目を扱うフォームです。
この種のフォームは「表形式インプット」と呼ばれることもよくあります。
これとは対照的な、異なる種類のさまざまなモデルを扱うことについては、[複数のモデルを持つ複雑なフォーム](input-multiple-models.md) の節で扱います。
以下に、表形式インプットを Yii で実装する方法を示します。
カバーすべき三つの異なる状況があり、それぞれ少しずつ異なる処理をしなければなりません。
- 特定の数のデータベースレコードを更新する
- 不特定の数の新しいレコードを作成する
- 一つのページでレコードを更新、作成、および、削除する
前に説明した単一モデルのフォームとは対照的に、モデルの配列を扱うことになります。
この配列がビューに渡されて、各モデルのためのインプットフィールドが表のような形式で表示され、そして、複数のモデルを一度にロードしたり検証したりするために [[yii\base\Model]] のヘルパメソッドを使用します。
- [[yii\base\Model::loadMultiple()|Model::loadMultiple()]] - 送信されたデータをモデルの配列にロードします。
- [[yii\base\Model::validateMultiple()|Model::validateMultiple()]] - モデルの配列を検証します。
### 特定の数のレコードを更新する
コントローラのアクションから始めましょう。
```php
<?php
namespace app\controllers;
use Yii;
use yii\base\Model;
use yii\web\Controller;
use app\models\Setting;
class SettingsController extends Controller
{
// ...
public function actionUpdate()
{
$settings = Setting::find()->indexBy('id')->all();
if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {
foreach ($settings as $setting) {
$setting->save(false);
}
return $this->redirect('index');
}
return $this->render('update', ['settings' => $settings]);
}
}
```
上記のコードでは、データベースからモデルを読み出すときに [[yii\db\ActiveQuery::indexBy()|indexBy()]] を使って、モデルのプライマリキーでインデックスされた配列にデータを投入しています。
このインデックスが、後で、フォームフィールドを特定するために使われます。
[[yii\base\Model::loadMultiple()|Model::loadMultiple()]] が POST から来るフォームデータを複数のモデルに代入し、[[yii\base\Model::validateMultiple()|Model::validateMultiple()]] が全てのモデルを一度に検証します。
保存するときには、`validateMultiple()` を使ってモデルの検証を済ませていますので、[[yii\db\ActiveRecord::save()|save()]] のパラメータに `false` を渡して、二度目の検証を実行しないようにしています。
次に、`update` ビューにあるフォームです。
Now the form that's in `update` view:
```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
$form = ActiveForm::begin();
foreach ($settings as $index => $setting) {
echo $form->field($setting, "[$index]value")->label($setting->name);
}
ActiveForm::end();
```
ここで全ての設定項目について、それぞれ、項目名を示すラベルと、項目の値を入れたインプットをレンダリングしています。
インプットの名前に適切なインデックスを追加することが肝腎です。
というのは、`loadMultiple` がそれを見て、どのモデルにどの値を代入するかを決定するからです。
### 不特定の数の新しいレコードを作成する
新しいレコードを作成するのは、モデルのインスタンスを作成する部分を除いて、更新の場合と同じです。
```php
public function actionCreate()
{
$count = count(Yii::$app->request->post('Setting', []));
$settings = [new Setting()];
for($i = 1; $i < $count; $i++) {
$settings[] = new Setting();
}
// ...
}
```
ここでは、デフォルトで一個のモデルを含む `$settings` 配列を初期値として作成し、少なくとも一個のテキストフィールドが常にビューに表示されるようにしています。
そして、受信したインプットの行数に合せて、配列にモデルを追加しています。
ビューでは javascript を使ってインプットの行を動的に追加することが出来ます。
### 更新、作成、削除を一つのページに組み合わせる
> Note|注意: この節はまだ執筆中です。
>
> まだ内容がありません。
(未定)
......@@ -2,30 +2,29 @@
======
モデルは [MVC](http://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャの一部を成すものです。
これは、業務のデータ、規則、ロジックを表現するオブジェクトです。
これは、ビジネスのデータ、規則、ロジックを表現するオブジェクトです。
モデルクラスは、[[yii\base\Model]] またはその子クラスを拡張することによって作成することが出来ます。
基底クラス [[yii\base\Model]] は数多くの有用な機能をサポートしています。
基底クラス [[yii\base\Model]] は、次のように、数多くの有用な機能をサポートしています。
* [属性](#attributes): 業務のデータを表現する。通常のオブジェクトプロパティや配列要素のようにしてアクセス出来る
* [属性のラベル](#attribute-labels): 属性の表示ラベルを規定する
* [一括代入](#massive-assignment): 一回のステップで複数の属性にデータを投入することをサポートしてい
* [検証規則](#validation-rules): 宣言された検証規則に基いて入力されたデータを確認する
* [データのエクスポート](#data-exporting): カスタマイズ可能な書式を使ってモデルのデータを配列の形式にエクスポートすることが出来る
* [属性](#attributes): ビジネスデータを表現します。通常のオブジェクトプロパティや配列要素のようにしてアクセスすることが出来ます
* [属性のラベル](#attribute-labels): 属性の表示ラベルを指定します
* [一括代入](#massive-assignment): 一回のステップで複数の属性にデータを投入することをサポートしています
* [検証規則](#validation-rules): 宣言された検証規則に基いて入力されたデータの有効性を保証します
* [データのエクスポート](#data-exporting): カスタマイズ可能な形式でモデルデータを配列にエクスポートすることが出来ます
`Model` クラスは、[アクティブレコード](db-active-record.md) のような、更に高度なモデルの基底クラスでもあります。
ういう高度なモデルについての詳細は、関連するドキュメントを参照してください。
れらの高度なモデルについての詳細は、関連するドキュメントを参照してください。
> Info|情報: あなたのモデルクラスの基底クラスとして [[yii\base\Model]] を使うことは必須の条件ではありません。
しかしながら、Yii のコンポーネントの多くが [[yii\base\Model]] をサポートするように作られていますので、
通常は [[yii\base\Model]] がモデルの基底クラスとして推奨されます。
> Info|情報: あなたのモデルクラスの基底として [[yii\base\Model]] を使うことが要求されている訳ではありません。
しかしながら、Yii のコンポーネントの多くが [[yii\base\Model]] をサポートするように作られていますので、通常は [[yii\base\Model]] がモデルの基底クラスとして推奨されます。
## 属性<span id="attributes"></span>
## 属性 <span id="attributes"></span>
モデルは業務のデータを *属性* の形式で表現します。
モデルはビジネスデータを *属性* の形式で表現します。
全ての属性はそれぞれパブリックにアクセス可能なモデルのプロパティと同様なものです。
[[yii\base\Model::attributes()]] メソッドが、モデルがどのような属性を持つかを定します。
[[yii\base\Model::attributes()]] メソッドが、モデルがどのような属性を持つかを定します。
属性に対しては、通常のオブジェクトプロパティにアクセスするのと同じようにして、アクセスすることが出来ます。
......@@ -38,7 +37,7 @@ echo $model->name;
```
また、配列の要素にアクセスするようして、属性にアクセスすることも出来ます。
これは、[[yii\base\Model]] が [ArrayAccess インターフェイス](http://php.net/manual/ja/class.arrayaccess.php)[ArrayIterator クラス](http://jp2.php.net/manual/ja/class.arrayiterator.php) をサポートしているおかげです。
これは、[[yii\base\Model]] が [ArrayAccess インターフェイス](http://php.net/manual/ja/class.arrayaccess.php)[ArrayIterator クラス](http://jp2.php.net/manual/ja/class.arrayiterator.php) をサポートしている恩恵です。
```php
$model = new \app\models\ContactForm;
......@@ -54,10 +53,10 @@ foreach ($model as $name => $value) {
```
### 属性を定義する<span id="defining-attributes"></span>
### 属性を定義する <span id="defining-attributes"></span>
あなたのモデルが [[yii\base\Model]] を直接に拡張するものである場合、既定によって、全ての *static でない public な* メンバ変数は属性となります。
例えば、次に示す `ContactForm` モデルは4つの属性、すなわち、`name``email``subject`、そして、`body` を持ちます。
あなたのモデルが [[yii\base\Model]] を直接に拡張するものである場合、デフォルトでは、全ての *static でない public な* メンバ変数は属性となります。
例えば、次に示す `ContactForm` モデルはつの属性、すなわち、`name``email``subject`、そして、`body` を持ちます。
この `ContactForm` モデルは、HTML フォームから受け取る入力データを表現するために使われます。
```php
......@@ -76,10 +75,9 @@ class ContactForm extends Model
[[yii\base\Model::attributes()]] をオーバーライドして、属性を異なる方法で定義することが出来ます。
このメソッドはモデルの中の属性の名前を返さなくてはなりません。
このメソッドはモデルが持つ属性の名前を返さなくてはなりません。
例えば、[[yii\db\ActiveRecord]] は、関連付けられたデータベーステーブルのコラム名を属性の名前として返すことによって、属性を定義しています。
これと同時に、定義された属性に対して通常のオブジェクトプロパティと同じようにアクセスすることが出来るように、
`__get()``__set()` などのマジックメソッドをオーバーライドする必要があるかもしれないことに注意してください。
ただし、これと同時に、定義された属性に対して通常のオブジェクトプロパティと同じようにアクセスすることが出来るように、`__get()``__set()` などのマジックメソッドをオーバーライドする必要があるかもしれないことに注意してください。
### 属性のラベル <span id="attribute-labels"></span>
......@@ -96,7 +94,7 @@ $model = new \app\models\ContactForm;
echo $model->getAttributeLabel('name');
```
既定では、属性のラベルは属性の名前から自動的に生成されます。
デフォルトでは、属性のラベルは属性の名前から自動的に生成されます。
ラベルの生成は [[yii\base\Model::generateAttributeLabel()]] というメソッドによって行われます。
このメソッドは、キャメルケースの変数名を複数の単語に分割し、各単語の最初の文字を大文字にします。
例えば、`username``Username` となり、`firstName``First Name` となります。
......@@ -142,35 +140,35 @@ public function attributeLabels()
}
```
さらに進んで、条件に従って属性のラベルを定義しても構いません。例えば、モデルが使用される
[シナリオ](#scenarios) に基づいて、同じ属性に対して違うラベルを返すことことが出来ます。
条件に従って属性のラベルを定義することも出来ます。
例えば、モデルが使用される [シナリオ](#scenarios) に基づいて、同じ属性に対して違うラベルを返すことことが出来ます。
> Info|情報: 厳密に言えば、属性のラベルは [ビュー](structure-views.md) の一部を成すものです。
しかし、たいていの場合、モデルの中でラベルを宣言する方が便利が良く、結果としてクリーンで再利用可能なコードになります。
## シナリオ<span id="scenarios"></span>
## シナリオ <span id="scenarios"></span>
モデルはさまざまに異なる *シナリオ* で使用されます。
例えば、`User` モデルはユーザログインの入力を収集するために使われますが、同時に、ユーザ登録の目的でも使われます。
異なるシナリオの下では、モデルが使用する業務のルールとロジックも異なるでしょう
異なるシナリオの下では、モデルが使用するビジネスルールとロジックも異なるものになり得ます
例えば、`email` 属性はユーザ登録の際には必須とされるかも知れませんが、ログインの際にはそうではないでしょう。
モデルは [[yii\base\Model::scenario]] プロパティを使って、自身が使われているシナリオを追跡します。
既定では、モデルは `default` という単一のシナリオのみをサポートします。
デフォルトでは、モデルは `default` という一つのシナリオだけをサポートします。
次のコードは、モデルのシナリオを設定する二つの方法を示すものです。
```php
// プロパティとしてシナリオをセットする
// シナリオをプロパティとして設定する
$model = new User;
$model->scenario = 'login';
// シナリオは設定情報でセットされ
// シナリオを設定情報で設定す
$model = new User(['scenario' => 'login']);
```
既定では、モデルによってサポートされるシナリオは、モデルで宣言されている [検証規則](#validation-rules) によって決定されます。
しかし、次のように、[[yii\base\Model::scenarios()]] メソッドをオーバーライドして、この動作をカスタマイズすることが出来ます。
デフォルトでは、モデルによってサポートされるシナリオは、モデルで宣言されている [検証規則](#validation-rules) によって決定されます。
しかし、次のように、[[yii\base\Model::scenarios()]] メソッドをオーバーライドして、この振る舞いをカスタマイズすることが出来ます。
```php
namespace app\models;
......@@ -190,16 +188,14 @@ 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()` のデフォルトの実装は、検証規則の宣言メソッドである [[yii\base\Model::rules()]] に現れる全てのシナリオを返すものです。
`scenarios()` をオーバーライドするときに、デフォルトのシナリオに加えて新しいシナリオを導入したい場合は、次のようなコードを書きます。
```php
namespace app\models;
......@@ -222,16 +218,17 @@ class User extends ActiveRecord
しかし、他の目的に使うことも可能です。例えば、現在のシナリオに基づいて異なる [属性のラベル](#attribute-labels) を宣言することも出来ます。
## 検証規則<span id="validation-rules"></span>
## 検証規則 <span id="validation-rules"></span>
モデルのデータをエンドユーザから受け取ったときは、データを検証して、それが一定の規則 (*検証規則*、あるいは、いわゆる *ビジネスルール*) を満たしていることを確認しなければなりません。
`ContactForm` モデルを例に挙げるなら、全ての属性が空ではなく、`email` 属性が有効なメールアドレスを含んでいることを確認したいでしょう。
いずれかの属性の値が対応するビジネスルールを満たしていないときは、ユーザがエラーを訂正するのを助ける適切なエラーメッセージが表示されるべきです
いずれかの属性の値が対応するビジネスルールを満たしていないときは、ユーザがエラーを訂正するのを助ける適切なエラーメッセージが表示されなければなりません
受信したデータを検証するために、[[yii\base\Model::validate()]] を呼ぶことが出来ます。
このメソッドは、[[yii\base\Model::rules()]] で宣言された検証規則を使って、該当するすべての属性を検証します。
エラーが見つからなければ、メソッドは true を返します。そうでなければ、[[yii\base\Model::errors]]
にエラーを保存して、false を返します。例えば、
エラーが見つからなければ、メソッドは true を返します。
そうでなければ、[[yii\base\Model::errors]] にエラーを保存して、false を返します。
例えば、
```php
$model = new \app\models\ContactForm;
......@@ -264,8 +261,8 @@ public function rules()
}
```
個の規則は、一個または複数の属性を検証するために使うことが出来ます。
また、一個の属性は、一個または複数の規則によって検証することが出来ます。
つの規則は、一つまたは複数の属性を検証するために使うことが出来ます。
また、一つの属性は、一つまたは複数の規則によって検証することが出来ます。
検証規則をどのように宣言するかについての詳細は [入力を検証する](input-validation.md) の節を参照してください。
時として、特定の [シナリオ](#scenarios) にのみ適用される規則が必要になるでしょう。
......@@ -287,16 +284,15 @@ public function rules()
`on` プロパティを指定しない場合は、その規則は全てのシナリオに適用されることになります。
現在の [[yii\base\Model::scenario|シナリオ]] に適用可能な規則は *アクティブな規則* と呼ばれます。
属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、その属性が `rules()`
の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。
属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、その属性が `rules()` の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。
## 一括代入<span id="massive-assignment"></span>
## 一括代入 <span id="massive-assignment"></span>
一括代入は、一行のコードを書くだけで、ユーザの入力したデータをモデルに投入できる便利な方法です。
一括代入は、入力されたデータを [[yii\base\Model::$attributes]] に直接に代入することによって、モデルの属性にデータを投入します。
一括代入は、一行のコードを書くだけで、ユーザの入力した複数のデータをモデルに投入できる便利な方法です。
一括代入は、入力されたデータを [[yii\base\Model::$attributes]] プロパティに直接に代入することによって、モデルの属性にデータを投入します。
次の二つのコード断片は等価であり、どちらもエンドユーザから送信されたフォームのデータを `ContactForm` モデルの属性に割り当てようとするものです。
明らかに、一括代入を使う前者の方が、後者よりも明で間違いも起こりにくいでしょう。
明らかに、一括代入を使う前者の方が、後者よりも明で間違いも起こりにくいでしょう。
```php
$model = new \app\models\ContactForm;
......@@ -313,11 +309,11 @@ $model->body = isset($data['body']) ? $data['body'] : null;
```
### 安全な属性<span id="safe-attributes"></span>
### 安全な属性 <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
public function scenarios()
......@@ -329,14 +325,14 @@ public function scenarios()
}
```
> Info|情報: 一括代入が安全な属性に対してのみ適用されるのは、どの属性がエンドユーザの入力データによって修正されうるかを制御する必要があるからです。
例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、
この属性が修正できるのは、管理者がバックエンドのインターフェイスを通じてする時だけに制限したいでしょう。
> Info|情報: 一括代入が安全な属性に対してのみ適用されるのは、エンドユーザの入力データがどの属性を修正することが出来るか、ということを制御する必要があるからです。
例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、この属性が修正できるのは、管理者がバックエンドのインターフェイスを通じてする時だけに制限したいでしょう。
[[yii\base\Model::scenarios()]] の既定の実装は [[yii\base\Model::rules()]] に現われる全てのシナリオと属性を返すものです。
[[yii\base\Model::scenarios()]] のデフォルトの実装は [[yii\base\Model::rules()]] に現われる全てのシナリオと属性を返すものです。
従って、このメソッドをオーバーライドしない場合は、アクティブな検証規則のどれかに出現する限り、その属性は安全である、ということになります。
このため、実際に検証することなく属性を安全であると宣言できるように、`safe` というエイリアスを与えられた特別なバリデータが提供されています。例えば、次の規則は `title``description` の両方が安全な属性であると宣言しています。
このため、実際に検証することなく属性を安全であると宣言できるように、`safe` というエイリアスを与えられた特別なバリデータが提供されています。
例えば、次の規則は `title``description` の両方が安全な属性であると宣言しています。
```php
public function rules()
......@@ -348,13 +344,12 @@ public function rules()
```
### 安全でない属性<span id="unsafe-attributes"></span>
### 安全でない属性 <span id="unsafe-attributes"></span>
上記で説明したように、[[yii\base\Model::scenarios()]] メソッドは二つの目的を持っています。
すなわち、どの属性が検証されるべきかを決めることと、どの属性が安全であるかを決めることです。
めったにない場合として、属性を検証する必要はあるが、安全であるという印は付けたくない、ということがあります。
そういう時は、下の例の `secret` 属性のように、`scenarios()` の中で宣言するときに属性の名前の前に感嘆符
`!` を前置することが出来ます。
そういう時は、下の例の `secret` 属性のように、名前の前に感嘆符 `!` を付けて `scenarios()` の中で宣言することが出来ます。
```php
public function scenarios()
......@@ -365,8 +360,9 @@ public function scenarios()
}
```
このモデルが `login` シナリオにある場合、三つの属性は全て検証されます。しかし、`username`
`password` の属性だけが一括代入が可能です。`secret` 属性に入力値を割り当てるためには、下記のように明示的に代入を実行する必要があります。
このモデルが `login` シナリオにある場合、三つの属性は全て検証されます。
しかし、`username``password` の属性だけが一括代入が可能です。
`secret` 属性に入力値を割り当てるためには、下記のように明示的に代入を実行する必要があります。
```php
$model->secret = $secret;
......@@ -375,12 +371,13 @@ $model->secret = $secret;
## データのエクスポート <span id="data-exporting"></span>
モデルを他の形式にエクスポートする必要が生じることはよくあります。例えば、一群のモデルを JSON や
Excel 形式に変換したい場合があるでしょう。
モデルを他の形式にエクスポートする必要が生じることはよくあります。
例えば、モデルのコレクションを JSON や Excel 形式に変換したい場合があるでしょう。
エクスポートのプロセスは二つの独立したステップに分割することが出来ます。
最初のステップで、モデルは配列に変換されます。そして第二のステップで、配列が目的の形式に変換されます。
あなたは最初のステップだけに注力しても構いません。と言うのは、第二のステップは汎用的なデータフォーマッタ、例えば [[yii\web\JsonResponseFormatter]]
によって達成できるからです。
最初のステップで、モデルは配列に変換されます。
そして第二のステップで、配列が目的の形式に変換されます。
あなたは最初のステップだけに注力することが出来ます。
と言うのは、第二のステップは汎用的なデータフォーマッタ、例えば [[yii\web\JsonResponseFormatter]] によって達成できるからです。
モデルを配列に変換する最も簡単な方法は、[[yii\base\Model::$attributes]] プロパティを使うことです。
例えば、
......@@ -390,27 +387,24 @@ $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
ウェブサービスの開発においては、これがモデルをエクスポートする既定の方法となっています。
このメソッドのデフォルトの動作は [[yii\base\Model::$attributes]] のそれと同じものです。
しかしながら、このメソッドを使うと、どのデータ項目 (*フィールド* と呼ばれます) を結果の配列に入れるか、そして、その項目にどのような書式を適用するかを選ぶことが出来ます。
実際、[レスポンス形式の設定](rest-response-formatting.md) で説明されているように、RESTful ウェブサービスの開発においては、これがモデルをエクスポートするデフォルトの方法となっています。
### フィールド<span id="fields"></span>
### フィールド <span id="fields"></span>
フィールドとは、単に、モデルの [[yii\base\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる名前付きの要素のことです。
フィールドとは、単に、モデルの [[yii\base\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる名前付きの要素のことです。
既定では、フィールドの名前は属性の名前と等しいものになります。しかし、この既定の動作は、[[yii\base\Model::fields()|fields()]]
および/または [[yii\base\Model::extraFields()|extraFields()]] メソッドをオーバーライドして、変更することが出来ます。
デフォルトでは、フィールドの名前は属性の名前と等しいものになります。
しかし、このデフォルトの動作は、[[yii\base\Model::fields()|fields()]] および/または [[yii\base\Model::extraFields()|extraFields()]] メソッドをオーバーライドして、変更することが出来ます。
どちらのメソッドも、フィールド定義のリストを返します。
`fields()` によって定義されるフィールドは、デフォルトフィールドです。すなわち、`toArray()` はデフォルトでこれらのフィールドを返す、ということを意味します。
`extraFields()` メソッドは、`$expand` パラメータによって指定する限りにおいて `toArray()` によって返される追加のフィールドを定義するものです。
例として、次のコードは、`fields()` で定義された全てのフィールドと、
(`extraFields()` で定義されていれば) `prettyName``fullAddress` フィールドを返すものです。
`extraFields()` メソッドは、`$expand` パラメータによって指定する限りにおいて `toArray()` によって返される、追加のフィールドを定義するものです。
例として、次のコードは、`fields()` で定義された全てのフィールドと、(`extraFields()` で定義されていれば) `prettyName``fullAddress` フィールドを返すものです。
```php
$array = $model->toArray([], ['prettyName', 'fullAddress']);
......@@ -423,8 +417,8 @@ $array = $model->toArray([], ['prettyName', 'fullAddress']);
例えば、
```php
// 明示的に全てのフィールドをリストする方法。(API の後方互換性を保つために) DB テーブルやモデル属性の
// 変更がフィールドの変更を引き起こさないことを保証したい場合に適している。
// 明示的に全てのフィールドをリストする方法。(API の後方互換性を保つために) DB テーブルや
// モデル属性の変更がフィールドの変更を引き起こさないことを保証したい場合に適している。
public function fields()
{
return [
......@@ -441,7 +435,7 @@ public function fields()
];
}
// いくつかのフィールドを除する方法。親の実装を継承しつつ、公開すべきでないフィールドは
// いくつかのフィールドを除する方法。親の実装を継承しつつ、公開すべきでないフィールドは
// 除外したいときに適している。
public function fields()
{
......@@ -454,17 +448,16 @@ public function fields()
}
```
> Warning|警告: 既定ではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、
> 公開すべきでない情報が含まれていないことを確認すべきです。そういう情報がある場合は、
> `fields()` をオーバーライドして、除去すべきです。上記の例では、`auth_key`、`password_hash`
> および `password_reset_token` を選んで除去しています。
> Warning|警告: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、公開すべきでない情報が含まれていないことを確認しなければなりません。
> そういう情報がある場合は、`fields()` をオーバーライドして、除外しなければなりません。
> 上記の例では、`auth_key`、`password_hash` および `password_reset_token` を除外しています。
## ベストプラクティス<span id="best-practices"></span>
## ベストプラクティス <span id="best-practices"></span>
モデルは、業務のデータ、規則、ロジックを表わす中心的なオブジェクトです。
モデルは、さまざまな場所で再利用される必要がよくあります。
良く設計されたアプリケーションでは、通常、モデルは [コントローラ](structure-controllers.md) よりもはるかに重いものになります。
モデルは、ビジネスのデータ、規則、ロジックを表わす中心的なオブジェクトです。
モデルは、たいてい、さまざまな場所で再利用される必要があります。
良く設計されたアプリケーションでは、通常、モデルは [コントローラ](structure-controllers.md) よりもはるかに太ったものになります。
要約すると、モデルは、
......@@ -474,22 +467,20 @@ public function fields()
* リクエスト、セッション、または他の環境データに直接アクセスするべきではありません。
これらのデータは、[コントローラ](structure-controllers.md) によってモデルに注入されるべきです。
* HTML を埋め込むなどの表示用のコードは避けるべきです - 表示は [ビュー](structure-views.md) で行う方が良いです。
* あまりに多くの [シナリオ](#scenarios)単一のモデルで持つことは避けましょう。
* あまりに多くの [シナリオ](#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) において、対応する基底モデルクラスから拡張した具体的なモデルクラスを定義します。
この具体的なモデルクラスが、そのアプリケーションやモジュールに固有の規則やロジックを含むべきです。
例えば、[アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) の中で、基底モデルクラス `common\models\Post`
を定義することが出来ます。次に、フロントエンドアプリケーションにおいては、`common\models\Post` から拡張した具体的なモデルクラス
`frontend\models\Post` を定義して使います。また、バックエンドアプリケーションにおいても、同様に、`backend\models\Post` を定義します。
例えば、[アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) の中で、基底モデルクラス `common\models\Post` を定義することが出来ます。
次に、フロントエンドアプリケーションにおいては、`common\models\Post` から拡張した具体的なモデルクラス `frontend\models\Post` を定義して使います。
また、バックエンドアプリケーションにおいても、同様に、`backend\models\Post` を定義します。
この戦略を取ると、`frontend\models\Post` の中のコードはフロントエンドアプリケーション固有のものであると保証することが出来ます。
そして、フロントエンドのコードにどのような変更を加えても、バックエンドアプリケーションを壊すかもしれないと心配する必要がなくなります。
......@@ -62,7 +62,7 @@ Composer がインストールされていれば、次のコマンドを使っ
php /path/to/yii-application/init --env=Production --overwrite=All
```
2. 新しいデータベースを作成し、それに従って `common/config/main-local.php``components.db` の構成情報を修正します。
2. 新しいデータベースを作成し、それに従って `common/config/main-local.php``components['db']` の構成情報を修正します。
3. コンソールコマンド `yii migrate` でマイグレーションを適用します。
4. ウェブサーバのドキュメントルートを設定します。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment