structure-models.md 28 KB
Newer Older
Nobuo Kihara committed
1 2 3 4
モデル
======

モデルは [MVC](http://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャの一部を成すものです。
5
これは、ビジネスのデータ、規則、ロジックを表現するオブジェクトです。
Nobuo Kihara committed
6 7

モデルクラスは、[[yii\base\Model]] またはその子クラスを拡張することによって作成することが出来ます。
8
基底クラス [[yii\base\Model]] は、次のように、数多くの有用な機能をサポートしています。
Nobuo Kihara committed
9

10 11 12 13 14
* [属性](#attributes): ビジネスデータを表現します。通常のオブジェクトプロパティや配列要素のようにしてアクセスすることが出来ます。
* [属性のラベル](#attribute-labels): 属性の表示ラベルを指定します。
* [一括代入](#massive-assignment): 一回のステップで複数の属性にデータを投入することをサポートしています。
* [検証規則](#validation-rules): 宣言された検証規則に基いて入力されたデータの有効性を保証します。
* [データのエクスポート](#data-exporting): カスタマイズ可能な形式でモデルデータを配列にエクスポートすることが出来ます。
Nobuo Kihara committed
15 16

`Model` クラスは、[アクティブレコード](db-active-record.md) のような、更に高度なモデルの基底クラスでもあります。
17
それらの高度なモデルについての詳細は、関連するドキュメントを参照してください。
Nobuo Kihara committed
18

19 20
> Info|情報: あなたのモデルクラスの基底として [[yii\base\Model]] を使うことが要求されている訳ではありません。
  しかしながら、Yii のコンポーネントの多くが [[yii\base\Model]] をサポートするように作られていますので、通常は [[yii\base\Model]] がモデルの基底クラスとして推奨されます。
Nobuo Kihara committed
21 22


23
## 属性 <span id="attributes"></span>
Nobuo Kihara committed
24

25
モデルはビジネスデータを *属性* の形式で表現します。
Nobuo Kihara committed
26
全ての属性はそれぞれパブリックにアクセス可能なモデルのプロパティと同様なものです。
27
[[yii\base\Model::attributes()]] メソッドが、モデルがどのような属性を持つかを指定します。
Nobuo Kihara committed
28 29 30 31 32 33 34 35 36 37 38 39

属性に対しては、通常のオブジェクトプロパティにアクセスするのと同じようにして、アクセスすることが出来ます。

```php
$model = new \app\models\ContactForm;

// "name" は ContactForm の属性
$model->name = 'example';
echo $model->name;
```

また、配列の要素にアクセスするようして、属性にアクセスすることも出来ます。
40
これは、[[yii\base\Model]] が [ArrayAccess インターフェイス](http://php.net/manual/ja/class.arrayaccess.php)[ArrayIterator クラス](http://jp2.php.net/manual/ja/class.arrayiterator.php) をサポートしている恩恵です。
Nobuo Kihara committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

```php
$model = new \app\models\ContactForm;

// 配列要素のように属性にアクセスする
$model['name'] = 'example';
echo $model['name'];

// 属性に反復アクセスする
foreach ($model as $name => $value) {
    echo "$name: $value\n";
}
```


56
### 属性を定義する <span id="defining-attributes"></span>
Nobuo Kihara committed
57

58 59
あなたのモデルが [[yii\base\Model]] を直接に拡張するものである場合、デフォルトでは、全ての *static でない public な* メンバ変数は属性となります。
例えば、次に示す `ContactForm` モデルは四つの属性、すなわち、`name``email``subject`、そして、`body` を持ちます。
Nobuo Kihara committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
この `ContactForm` モデルは、HTML フォームから受け取る入力データを表現するために使われます。

```php
namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
}
```


[[yii\base\Model::attributes()]] をオーバーライドして、属性を異なる方法で定義することが出来ます。
78
このメソッドはモデルが持つ属性の名前を返さなくてはなりません。
79
例えば、[[yii\db\ActiveRecord]] は、関連付けられたデータベーステーブルのコラム名を属性の名前として返すことによって、属性を定義しています。
80
ただし、これと同時に、定義された属性に対して通常のオブジェクトプロパティと同じようにアクセスすることが出来るように、`__get()``__set()` などのマジックメソッドをオーバーライドする必要があるかもしれないことに注意してください。
Nobuo Kihara committed
81 82


83
### 属性のラベル <span id="attribute-labels"></span>
Nobuo Kihara committed
84 85 86 87 88 89 90 91 92 93 94 95 96

属性の値を表示したり、入力してもらったりするときに、属性と関連付けられたラベルを表示する必要があることがよくあります。
例えば、`firstName` という名前の属性を考えたとき、入力フォームやエラーメッセージのような箇所でエンドユーザに表示するときは、もっとユーザフレンドリーな `First Name` というラベルを表示したいと思うでしょう。

[[yii\base\Model::getAttributeLabel()]] を呼ぶことによって属性のラベルを得ることが出来ます。例えば、

```php
$model = new \app\models\ContactForm;

// "Name" を表示する
echo $model->getAttributeLabel('name');
```

97
デフォルトでは、属性のラベルは属性の名前から自動的に生成されます。
Nobuo Kihara committed
98 99 100 101
ラベルの生成は [[yii\base\Model::generateAttributeLabel()]] というメソッドによって行われます。
このメソッドは、キャメルケースの変数名を複数の単語に分割し、各単語の最初の文字を大文字にします。
例えば、`username``Username` となり、`firstName``First Name` となります。

102
自動的に生成されるラベルを使用したくない場合は、[[yii\base\Model::attributeLabels()]] をオーバーライドして、属性のラベルを明示的に宣言することが出来ます。例えば、
Nobuo Kihara committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

```php
namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;

    public function attributeLabels()
    {
        return [
            'name' => 'Your name',
            'email' => 'Your email address',
            'subject' => 'Subject',
            'body' => 'Content',
        ];
    }
}
```

複数の言語をサポートするアプリケーションでは、属性のラベルを翻訳したいと思うでしょう。
129
これも、以下のように、[[yii\base\Model::attributeLabels()|attributeLabels()]] の中で行うことが出来ます。
Nobuo Kihara committed
130 131 132 133 134 135 136 137 138 139 140 141 142

```php
public function attributeLabels()
{
    return [
        'name' => \Yii::t('app', 'Your name'),
        'email' => \Yii::t('app', 'Your email address'),
        'subject' => \Yii::t('app', 'Subject'),
        'body' => \Yii::t('app', 'Content'),
    ];
}
```

143 144
条件に従って属性のラベルを定義することも出来ます。
例えば、モデルが使用される [シナリオ](#scenarios) に基づいて、同じ属性に対して違うラベルを返すことことが出来ます。
Nobuo Kihara committed
145 146

> Info|情報: 厳密に言えば、属性のラベルは [ビュー](structure-views.md) の一部を成すものです。
147
  しかし、たいていの場合、モデルの中でラベルを宣言する方が便利が良く、結果としてクリーンで再利用可能なコードになります。
Nobuo Kihara committed
148 149


150
## シナリオ <span id="scenarios"></span>
Nobuo Kihara committed
151 152 153

モデルはさまざまに異なる *シナリオ* で使用されます。
例えば、`User` モデルはユーザログインの入力を収集するために使われますが、同時に、ユーザ登録の目的でも使われます。
154
異なるシナリオの下では、モデルが使用するビジネスルールとロジックも異なるものになり得ます。
Nobuo Kihara committed
155 156 157
例えば、`email` 属性はユーザ登録の際には必須とされるかも知れませんが、ログインの際にはそうではないでしょう。

モデルは [[yii\base\Model::scenario]] プロパティを使って、自身が使われているシナリオを追跡します。
158
デフォルトでは、モデルは `default` という一つのシナリオだけをサポートします。
159
次のコードは、モデルのシナリオを設定する二つの方法を示すものです。
Nobuo Kihara committed
160 161

```php
162
// シナリオをプロパティとして設定する
Nobuo Kihara committed
163 164 165
$model = new User;
$model->scenario = 'login';

166
// シナリオを設定情報で設定する
Nobuo Kihara committed
167 168 169
$model = new User(['scenario' => 'login']);
```

170 171
デフォルトでは、モデルによってサポートされるシナリオは、モデルで宣言されている [検証規則](#validation-rules) によって決定されます。
しかし、次のように、[[yii\base\Model::scenarios()]] メソッドをオーバーライドして、この振る舞いをカスタマイズすることが出来ます。
Nobuo Kihara committed
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

```php
namespace app\models;

use yii\db\ActiveRecord;

class User extends ActiveRecord
{
    public function scenarios()
    {
        return [
            'login' => ['username', 'password'],
            'register' => ['username', 'email', 'password'],
        ];
    }
}
```

> Info|情報: 上記の例と後続の例では、モデルクラスは [[yii\db\ActiveRecord]] を拡張するものとなっています。
191
  というのは、複数のシナリオを使用することは、通常は、[アクティブレコード](db-active-record.md) クラスで発生するからです。
Nobuo Kihara committed
192 193 194

`seanarios()` メソッドは、キーがシナリオの名前であり、値が対応する *アクティブな属性* である配列を返します。
アクティブな属性とは、[一括代入](#massive-assignment) することが出来て、[検証](#validation-rules) の対象になる属性です。
195
上記の例では、`login` シナリオにおいては `username``password` の属性がアクティブであり、一方、`register` シナリオにおいては、`username``password` に加えて `email` もアクティブです。
Nobuo Kihara committed
196

197 198
`scenarios()` のデフォルトの実装は、検証規則の宣言メソッドである [[yii\base\Model::rules()]] に現れる全てのシナリオを返すものです。
`scenarios()` をオーバーライドするときに、デフォルトのシナリオに加えて新しいシナリオを導入したい場合は、次のようなコードを書きます。
Nobuo Kihara committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

```php
namespace app\models;

use yii\db\ActiveRecord;

class User extends ActiveRecord
{
    public function scenarios()
    {
        $scenarios = parent::scenarios();
        $scenarios['login'] = ['username', 'password'];
        $scenarios['register'] = ['username', 'email', 'password'];
        return $scenarios;
    }
}
```

シナリオの機能は、主として、[検証](#validation-rules)[属性の一括代入](#massive-assignment) によって使用されます。
しかし、他の目的に使うことも可能です。例えば、現在のシナリオに基づいて異なる [属性のラベル](#attribute-labels) を宣言することも出来ます。


221
## 検証規則 <span id="validation-rules"></span>
Nobuo Kihara committed
222 223

モデルのデータをエンドユーザから受け取ったときは、データを検証して、それが一定の規則 (*検証規則*、あるいは、いわゆる *ビジネスルール*) を満たしていることを確認しなければなりません。
224
`ContactForm` モデルを例に挙げるなら、全ての属性が空ではなく、`email` 属性が有効なメールアドレスを含んでいることを確認したいでしょう。
225
いずれかの属性の値が対応するビジネスルールを満たしていないときは、ユーザがエラーを訂正するのを助ける適切なエラーメッセージが表示されなければなりません。
Nobuo Kihara committed
226 227 228

受信したデータを検証するために、[[yii\base\Model::validate()]] を呼ぶことが出来ます。
このメソッドは、[[yii\base\Model::rules()]] で宣言された検証規則を使って、該当するすべての属性を検証します。
229 230 231
エラーが見つからなければ、メソッドは true を返します。
そうでなければ、[[yii\base\Model::errors]] にエラーを保存して、false を返します。
例えば、
Nobuo Kihara committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

```php
$model = new \app\models\ContactForm;

// モデルの属性にユーザの入力を代入する
$model->attributes = \Yii::$app->request->post('ContactForm');

if ($model->validate()) {
    // すべての入力値は有効である
} else {
    // 検証が失敗: $errors はエラーメッセージを含む配列
    $errors = $model->errors;
}
```


248 249
モデルに関連付けられた検証規則を宣言するためには、[[yii\base\Model::rules()]] メソッドをオーバーライドして、モデルの属性が満たすべき規則を返すようにします。
次の例は、`ContactForm` モデルのために宣言された検証規則を示します。
Nobuo Kihara committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263

```php
public function rules()
{
    return [
        // name、email、subject、body の属性が必須
        [['name', 'email', 'subject', 'body'], 'required'],

        // email 属性は、有効なメールアドレスでなければならない
        ['email', 'email'],
    ];
}
```

264 265
一つの規則は、一つまたは複数の属性を検証するために使うことが出来ます。
また、一つの属性は、一つまたは複数の規則によって検証することが出来ます。
266
検証規則をどのように宣言するかについての詳細は [入力を検証する](input-validation.md) の節を参照してください。
Nobuo Kihara committed
267

268 269
時として、特定の [シナリオ](#scenarios) にのみ適用される規則が必要になるでしょう。
そのためには、下記のように、規則に `on` プロパティを指定することが出来ます。
Nobuo Kihara committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

```php
public function rules()
{
    return [
        // "register" シナリオでは、username、email、password のすべてが必須
        [['username', 'email', 'password'], 'required', 'on' => 'register'],

        // "login" シナリオでは、username と password が必須
        [['username', 'password'], 'required', 'on' => 'login'],
    ];
}
```

`on` プロパティを指定しない場合は、その規則は全てのシナリオに適用されることになります。
現在の [[yii\base\Model::scenario|シナリオ]] に適用可能な規則は *アクティブな規則* と呼ばれます。

287
属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、その属性が `rules()` の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。
Nobuo Kihara committed
288 289


290
## 一括代入 <span id="massive-assignment"></span>
Nobuo Kihara committed
291

292 293
一括代入は、一行のコードを書くだけで、ユーザの入力した複数のデータをモデルに投入できる便利な方法です。
一括代入は、入力されたデータを [[yii\base\Model::$attributes]] プロパティに直接に代入することによって、モデルの属性にデータを投入します。
Nobuo Kihara committed
294
次の二つのコード断片は等価であり、どちらもエンドユーザから送信されたフォームのデータを `ContactForm` モデルの属性に割り当てようとするものです。
295
明らかに、一括代入を使う前者の方が、後者よりも明快で間違いも起こりにくいでしょう。
Nobuo Kihara committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

```php
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
```

```php
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;
```


312
### 安全な属性 <span id="safe-attributes"></span>
Nobuo Kihara committed
313

314 315 316
一括代入は、いわゆる *安全な属性*、すなわち、[[yii\base\Model::scenarios()]] においてモデルの現在の [[yii\base\Model::scenario|シナリオ]] のためにリストされている属性に対してのみ適用されます。
例えば、`User` モデルが次のようなシナリオ宣言を持っている場合において、現在のシナリオが `login` であるときは、`username``password` のみが一括代入が可能です。
その他の属性はいっさい触れられません。
Nobuo Kihara committed
317 318 319 320 321 322 323 324 325 326 327

```php
public function scenarios()
{
    return [
        'login' => ['username', 'password'],
        'register' => ['username', 'email', 'password'],
    ];
}
```

328 329
> Info|情報: 一括代入が安全な属性に対してのみ適用されるのは、エンドユーザの入力データがどの属性を修正することが出来るか、ということを制御する必要があるからです。
  例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、この属性が修正できるのは、管理者がバックエンドのインターフェイスを通じてする時だけに制限したいでしょう。
Nobuo Kihara committed
330

331
[[yii\base\Model::scenarios()]] のデフォルトの実装は [[yii\base\Model::rules()]] に現われる全てのシナリオと属性を返すものです。
Nobuo Kihara committed
332 333
従って、このメソッドをオーバーライドしない場合は、アクティブな検証規則のどれかに出現する限り、その属性は安全である、ということになります。

334 335
このため、実際に検証することなく属性を安全であると宣言できるように、`safe` というエイリアスを与えられた特別なバリデータが提供されています。
例えば、次の規則は `title``description` の両方が安全な属性であると宣言しています。
Nobuo Kihara committed
336 337 338 339 340 341 342 343 344 345 346

```php
public function rules()
{
    return [
        [['title', 'description'], 'safe'],
    ];
}
```


347
### 安全でない属性 <span id="unsafe-attributes"></span>
Nobuo Kihara committed
348

349
上記で説明したように、[[yii\base\Model::scenarios()]] メソッドは二つの目的を持っています。
Nobuo Kihara committed
350 351
すなわち、どの属性が検証されるべきかを決めることと、どの属性が安全であるかを決めることです。
めったにない場合として、属性を検証する必要はあるが、安全であるという印は付けたくない、ということがあります。
352
そういう時は、下の例の `secret` 属性のように、名前の前に感嘆符 `!` を付けて `scenarios()` の中で宣言することが出来ます。
Nobuo Kihara committed
353 354 355 356 357 358 359 360 361 362

```php
public function scenarios()
{
    return [
        'login' => ['username', 'password', '!secret'],
    ];
}
```

363 364 365
このモデルが `login` シナリオにある場合、三つの属性は全て検証されます。
しかし、`username``password` の属性だけが一括代入が可能です。
`secret` 属性に入力値を割り当てるためには、下記のように明示的に代入を実行する必要があります。
Nobuo Kihara committed
366 367 368 369 370 371

```php
$model->secret = $secret;
```


372
## データのエクスポート <span id="data-exporting"></span>
Nobuo Kihara committed
373

374 375
モデルを他の形式にエクスポートする必要が生じることはよくあります。
例えば、モデルのコレクションを JSON や Excel 形式に変換したい場合があるでしょう。
Nobuo Kihara committed
376
エクスポートのプロセスは二つの独立したステップに分割することが出来ます。
377 378 379 380
最初のステップで、モデルは配列に変換されます。
そして第二のステップで、配列が目的の形式に変換されます。
あなたは最初のステップだけに注力することが出来ます。
と言うのは、第二のステップは汎用的なデータフォーマッタ、例えば [[yii\web\JsonResponseFormatter]] によって達成できるからです。
Nobuo Kihara committed
381 382 383 384 385 386 387 388 389

モデルを配列に変換する最も簡単な方法は、[[yii\base\Model::$attributes]] プロパティを使うことです。
例えば、

```php
$post = \app\models\Post::findOne(100);
$array = $post->attributes;
```

390
デフォルトでは、[[yii\base\Model::$attributes]] プロパティは [[yii\base\Model::attributes()]] で宣言されている *全て* の属性の値を返します。
Nobuo Kihara committed
391 392

モデルを配列に変換するためのもっと柔軟で強力な方法は、[[yii\base\Model::toArray()]] メソッドを使うことです。
393 394 395
このメソッドのデフォルトの動作は [[yii\base\Model::$attributes]] のそれと同じものです。
しかしながら、このメソッドを使うと、どのデータ項目 (*フィールド* と呼ばれます) を結果の配列に入れるか、そして、その項目にどのような書式を適用するかを選ぶことが出来ます。
実際、[レスポンス形式の設定](rest-response-formatting.md) で説明されているように、RESTful ウェブサービスの開発においては、これがモデルをエクスポートするデフォルトの方法となっています。
Nobuo Kihara committed
396 397


398
### フィールド <span id="fields"></span>
Nobuo Kihara committed
399

400
フィールドとは、単に、モデルの [[yii\base\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる、名前付きの要素のことです。
Nobuo Kihara committed
401

402 403
デフォルトでは、フィールドの名前は属性の名前と等しいものになります。
しかし、このデフォルトの動作は、[[yii\base\Model::fields()|fields()]] および/または [[yii\base\Model::extraFields()|extraFields()]] メソッドをオーバーライドして、変更することが出来ます。
404 405
どちらのメソッドも、フィールド定義のリストを返します。
`fields()` によって定義されるフィールドは、デフォルトフィールドです。すなわち、`toArray()` はデフォルトでこれらのフィールドを返す、ということを意味します。
406 407
`extraFields()` メソッドは、`$expand` パラメータによって指定する限りにおいて `toArray()` によって返される、追加のフィールドを定義するものです。
例として、次のコードは、`fields()` で定義された全てのフィールドと、(`extraFields()` で定義されていれば) `prettyName``fullAddress` フィールドを返すものです。
Nobuo Kihara committed
408 409 410 411 412 413

```php
$array = $model->toArray([], ['prettyName', 'fullAddress']);
```

`fields()` をオーバーライドして、フィールドを追加、削除、リネーム、再定義することが出来ます。
414 415
`fields()` の返り値は配列でなければなりません。配列のキーはフィールド名であり、配列の値は対応するフィールド定義です。
フィールドの定義には、プロパティ/属性 の名前か、または、対応するフィールドの値を返す無名関数を使うことが出来ます。
Nobuo Kihara committed
416 417 418 419
フィールド名がそれを定義する属性名と同一であるという特殊な場合においては、配列のキーを省略することが出来ます。
例えば、

```php
420 421
// 明示的に全てのフィールドをリストする方法。(API の後方互換性を保つために) DB テーブルや
// モデル属性の変更がフィールドの変更を引き起こさないことを保証したい場合に適している。
Nobuo Kihara committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
public function fields()
{
    return [
        // フィールド名が属性名と同じ
        'id',

        // フィールド名は "email"、対応する属性名は "email_address"
        'email' => 'email_address',

        // フィールド名は "name"、その値は PHP コールバックで定義
        'name' => function () {
            return $this->first_name . ' ' . $this->last_name;
        },
    ];
}

438
// いくつかのフィールドを除外する方法。親の実装を継承しつつ、公開すべきでないフィールドは
Nobuo Kihara committed
439 440 441 442 443
// 除外したいときに適している。
public function fields()
{
    $fields = parent::fields();

444
    // 公開すべきでない情報を含むフィールドを削除する
Nobuo Kihara committed
445 446 447 448 449 450
    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);

    return $fields;
}
```

451 452 453
> Warning|警告: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、公開すべきでない情報が含まれていないことを確認しなければなりません。
> そういう情報がある場合は、`fields()` をオーバーライドして、除外しなければなりません。
> 上記の例では、`auth_key`、`password_hash` および `password_reset_token` を除外しています。
Nobuo Kihara committed
454 455


456
## ベストプラクティス <span id="best-practices"></span>
Nobuo Kihara committed
457

458 459 460
モデルは、ビジネスのデータ、規則、ロジックを表わす中心的なオブジェクトです。
モデルは、たいてい、さまざまな場所で再利用される必要があります。
良く設計されたアプリケーションでは、通常、モデルは [コントローラ](structure-controllers.md) よりもはるかに太ったものになります。
Nobuo Kihara committed
461 462 463

要約すると、モデルは、

464 465 466
* ビジネスデータを表現する属性を含むことが出来ます。
* データの有効性と整合性を保証する検証規則を含むことが出来ます。
* ビジネスロジックを実装するメソッドを含むことが出来ます。
467
* リクエスト、セッション、または他の環境データに直接アクセスするべきではありません。
468 469
  これらのデータは、[コントローラ](structure-controllers.md) によってモデルに注入されるべきです。
* HTML を埋め込むなどの表示用のコードは避けるべきです -  表示は [ビュー](structure-views.md) で行う方が良いです。
470
* あまりに多くの [シナリオ](#scenarios) を一つのモデルで持つことは避けましょう。
Nobuo Kihara committed
471 472

大規模で複雑なシステムを開発するときには、たいてい、上記の最後にあげた推奨事項を考慮するのが良いでしょう。
473
そういうシステムでは、モデルは数多くの場所で使用され、それに従って、数多くの規則セットやビジネスロジックを含むため、非常に太ったものになり得ます。
Nobuo Kihara committed
474
コードの一ヶ所に触れるだけで数ヶ所の違った場所に影響が及ぶため、ついには、モデルのコードの保守が悪夢になってしまうこともよくあります。
475
モデルのコードの保守性を高めるためには、以下の戦略をとることが出来ます。
Nobuo Kihara committed
476

477
* 異なる [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) によって共有される一連の基底モデルクラスを定義します。
478
  これらのモデルクラスは、すべてで共通に使用される最小限の規則セットとロジックのみを含むべきです。
479
* モデルを使用するそれぞれの [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) において、対応する基底モデルクラスから拡張した具体的なモデルクラスを定義します。
480
  この具体的なモデルクラスが、そのアプリケーションやモジュールに固有の規則やロジックを含むべきです。
Nobuo Kihara committed
481

482 483 484
例えば、[アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) の中で、基底モデルクラス `common\models\Post` を定義することが出来ます。
次に、フロントエンドアプリケーションにおいては、`common\models\Post` から拡張した具体的なモデルクラス `frontend\models\Post` を定義して使います。
また、バックエンドアプリケーションにおいても、同様に、`backend\models\Post` を定義します。
Nobuo Kihara committed
485 486
この戦略を取ると、`frontend\models\Post` の中のコードはフロントエンドアプリケーション固有のものであると保証することが出来ます。
そして、フロントエンドのコードにどのような変更を加えても、バックエンドアプリケーションを壊すかもしれないと心配する必要がなくなります。