Files
yii2/docs/guide-ja/concept-behaviors.md
2023-12-23 10:58:40 +03:00

361 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

ビヘイビア
==========
ビヘイビアは [[yii\base\Behavior]] またその子クラスのインスタンスです。
ビヘイビアは [ミックスイン](https://ja.wikipedia.org/wiki/Mixin) としても知られ、既存の [[yii\base\Component|component]] クラスの
機能を、クラスの継承を変更せずに拡張することができます。コンポーネントにビヘイビアをアタッチすると、その
コンポーネントにはビヘイビアのメソッドとプロパティが "注入" され、それらのメソッドとプロパティは、
コンポーネント・クラス自体に定義されているかのようにアクセスできるようになります。また、ビヘイビアは、
コンポーネントによってトリガされた [イベント](concept-events.md) に応答することができるので、ビヘイビアでコンポーネントの通常のコード実行をカスタマイズすることができます。
ビヘイビアを定義する <span id="defining-behaviors"></span>
--------------------
ビヘイビアを定義するには、 [[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()
{
// ...
}
}
```
上のコードは、`prop1``prop2` という2つのプロパティと `foo()` というメソッドを持つ `app\components\MyBehavior` ビヘイビア・クラスを定義します。
`prop2` プロパティは、 `getProp2()` getter メソッドと `setProp2()` setter メソッドで定義されることに着目してください。
[[yii\base\Behavior]] は [[yii\base\BaseObject]] を継承しているので、getter と​​ setter による [プロパティ](concept-properties.md) 定義をサポートします。
このクラスはビヘイビアなので、コンポーネントにアタッチされると、そのコンポーネントは `prop1``prop2` のプロパティと `foo()` メソッドを持つようになります。
> Tip: ビヘイビア内から、[[yii\base\Behavior::owner]] プロパティを介して、ビヘイビアをアタッチしたコンポーネントにアクセスすることができます。
> Note: ビヘイビアの [[yii\base\Behavior::__get()]] および/または [[yii\base\Behavior::__set()]] メソッドをオーバーライドする場合は、
同時に [[yii\base\Behavior::canGetProperty()]] および/または [[yii\base\Behavior::canSetProperty()]] もオーバーライドする必要があります。
コンポーネントのイベントを処理する
----------------------------------
ビヘイビアが、アタッチされたコンポーネントがトリガするイベントに応答する必要がある場合は、
[[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) {
}
```
ビヘイビアをアタッチする <span id="attaching-behaviors"></span>
------------------------
[[yii\base\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::class,
// 名前付きビヘイビア ビヘイビア・クラス名のみ
'myBehavior2' => MyBehavior::class,
// 無名ビヘイビア 構成情報配列
[
'class' => MyBehavior::class,
'prop1' => 'value1',
'prop2' => 'value2',
],
// 名前付きビヘイビア 構成情報配列
'myBehavior4' => [
'class' => MyBehavior::class,
'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::class);
// 構成情報配列をアタッチ
$component->attachBehavior('myBehavior3', [
'class' => MyBehavior::class,
'prop1' => 'value1',
'prop2' => 'value2',
]);
```
[[yii\base\Component::attachBehaviors()]] メソッドを使うと、いちどに複数のビヘイビアをアタッチできます:
```php
$component->attachBehaviors([
'myBehavior1' => new MyBehavior(), // 名前付きビヘイビア
MyBehavior::class, // 無名ビヘイビア
]);
```
次のように、 [構成情報](concept-configurations.md) を通じてビヘイビアをアタッチすることもできます:
```php
[
'as myBehavior2' => MyBehavior::class,
'as myBehavior3' => [
'class' => MyBehavior::class,
'prop1' => 'value1',
'prop2' => 'value2',
],
]
```
詳しくは [構成情報](concept-configurations.md#configuration-format)
のセクションを参照してください。
ビヘイビアを使用する <span id="using-behaviors"></span>
--------------------
ビヘイビアを使用するには、まず上記の方法に従って [[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();
```
ビヘイビアをデタッチする <span id="detaching-behaviors"></span>
------------------------
ビヘイビアをデタッチするには、ビヘイビアに付けられた名前とともに [[yii\base\Component::detachBehavior()]] を呼び出します:
```php
$component->detachBehavior('myBehavior1');
```
*全ての* ビヘイビアをデタッチすることもできます:
```php
$component->detachBehaviors();
```
`TimestampBehavior` を利用する <span id="using-timestamp-behavior"></span>
------------------------------
しめくくりに、[[yii\behaviors\TimestampBehavior]] を見てみましょう。このビヘイビアは、
`insert()``update()` または `save()` のメソッドを通じて [[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::class,
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
// UNIX タイムスタンプではなく datetime を使う場合は
// 'value' => new Expression('NOW()'),
],
];
}
}
```
上のビヘイビア構成は、レコードが:
* 挿入されるとき、ビヘイビアは現在の UNIX タイムスタンプを
`created_at``updated_at` 属性に割り当てます
* 更新されるとき、ビヘイビアは現在の UNIX タイムスタンプを `updated_at` 属性に割り当てます
> Note: 上記の実装が MySQL データベースで動作するようにするためには、`created_at` と `updated_at` のカラムを UNIX タイムスタンプ になるように int(11) として宣言してください。
このコードが所定の位置にあれば、例えば `User` オブジェクトがあって、それを保存しようとしたら、そこで、
`created_at``updated_at` が自動的に現在の UNIX タイムスタンプで埋められます。
```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');
```
その他のビヘイビア
------------------
その他にも、内蔵または外部ライブラリによって利用できるビヘイビアがいくつかあります。
- [[yii\behaviors\BlameableBehavior]] - 指定された属性に現在のユーザ ID を自動的に設定します。
- [[yii\behaviors\SluggableBehavior]] - 指定された属性に、URL のスラグとして使用できる値を
自動的に設定します。
- [[yii\behaviors\AttributeBehavior]] - 特定のイベントが発生したときに、ActiveRecord オブジェクトの一つまたは複数の属性に、
指定された値を自動的に設定します。
- [yii2tech\ar\softdelete\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - ActiveRecord をソフト・デリートおよびソフト・リストアするメソッド、
すなわち、レコードの削除を示すフラグまたはステータスを設定するメソッドを提供します。
- [yii2tech\ar\position\PositionBehavior](https://github.com/yii2tech/ar-position) - レコードの順序を整数のフィールドによって管理することが出来るように、
順序変更メソッドを提供します。
ビヘイビアとトレイトの比較 <span id="comparison-with-traits"></span>
--------------------------
ビヘイビアは、主となるクラスにそのプロパティやメソッドを「注入する」という点で [トレイト](https://www.php.net/manual/ja/language.oop5.traits.php)
に似ていますが、これらは多くの面で異なります。以下に説明するように、それらは互いに長所と短所を持っています。
それらは代替手段というよりも、むしろ相互補完関係のようなものです。
### ビヘイビアを使う理由 <span id="pros-for-behaviors"></span>
ビヘイビアは通常のクラスのように、継承をサポートしています。いっぽうトレイトは、
言語サポートされたコピー&ペーストとみなすことができます。トレイトは継承をサポートしません。
ビヘイビアは、コンポーネント・クラスの変更を必要とせず、コンポーネントに動的にアタッチまたはデタッチすることが可能です。
トレイトを使用するには、トレイトを使うクラスのコードを書き換える必要があります。
ビヘイビアは構成可能ですがトレイトは不可能です。
ビヘイビアは、イベントに応答することで、コンポーネントのコード実行をカスタマイズできます。
同じコンポーネントにアタッチされた異なるビヘイビア間で名前の競合がある場合、その競合は自動的に、
先にコンポーネントにアタッチされたものを優先することで解消されます。
異なるトレイトによって引き起こされる名前競合の場合は、
影響を受けるプロパティやメソッドの名前変更による、手動での解決が必要です。
### トレイトを使う理由 <span id="pros-for-traits"></span>
ビヘイビアは時間もメモリも食うオブジェクトなので、トレイトはビヘイビアよりはるかに効率的です。
トレイトはネイティブな言語構造であるため、IDE との相性に優れています。