Web EGGではじめてのDrupalネタです。
Drupalを触っててふと気になったのが、 指定したユーザが管理者ロールか否か を判定しようとしたものの、
調べてもベストプラクティスが見つからず、ソースコードを追ったらやっと正解を見つけたという話の備忘録です。
Drupal7を対象にした記事です。
Drupal自体のことはじめ等の記事ではありません。Drupalの事前知識があるものとします。
<?php
// $uidは何かしらのノードから取ってくる
$account = user_load($uid);
if (array_key_exists(variable_get('user_admin_role'), $account->roles)) {
// 管理者
} else {
// 管理者じゃない
}
という感じにvariable_get('user_admin_role')
を利用すればOKでした。
INSERTの順序的に3だろうと決め打ちしたり、administratorという名前でSELECTしてくるのは間違い と認識しています。
結論に至るまでに調べた事項は
という感じでした。
modules/user/user.installにテーブル定義が書いてありました。
ロール一覧テーブルにはロールのID, 名前, 表示順序
カラムしかないので、
「このロールはAdmin相当か否か」を判断する材料がありません
ということはデフォルトで挿入されているロール一覧のうち「これがAdmin相当のロールだ」と見なす必要がありそうです。
ならそのロールのIDに対応する定数があるのでは。と思ったのですが、ない。 なぜかAdminのロールだけありません。
それ以外の初期状態で入ってるロールはincludes/bootstrap.incに定数が定義されています。
DRUPAL_ANONYMOUS_RID
とDRUPAL_AUTHENTICATED_RID
はあるのに、なぜAdminのロールだけない。
動的に変わるから定数としてハードコードできないとか、なんかありそう。怪しい気がする。
roleテーブルの初期値を探してコードを追ってみると
// Built-in roles.
$rid_anonymous = db_insert('role')
->fields(array('name' => 'anonymous user', 'weight' => 0))
->execute();
$rid_authenticated = db_insert('role')
->fields(array('name' => 'authenticated user', 'weight' => 1))
->execute();
という処理をmodules/user/user.installに見つけました。
どうやらAdminロールはBuilt-in roles
に該当しないようです。
Drupalのロール一覧テーブルには管理者フラグ的なものもないし、Drupalが提供しているロールIDに相当する定数もないならどないせえっちゅうねん。という感じなんですが、どうやらAdminのロールIDは3で固定らしい(?)という情報が出てきました。
unless you install Drupal in your custom installation profile and modify the administrator role there, the rid or administrator will be always 3.
— http://drupal.stackexchange.com/a/44735
本当…? それ本当なら定数が提供されてるものじゃない…?
user_has_role関数というものがあるらしい。
これじゃん! と思ったんですが、引数にロールIDが必要でした。
つまりAdmin相当のロールIDを知っていない限りこれを利用できません。
あと無駄にSELECT走るのでN+1が余裕で起きそう。
One liner would be:
$rid = array_search('administrator', user_roles());
— http://drupal.stackexchange.com/a/50437
IDが駄目なら名前で探せって、結局マジックナンバー解消してないじゃん…
user_rolesの戻り値はDBからSELECTしてきた[ロールID => ロール名]
の連想配列なので、ロール管理画面からロール名を変えれば余裕でバグります。
ロールの名前は変えるな って運用で縛れば無理な話ではないんですが、
画面から正常系の機能として編集できてしまう以上、ロール名を決め打ちするのは汎用性を失うし、なによりキモい。
なかば諦めムードでdefine('DRUPAL_ADMINISTRATOR_RID', 3);
とか書き始めていたのですが、
どうしても納得行かなくてソースを眺めていたら正解を見つけました。
// Create a default role for site administrators, with all available permissions assigned.
$admin_role = new stdClass();
$admin_role->name = 'administrator';
$admin_role->weight = 2;
user_role_save($admin_role);
user_role_grant_permissions($admin_role->rid, array_keys(module_invoke_all('permission')));
// Set this as the administrator role.
variable_set('user_admin_role', $admin_role->rid);
profiles/standard/standard.installに書かれていました。
Built-in rolesとは別枠で、Drupalの初期化を行うときにAdminのロールは作成されるようです。
user_role_save
でロールをINSERTしたあと、セットされたridを使用してvariable_set
していました。
variable_setは内部的にDBを使用しているので、永続化される値の1つなようです。
variable_set
されているならvariable_get
で値を取得できるので、
variable_get('user_admin_role')
でAdmin相当のロールIDを入手できました。
user_role_save
を呼ぶよりも前でridに相当する値をハードコートしてないことからわかるように、 AdminロールのIDは3とは限りません
コードを読んでおいて良かった…
野良の情報を調べていて思ったのは、やっぱPHPだわ。という感想でした。
PHPの野良情報はかなりの確実で外れだったり、不確実だったり、バッドノウハウをドヤ顔で語ってたりする(ブーメラン)という感触があり、Drupalも例に漏れず”PHPの情報”にあふれているなぁ、と感じました。
特にDrupalはWordpressと同様にライトな人でもシステム作れちゃうYO的なやつなので、
誤った理解・浅い理解の情報が溢れやすい傾向にあるんじゃないかと思っています。
Drupalについて調べるときは、公式ドキュメントとソースコード以外信じないことにしました。
もしかしたら、公式ドキュメントすら信じられずにソースコードしか信じないときがくるかもしれません。笑