concept-behaviors.md 13.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
ビヘイビア
=========

ビヘイビアは [[yii\base\Behavior]] 、あるいはその子クラスのインスタンスです。ビヘイビアは
[ミックスイン](http://en.wikipedia.org/wiki/Mixin) としても知られ、既存の [[yii\base\Component|component]] クラスの
機能を、クラスの継承を変更せずに拡張することができます。コンポーネントにビヘイビアをアタッチすると、その
コンポーネントにはビヘイビアのメソッドとプロパティが "注入" され、それらのメソッドとプロパティは、
コンポーネントクラス自体に定義されているかのようにアクセスできるようになります。また、ビヘイビアは、
コンポーネントによってトリガされた [イベント](concept-events.md) に応答することができるので、
ビヘイビアでコンポーネントの通常のコード実行をカスタマイズすることができます。


13
ビヘイビアの定義 <span id="defining-behaviors"></span>
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
------------------

ビヘイビアを定義するには、 [[yii\base\Behavior]] あるいは子クラスを継承するクラスを作成します。たとえば:

```php
namespace app\components;

use yii\base\Behavior;

class MyBehavior extends Behavior
{
    public $prop1;

    private $_prop2;

    public function getProp2()
    {
        return $this->_prop2;
    }

    public function setProp2($value)
    {
        $this->_prop2 = $value;
    }

    public function foo()
    {
        // ...
    }
}
```

上のコードは、 `app\components\MyBehavior` という、2つのプロパティ -- `prop1``prop2` -- と
`foo()` メソッドを持つビヘイビアクラスを定義します。`prop2` プロパティは、 `getProp2()` getter メソッドと `setProp2()` setter メソッドで定義されることに着目してください。
[[yii\base\Behavior]] は [[yii\base\Object]] を継承しているので、getter と​​ setter による [プロパティ](concept-properties.md) 定義をサポートします。

このクラスはビヘイビアなので、コンポーネントにアタッチされると、そのコンポーネントは `prop1``prop2` プロパティ、それと `foo()` メソッドを持つようになります。

> Tip: ビヘイビア内から、[[yii\base\Behavior::owner]] プロパティを介して、ビヘイビアをアタッチしたコンポーネントにアクセスすることができます。

コンポーネントイベントのハンドリング
------------------

ビヘイビアが、アタッチされたコンポーネントがトリガするイベントに応答する必要がある場合は、
[[yii\base\Behavior::events()]] メソッドをオーバーライドしましょう。たとえば:

```php
namespace app\components;

use yii\db\ActiveRecord;
use yii\base\Behavior;

class MyBehavior extends Behavior
{
    // ...

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
        ];
    }

    public function beforeValidate($event)
    {
        // ...
    }
}
```

[[yii\base\Behavior::events()]] メソッドは、イベントとそれに対応するハンドラのリストを返します。
上の例では [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] イベントがあること、
そのハンドラ定義である `beforeValidate()` を宣言しています。イベントハンドラを指定するときは、以下の表記方法が使えます:

* ビヘイビアクラスのメソッド名を参照する文字列 (上の例など)
* オブジェクトまたはクラス名と文字列のメソッド名 (括弧なし) 例 `[$object, 'methodName']`
* 無名関数

イベントハンドラのシグネチャは次のようにしてください。`$event` はイベントのパラメータを参照します。イベントの詳細については
[イベント](concept-events.md) セクションを参照してください。

```php
function ($event) {
}
```

100
ビヘイビアのアタッチ <span id="attaching-behaviors"></span>
101 102 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
-------------------

[[yii\base\Component|component]] へのビヘイビアのアタッチは、静的にも動的にも可能です。実際は、前者のほうがより一般的ですが。

ビヘイビアを静的にアタッチするには、ビヘイビアをアタッチしたいコンポーネントクラスの [[yii\base\Component::behaviors()|behaviors()]] メソッドをオーバーライドします。
[[yii\base\Component::behaviors()|behaviors()]] メソッドは、ビヘイビアの [構成](concept-configurations.md) のリストを返さなければなりません。
各ビヘイビアの構成内容は、ビヘイビアのクラス名でも、構成情報配列でもかまいません。

```php
namespace app\models;

use yii\db\ActiveRecord;
use app\components\MyBehavior;

class User extends ActiveRecord
{
    public function behaviors()
    {
        return [
            // 無名ビヘイビア ビヘイビアクラス名のみ
            MyBehavior::className(),

            // 名前付きビヘイビア ビヘイビアクラス名のみ
            'myBehavior2' => MyBehavior::className(),

            // 無名ビヘイビア 構成情報配列
            [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ],

            // 名前付きビヘイビア 構成情報配列
            'myBehavior4' => [
                'class' => MyBehavior::className(),
                'prop1' => 'value1',
                'prop2' => 'value2',
            ]
        ];
    }
}
```

ビヘイビア構成に対応する配列のキーを指定することによって、ビヘイビアに名前を関連付けることができます。この場合、ビヘイビアは *名前付きビヘイビア* と呼ばれます。上の例では、2つの名前付きビヘイビア​​
`myBehavior2``myBehavior4` があります。ビヘイビアが名前と関連付けられていない場合は、 *無名ビヘイビア* と呼ばれます。


ビヘイビアを動的にアタッチするには、ビヘイビアをアタッチしようとしているコンポーネントの [[yii\base\Component::attachBehavior()]] メソッドを呼びます:

```php
use app\components\MyBehavior;

// ビヘイビアオブジェクトをアタッチ
$component->attachBehavior('myBehavior1', new MyBehavior);

// ビヘイビアクラスをアタッチ
$component->attachBehavior('myBehavior2', MyBehavior::className());

// 構成情報配列をアタッチ
$component->attachBehavior('myBehavior3', [
    'class' => MyBehavior::className(),
    'prop1' => 'value1',
    'prop2' => 'value2',
]);
```
[[yii\base\Component::attachBehaviors()]] メソッドを使うと、いちどに複数のビヘイビアをアタッチできます:

```php
$component->attachBehaviors([
    'myBehavior1' => new MyBehavior,  // 名前付きビヘイビア
    MyBehavior::className(),          // 無名ビヘイビア
]);
```

次のように、 [構成情報](concept-configurations.md) を通じてビヘイビアをアタッチすることもできます:

```php
[
    'as myBehavior2' => MyBehavior::className(),

    'as myBehavior3' => [
        'class' => MyBehavior::className(),
        'prop1' => 'value1',
        'prop2' => 'value2',
    ],
]
```

詳しくは [構成情報](concept-configurations.md#configuration-format) セクションを参照してください。

191
ビヘイビアの使用 <span id="using-behaviors"></span>
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
---------------

ビヘイビアを使用するには、まず上記の方法に従って [[yii\base\Component|コンポーネント]] にアタッチします。ビヘイビアがコンポーネントにアタッチされれば、その使用方法はシンプルです。

あなたは、アタッチされているコンポーネントを介して、ビヘイビアの *パブリック* メンバ変数、または getter や setter によって定義されたプロパティにアクセスすることができます:

```php
// "prop1" はビヘイビアクラス内で定義されたプロパティ
echo $component->prop1;
$component->prop1 = $value;
```

また同様に、ビヘイビアの *パブリック* メソッドも呼ぶことができます:

```php
// foo() はビヘイビアクラス内で定義されたパブリックメソッド
$component->foo();
```

ご覧のように、 `$component``prop1``foo()` を定義していないにもかかわらず、
アタッチされたビヘイビアによって、それらをコンポーネント定義の一部であるかのように使うことができるのです。

もし2つのビヘイビアが同じプロパティやメソッドを定義し、かつ両方とも同じコンポーネントにアタッチされている場合は、
プロパティやメソッドのアクセス時に、*最初に* コンポーネントにアタッチされたビヘイビアが優先されます。

ビヘイビアはコンポーネントにアタッチされるとき、名前と関連付けられているかもしれません。その場合、
その名前を使用してビヘイビアオブジェクトにアクセスすることができます:

```php
$behavior = $component->getBehavior('myBehavior');
```

また、コンポーネントにアタッチされた全てのビヘイビアを取得することもできます:

```php
$behaviors = $component->getBehaviors();
```


231
ビヘイビアのデタッチ <span id="detaching-behaviors"></span>
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
-------------------

ビヘイビアをデタッチするには、ビヘイビアに付けられた名前とともに [[yii\base\Component::detachBehavior()]] を呼び出します:

```php
$component->detachBehavior('myBehavior1');
```

*全ての* ビヘイビアをデタッチすることもできます:

```php
$component->detachBehaviors();
```


247
`TimestampBehavior` の利用 <span id="using-timestamp-behavior"></span>
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
-------------------------

しめくくりに、[[yii\behaviors\TimestampBehavior]] を見てみましょう。このビヘイビアは、
保存時 (つまり挿入や更新) に、[[yii\db\ActiveRecord|アクティブレコード]] モデルの
タイムスタンプ属性の自動更新をサポートします。

まず、使用しようと考えている [[yii\db\ActiveRecord|アクティブレコード]] クラスに、このビヘイビアをアタッチします:

```php
namespace app\models\User;

use yii\db\ActiveRecord;
use yii\behaviors\TimestampBehavior;

class User extends ActiveRecord
{
    // ...

    public function behaviors()
    {
        return [
            [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
                ],
            ],
        ];
    }
}
```

上のビヘイビア構成は、レコードが:

* 挿入されるとき、ビヘイビアは現在のタイムスタンプを `created_at``updated_at` 属性に割り当てます
* 更新されるとき、ビヘイビアは現在のタイムスタンプを `updated_at` 属性に割り当てます

所定の位置にそのコードを使用すると、もし `User` オブジェクトを設け、それを保存しようとしたら、そこで、
`created_at``updated_at` が自動的に現在のタイムスタンプで埋められます。

```php
$user = new User;
$user->email = 'test@example.com';
$user->save();
echo $user->created_at;  // 現在のタイムスタンプが表示される
```

[[yii\behaviors\TimestampBehavior|TimestampBehavior]] は、指定された属性に現在のタイムスタンプを割り当てて
それをデータベースに保存する、便利なメソッド [[yii\behaviors\TimestampBehavior::touch()|touch()]] を提供します。

```php
$user->touch('login_time');
```

303
ビヘイビアとトレイトの比較 <span id="comparison-with-traits"></span>
304 305 306 307 308 309 310
----------------------

ビヘイビアは、主となるクラスにそのプロパティやメソッドを「注入する」という点で [トレイト](http://www.php.net/traits)
に似ていますが、これらは多くの面で異なります。以下に説明するように、それらは互いに長所と短所を持っています。
それらは代替手段というよりも、むしろ相互補完関係のようなものです。


311
### ビヘイビアを使う理由 <span id="pros-for-behaviors"></span>
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326

ビヘイビアは通常のクラスのように、継承をサポートしています。いっぽうトレイトは、
言語サポートされたコピー&ペーストとみなすことができます。トレイトは継承をサポートしません。

ビヘイビアは、コンポーネントクラスの変更を必要とせずに、動的なコンポーネントへのアタッチとデタッチが可能です。トレイトを使用するには、クラスをトレイトを使って書き換える必要があります。

ビヘイビアは構成可能ですがトレイトは不可能です。

ビヘイビアは、イベントに応答することで、コンポーネントのコード実行をカスタマイズできます。

同じコンポーネントにアタッチされた異なるビヘイビア間で名前の競合がある場合、その競合は自動的に、
先にコンポーネントにアタッチされたものを優先することで解消されます。
別のトレイトが起こした名前競合の場合、影響を受けるプロパティやメソッドの名前変更による、手動での解決が必要です。


327
### トレイトを使う理由 <span id="pros-for-traits"></span>
328 329 330 331 332

ビヘイビアは時間もメモリも食うオブジェクトなので、トレイトはビヘイビアよりはるかに効率的です。

トレイトは言語構造であるため、IDE との相性に優れています。