CMSのポテンシャルを引き出す─MODxで作る商用サイト

第9回MODxとMySQLの関係 + おまけスニペット

はじめに

連載開始からしばらく経ちました。今では多くの皆さんが快適なMODxライフをお過ごしかと思います(笑⁠⁠。一方で、既存サイトのコンテンツが多い、複雑といった事情から、なかなかMODxやCMSの導入に踏みきれない方もいらっしゃるのではないでしょうか?

今回は、バックエンドデータベースから見たMODxについて解説していきます。データベース側の構造を知ることで、既存サイトのコンテンツをひとつひとつコピー&ペーストし、管理画面から登録するのではなく、データベース側からサイトを半自動的にマイグレーションする、という手段を検討することができ、より効率的な運用管理を目指すことができるようになります。

前回紹介したように、MODx-2.1からはMS SQL Serverがサポートされるようになりましたが、実績の面からはMySQLを使う方が間違いなく無難です。今回もMySQLを前提とした解説を行っていきます。よろしくお付き合いください。

MySQLのテーブル構造

さっそくですが、本連載の1回目でMySQLを設定したように、MODxが持つテーブル情報を確認するには次のようにします。

MODxが使用しているテーブルの確認
# mysql -u root modx
mysql> show tables;
+------------------------------------+ 
| Tables_in_modx                     | 
+------------------------------------+ 
| modx_access_actiondom              | 
| modx_access_actions                | 
| modx_access_category               | 
| modx_access_context                | 
| modx_access_elements               | 
| modx_access_menus                  | 
| modx_access_permissions            | 
| modx_access_policies               | 
| modx_access_policy_template_groups | 
| modx_access_policy_templates       | 
| modx_access_resource_groups        | 
| modx_access_resources              | 
| modx_access_templatevars           | 
| modx_actiondom                     | 
| modx_actions                       | 
| modx_actions_fields                | 
| modx_active_users                  | 
| modx_categories                    | 
| modx_categories_closure            | 
| modx_class_map                     | 
| modx_content_type                  | 
| modx_context                       | 
| modx_context_resource              | 
| modx_context_setting               | 
| modx_document_groups               | 
| modx_documentgroup_names           | 
| modx_element_property_sets         | 
| modx_event_log                     | 
| modx_fc_profiles                   | 
| modx_fc_profiles_usergroups        | 
| modx_fc_sets                       | 
| modx_keyword_xref                  | 
| modx_lexicon_entries               | 
| modx_lexicon_topics                | 
| modx_manager_log                   | 
| modx_member_groups                 | 
| modx_membergroup_names             | 
| modx_menus                         | 
| modx_namespaces                    | 
| modx_property_set                  | 
| modx_register_messages             | 
| modx_register_queues               | 
| modx_register_topics               | 
| modx_session                       | 
| modx_site_content                  | 
| modx_site_content_metatags         | 
| modx_site_htmlsnippets             | 
| modx_site_keywords                 | 
| modx_site_metatags                 | 
| modx_site_plugin_events            | 
| modx_site_plugins                  | 
| modx_site_snippets                 | 
| modx_site_templates                | 
| modx_site_tmplvar_access           | 
| modx_site_tmplvar_contentvalues    | 
| modx_site_tmplvar_templates        | 
| modx_site_tmplvars                 | 
| modx_system_eventnames             | 
| modx_system_settings               | 
| modx_transport_packages            | 
| modx_transport_providers           | 
| modx_user_attributes               | 
| modx_user_group_roles              | 
| modx_user_messages                 | 
| modx_user_roles                    | 
| modx_user_settings                 | 
| modx_users                         | 
| modx_workspaces                    | 
+------------------------------------+ 
68 rows in set (0.00 sec) 

このように、MODx Revolutionは普段目に見えないところでたくさんのテーブルを使ってデータを管理、表示していることが推測できます。

テーブル名からも大体の用途が想像できるかもしれませんが、これらのテーブル名と用途を一覧にしてみましたので参考にしてみてください。中には語彙やコンテキストなど、読者の皆さんには意味不明な項目があるかもしれませんが、例のごとく次回以降ゆっくりと解説を行っていく予定です。言い訳になりますが、一部ドキュメントが整っていない&時間の都合で完全に内容を網羅することができませんでした。ご了承ください。

表1 テーブル名と用途
テーブル名内容
modx_access_actiondom詳細不明
modx_access_actionsアクションのアクセス設定
modx_access_categoryカテゴリのアクセス設定
modx_access_contextコンテキストのアクセス設定
modx_access_elementsエレメントのアクセス設定
modx_access_menusメニューのアクセス設定
modx_access_permissionsアクセスパーミッションの設定
modx_access_policiesアクセスポリシーの設定(権限管理→ユーザーグループとロールの管理→アクセスポリシー)
modx_access_policy_template_groupsアクセステンプレートのテンプレートグループ設定
modx_access_policy_templatesアクセスポリシーテンプレートの設定(権限管理→ユーザーグループとロールの管理→Policy Templates)
modx_access_resource_groupsリソースグループのアクセス設定
modx_access_resourcesリソースのアクセス設定
modx_access_templatevarsカスタムテンプレート変数のアクセス設定
modx_actiondom詳細不明
modx_actionsアクションの内容(システム→アクション)
modx_actions_fieldsアクションフィールドの内容
modx_active_users現在ログイン中のユーザ情報
modx_categoriesスニペットなどのカテゴリ情報
modx_categories_closure上記カテゴリの親子関係など
modx_class_mapドキュメントやリンク、スニペットなどのドキュメントクラス
modx_content_typeコンテンツタイプ(システム→コンテンツタイプ)
modx_contextコンテキスト(バーチャルドメインのようなもの)の内容
modx_context_resourceコンテキストのリソース
modx_context_settingコンテキストの設定
modx_document_groupsリソースグループとリソースの関係
modx_documentgroup_namesリソースグループの情報
modx_element_property_setsエレメントのプロパティセット
modx_event_logイベントログ
modx_fc_profiles管理画面カスタマイズのためのプロファイル
modx_fc_profiles_usergroups上記のユーザグループ設定
modx_fc_sets管理画面カスタマイズのためのセット
modx_keyword_xrefキーワードのクロスリファレンス?
modx_lexicon_entries語彙(gettextのようなもの)のキーと内容の対応
modx_lexicon_topics語彙のトピック情報
modx_manager_log管理者ログ
modx_member_groupsメンバーグループの情報
modx_membergroup_namesメンバーグループの名前や説明
modx_menusトップメニューの構成(システム→アクション)
modx_namespacesプラグインやスニペットのネームスペース
modx_property_set設定をまとめたプロパティセット(ツール→プロパティセット)
modx_register_messages登録時のメッセージ
modx_register_queues登録キュー
modx_register_topics詳細不明
modx_sessionMODxのログインセッション
modx_site_content各リソースの内容
modx_site_content_metatagsメタタグの管理。最近では使用されない。
modx_site_htmlsnippetsチャンクの内容
modx_site_keywordsキーワードの管理。最近では使用されない。
modx_site_metatagsメタタグの管理。最近では使用されない。
modx_site_plugin_eventsプラグインのイベント
modx_site_pluginsTinyMCEのようなプラグイン
modx_site_snippets独自スニペットやWayFinderのようなスニペット
modx_site_templatesテンプレート
modx_site_tmplvar_accessカスタムテンプレート変数のアクセス許可
modx_site_tmplvar_contentvaluesカスタムテンプレート変数の実データ
modx_site_tmplvar_templatesカスタムテンプレート変数とテンプレートの対応
modx_site_tmplvarsカスタムテンプレート変数の定義
modx_system_eventnamesプラグインなどが使用するシステムイベント名
modx_system_settingsシステム設定
modx_transport_packagesパッケージ情報
modx_transport_providersパッケージのプロバイダ情報
modx_user_attributesメールアドレスや氏名などのユーザ情報
modx_user_group_rolesグループロール
modx_user_messagesメッセージ
modx_user_rolesユーザロール
modx_user_settingsユーザ設定(権限管理→ユーザ管理→ユーザ→設定の内容)
modx_usersユーザ名、パスワードなどの認証情報
modx_workspacesMODxコアが使用するワークスペース

MODxの管理画面を操作する中で最も日常的に発生するのは、新規リソースの作成や編集です。リソースの作成前後でMySQLの構造を比較してみるとわかりますが、新規リソースを作成した場合にデータベース上で更新されるテーブルは「modx_site_content」です。

試しに、⁠テンプレート⁠⁠、⁠ページタイトル⁠⁠、⁠エイリアス⁠⁠、⁠ページコンテンツ」の4つを入力したリソースを作成し、mysqlコマンドで情報を取得すると次のような結果が得られます。

作成済みリソースの確認
mysql> select * from modx_site_content where pagetitle='testtitle'\G 
*************************** 1. row *************************** 
             id: 13 
           type: document 
    contentType: text/html 
      pagetitle: testtitle 
      longtitle: 
    description: 
          alias: testalias 
link_attributes: 
      published: 0 
       pub_date: 0 
     unpub_date: 0 
         parent: 0 
       isfolder: 0 
      introtext: 
        content: testcontent 
       richtext: 1 
       template: 3 
      menuindex: 3 
     searchable: 1 
      cacheable: 1 
      createdby: 1 
      createdon: 1308665190 
       editedby: 0 
       editedon: 0 
        deleted: 0 
      deletedon: 0 
      deletedby: 0 
    publishedon: 0 
    publishedby: 0 
      menutitle: 
        donthit: 0 
     privateweb: 0 
     privatemgr: 0 
  content_dispo: 0 
       hidemenu: 0 
      class_key: modDocument 
    context_key: web 
   content_type: 1 
            uri: testalias.html 
   uri_override: 0 
1 row in set (0.00 sec) 

逆にテーブルの構造を見てみると、次のようになっています。

modx_site_contentテーブルの構造
mysql> show create table modx_site_content\G 
*************************** 1. row *************************** 
       Table: modx_site_content 
Create Table: CREATE TABLE `modx_site_content` ( 
  `id` int(10) unsigned NOT NULL auto_increment, 
  `type` varchar(20) NOT NULL default 'document', 
  `contentType` varchar(50) NOT NULL default 'text/html', 
  `pagetitle` varchar(255) NOT NULL default '', 
  `longtitle` varchar(255) NOT NULL default '', 
  `description` varchar(255) NOT NULL default '', 
  `alias` varchar(255) default '', 
  `link_attributes` varchar(255) NOT NULL default '', 
  `published` tinyint(1) unsigned NOT NULL default '0', 
  `pub_date` int(20) NOT NULL default '0', 
  `unpub_date` int(20) NOT NULL default '0', 
  `parent` int(10) NOT NULL default '0', 
  `isfolder` tinyint(1) unsigned NOT NULL default '0', 
  `introtext` text, 
  `content` mediumtext, 
  `richtext` tinyint(1) unsigned NOT NULL default '1', 
  `template` int(10) NOT NULL default '0', 
  `menuindex` int(10) NOT NULL default '0', 
  `searchable` tinyint(1) unsigned NOT NULL default '1', 
  `cacheable` tinyint(1) unsigned NOT NULL default '1', 
  `createdby` int(10) NOT NULL default '0', 
  `createdon` int(20) NOT NULL default '0', 
  `editedby` int(10) NOT NULL default '0', 
  `editedon` int(20) NOT NULL default '0', 
  `deleted` tinyint(1) unsigned NOT NULL default '0', 
  `deletedon` int(20) NOT NULL default '0', 
  `deletedby` int(10) NOT NULL default '0', 
  `publishedon` int(20) NOT NULL default '0', 
  `publishedby` int(10) NOT NULL default '0', 
  `menutitle` varchar(255) NOT NULL default '', 
  `donthit` tinyint(1) unsigned NOT NULL default '0', 
  `privateweb` tinyint(1) unsigned NOT NULL default '0', 
  `privatemgr` tinyint(1) unsigned NOT NULL default '0', 
  `content_dispo` tinyint(1) NOT NULL default '0', 
  `hidemenu` tinyint(1) NOT NULL default '0', 
  `class_key` varchar(100) NOT NULL default 'modDocument', 
  `context_key` varchar(100) NOT NULL default 'web', 
  `content_type` int(11) unsigned NOT NULL default '1', 
  `uri` text, 
  `uri_override` tinyint(1) NOT NULL default '0', 
  PRIMARY KEY  (`id`), 
  KEY `alias` (`alias`), 
  KEY `published` (`published`), 
  KEY `pub_date` (`pub_date`), 
  KEY `unpub_date` (`unpub_date`), 
  KEY `parent` (`parent`), 
  KEY `isfolder` (`isfolder`), 
  KEY `template` (`template`), 
  KEY `menuindex` (`menuindex`), 
  KEY `searchable` (`searchable`), 
  KEY `cacheable` (`cacheable`), 
  KEY `hidemenu` (`hidemenu`), 
  KEY `class_key` (`class_key`), 
  KEY `context_key` (`context_key`), 
  KEY `uri` (`uri`(333)), 
  KEY `uri_override` (`uri_override`), 
  FULLTEXT KEY `content_ft_idx` (`pagetitle`,`longtitle`,`description`,`introtext`,`content`) 
) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=utf8

このように、ほとんどの項目にはデフォルト値が入るようになっているため、新規リソースをMySQL側から作成するのも容易です。

MySQL側からリソースを作成
mysql> INSERT INTO modx_site_content (pagetitle, alias, content, published) values ('testtitle2', 'testalias2', 'testcontent2', 1); 
Query OK, 1 row affected (0.00 sec) 

ここまでくればご想像のとおりですが、テンプレートや親リソースを設定することもできます。

テンプレートと親リソースを指定してリソースを作成
mysql> INSERT INTO modx_site_content (pagetitle, alias, content, published, template, parent) values ('testtitle2', 'testalias2', 'testcontent2', 1, 3, 5); 
Query OK, 1 row affected (0.00 sec) 

これを応用していけば、スクリプトを使って静的なHTMLサイトをMODx化させることも不可能ではありません。データを挿入した後で、最終的には画像や各リンクなど手動で修正すべき点はあるでしょうが、コピー&ペーストで移行を行うよりははるかに手軽です。

カスタムテンプレート変数の構造

通常のリソース同様、比較的頻繁に作成、更新するであろう項目がカスタムテンプレート変数です。なかみつ園では各商品ページにテンプレート変数を埋め込んでいるのですが、値段や内容量などの確認、いわゆる「棚卸」のために、わざわざマウスで管理画面を何度も遷移するのは骨が折れます。

こちらもデータベース構造を把握しておけば、mysqlコマンドやphpMyAdminといったツールから比較的手軽に内容を確認、修正することができます。

例のごとく、カスタムテンプレート変数は各リソースに割り当て可能な変数ですが、データベースの構造は先ほどの「modx_site_content」とは少し異なります。以前の記事で、値段を格納するための「price」というカスタムテンプレート変数を定義し、⁠mytpl」というテンプレートから参照できるような設定を行いました。これをデータベース的に追いかけてみましょう。

カスタムテンプレート変数priceの情報を表示
mysql> select * from modx_site_tmplvars where name = 'price'\G 
*************************** 1. row *************************** 
               id: 1 
             type: text 
             name: price 
          caption: price 
      description: 
      editor_type: 0 
         category: 0 
           locked: 0 
         elements: 
             rank: 0 
          display: default 
     default_text: 100? 
       properties: a:0:{} 
 input_properties: NULL 
output_properties: NULL 
1 row in set (0.00 sec) 

次に、カスタムテンプレート変数とテンプレートの関係について調べておきます。まず、⁠mytpl」というテンプレートのIDは次のようにして取得できます。MySQLに詳しい方はサブクエリを使って、一気に情報を取得してみても良いでしょう。

テンプレートのIDを確認
mysql> select id,templatename from modx_site_templates; 
+----+--------------+ 
| id | templatename | 
+----+--------------+ 
|  1 | BaseTemplate | 
|  3 | mytpl        | 
+----+--------------+ 
2 rows in set (0.00 sec) 

「mytpl」のIDは3であることがわかりました。次にmodx_site_tmplvar_templatesテーブルを表示してみます。

mysql> select * from modx_site_tmplvar_templates; 
+-----------+------------+------+ 
| tmplvarid | templateid | rank | 
+-----------+------------+------+ 
|         1 |          3 |    0 | 
+-----------+------------+------+ 
1 row in set (0.00 sec) 

tmplvaridはテンプレート変数ID、templateidはテンプレートIDを表しますので、この結果は

  • 「テンプレート変数IDが1であるpriceテンプレートはテンプレートIDが3であるmytplテンプレート内で使用可能」

を意味します。最後に、肝心のカスタムテンプレート変数の中身ですが、⁠modx_site_tmplvar_contentvalues」テーブルに格納されています。

カスタムテンプレート変数の内容を表示
mysql> select * from modx_site_tmplvar_contentvalues; 
+----+-----------+-----------+--------+ 
| id | tmplvarid | contentid | value  | 
+----+-----------+-----------+--------+ 
|  1 |         1 |        10 | 1,050円 | 
|  2 |         1 |        11 | 1,575円 | 
+----+-----------+-----------+--------+ 
2 rows in set (0.00 sec) 

contentidとは各リソースのIDを表すため、リソースIDが10と11であるリソースの中で、tmplvarid=1、つまりpriceというカスタムテンプレートが使用されていることになります。

リソースやカスタムテンプレート変数以外にも、スニペットやテンプレートなど、多くのものがMySQL上に格納されます。お使いの用途に応じて、データベースをうまく操作するようにしてください。

おまけ

もう1つおまけとして、今回は簡単なスニペットの作り方について紹介したいと思います。前回紹介したように、MODx-2.1.0からはこれまで使用できていた多くの関数が削られてしまい、筆者が個人的によく使用していた

$doc = $modx->getDocument(..);

といった構文もサポートされなくなってしまいました。Summary of Legacy Code Removed in 2.1PHP Coding in MODx Revolution, Pt. lでも紹介されているとおり、MODx-2.1.0以降では

$doc = $modx->getObject(..);

という形でオブジェクトの情報を取得します。実際の例として、gihyo.jpの各ページには

  • 「この記事を読むのに必要な時間:およそ ○分」

といった表示がありますが、これはご想像のとおりスニペットが表示している値です。読むための時間は基本的にページの分量に比例しますので、⁠1,000文字読むのに約1分(1文字あたり1/1000分)要する」と仮定した場合、スニペットの内容は次のようになります。

getEstimate
<?php
// 自身のリソースIDを取得
$resourceId = $modx->resource->get('id');

// リソースを配列で取得。$docをprint_rしてみると何が入っているのかよくわかります。
$doc = $modx->getObject('modResource', $resourceId);

// タグを除去
$stripped = strip_tags($doc->get('content'));

// 結果を整数で表示。0分と表示されないよう、1をプラス(手抜き!!)
return (int)(mb_strlen($stripped) / 1000) + 1;

getObjectというメソッドは多くのスニペットから使用することが多いので、覚えておいて損はありません。

最後に

今回はデータベース側から見たMODxということで、文字ばかりの記事になってしまいましたが、バックエンドを知ることで、MODxマスターに一歩近づくことができます。

もし価格や内容量を含む商品データベースが別サーバに存在する場合、crontab+スクリプトから定期的にマスターの情報をMODx側に適用する、という使い方などは実用的でおもしろそうです。

データベースを操作しすぎて管理画面との整合性がとれなくなっては元も子もありませんが、状況に応じてデータを簡単かつ正確に操作できるよう、お使いの環境にあった最適な運用方法をぜひ考えてみてください。

おすすめ記事

記事・ニュース一覧