「PowerCMS X Advent Calendar 2021」の5日目は株式会社LABの安倍がお送りします。株式会社LABはアルファサード株式会社の関連会社で、私はPowerCMS Xの開発にも関わっています。最近はJamstackの研究をしており、「PowerCMS X R&D Website」の運営をしています。
さて「PowerCMS Xでリレーションを操るPHPの関数3選」ですが、プラグイン開発に関する話題です。2021年も色々とプラグインを実装しましたが、やはりリレーションに関する操作をすることは多いように感じます。今日はよく使うなというメソッド、これはちょっと面白いと感じたメソッドをご紹介します。
リレーション元IDの取得
よく例に挙げられるセミナーモデルで話を進めます。セミナーモデルには例えば「講師」「会場」「セミナーカテゴリ」など、複数のリレーションを設定することが多いのではないでしょうか? それらをフロントで一覧表示する時、検索フォームを付けて絞り込み検索を実装したいというご要望がよくあります。この時、セミナーモデルのpre_listing
コールバックで検索を実装するのですが、検索フォームで指定された講師を選択しているセミナーのID、指定された会場を選択しているセミナーのIDを抽出する時に利用します。
講師や会場を選択するチェックボックス(またはラジオボタン)のvalueは講師モデル・会場モデルのIDにしておきます。講師名や会場名にすると面倒です。
例えば、get_relation_from_ids( $app, 'seminar', 'lecturer', [1,3] )
のようにすると、講師モデルのIDが1
または3
を指定しているセミナーのIDが返ってきます。取得したIDを使用する時、対象モデルにrev_type
カラムがある時(モデルの編集画面で「リビジョン対応」にチェックを入れている時)は、rev_type
が0
のものを対象にする必要があります。
ちなみに逆パターン…リレーション先IDを取得するメソッドもあります。例えば、セミナーオブジェクトで選択している講師のIDを取得するものです。
リレーションの差分抽出
これはセミナーモデルを更新した時、どの講師が新規に選択されたか、どの講師が選択から外されたか、を知りたいということです。リレーション先のオブジェクトを操作する必要があり実装しました。
通常のテキストカラム等であれば、第3引数のオブジェクトと第4引数のオブジェクトを比較することで変更の有無を把握できます。
if ( $obj->title === $org_obj->title ) {
// 変更なし
} else {
// 変更あり
}
しかし、リレーション型のカラムは「疑似カラム」でテーブルにカラムは作成されないので、$obj->lecturer
のようにして値を取得することができません。そこで独自に研究した結果、$obj->_relations
を利用することになりました。
例えば、diff_relation( $app, $obj, $org_obj, 'lecturers', 'lecturer' );
とすると、講師(lecturers
カラム)でリレーション設定している(していた)講師モデルのIDを返します。鈴木さん(ID: 5)を選択から外す、伊藤さん(ID: 6)はそのまま、野田さん(ID: 7)を新たに選択した時は以下のように結果が返ります。(開発環境のためXdebugが有効です。)
object(stdClass)[266]
public 'add' =>
array (size=1)
1 => string '7' (length=1)
public 'keep' =>
array (size=1)
0 => string '6' (length=1)
public 'delete' =>
array (size=1)
1 => string '5' (length=1)
リレーションの値を収集する
セミナーをキーワード検索する時に講師名や会場名でも検索にヒットするようにしたいというご要望がありました。ただ、リレーションでデータを選択している講師や会場を検索するのはなんだか大変そうです。やり方は色々ありそうですが、検索用テキストカラムを作成しpost_save
で予め講師名や会場名を集めておこうか、ということになりました。その際、リレーションに関する情報を書いてオブジェクトと共に渡すとリレーション先の指定カラムの値をひたすら集めて配列で返すメソッドを書きました。
リレーションに関する情報は以下のような形式で記述します。セミナーオブジェクトで選択している講師オブジェクトで選択している講師の資格名、のように2つのリレーションを辿るところまでテスト済です。
$target_relations = [
'リレーション先モデル名' => '値を取得したいリレーション先モデルのカラム名',
'リレーション先モデル名' => [
'値を取得したいリレーション先モデルのカラム名',
'値を取得したいリレーション先モデルのカラム名',
'値を取得したいリレーション先モデルのカラム名' => [
'リレーション先モデル名' => '値を取得したいリレーション先モデルのカラム名',
],
],
'リレーション先モデル名' => [
'リレーション先モデル名' => '値を取得したいリレーション先モデルのカラム名',
],
];
if ( $seminar->_relations ) {
$relations = $seminar->_relations;
} else {
$relations = $app->get_relations( $seminar );
}
$search_text = get_relation_values( $app, $relations, $target_relations );
このメソッドを使うのかどうかは議論の余地がありそうですが、頭の体操になりましたしなんだか面白く感じました。
まとめにかえて…DevelopUtilityクラス
今回紹介したメソッドは他の案件でも利用する可能性がありそうです。上記の他にも管理画面をカスタマイズする際に環境変数init_tags
をtrue
にすることなくリレーションを読みこむタグの実装や、何度か使用したことがあるのが晦日の算出メソッドなどの開発実績があります。こういったメソッドをDevelopUtilityクラスとしてまとめ、必要に応じてrequire
して使うと良いのかな?と考えています。