経緯
PHP製の Cockpit CMS を本格的に使いたいと思ったのですが、あまりドキュメントがなかったので手元で検証した内容をメモしておきます。
前提
LAMPサーバを構築して、以下のようにします。
- ドキュメントルート:
/PATH/TO/COCKPITCMS/DOCROOT/
- 権限付与:
chown apache:ftpuser -R /PATH/TO/COCKPITCMS/DOCROOT/cms/
- URL:
https://www.example.jp/cms/
インストール・設定
Start journey の free 版でダウンロード、展開したファイル一式を FTPS や SFTP でアップロード、先の権限付与をして https://www.example.jp/cms/install
にアクセスします。
これでインストール完了画面が表示されます。一度この画面を踏まないとログインできないのでうっかり忘れないように注意。
その後、晴れて https://www.example.jp/cms/
にアクセス。先の初期ユーザID・パスワードである admin/admin
でログインします。
ダッシュボードで「Hello.」と表示されればログイン成功です。
コンテンツモデルの作成
初期状態だとコンテンツデータのフォーマットすらない状態なので、設定します。
左のペインの「Content」をクリックし、 collection を選択。今回はデモのため適当に hello
というコンテンツモデルを作成し、データフィールドを定義していきます。
- モデル:
hello
- フィールド:
- 国,
country
, テキスト (以下、表示名、フィールド名、データタイプの順) - 国 (英語表記),
country_en
, テキスト - 現存,
existent
, セレクト- オプション:
現存
,崩壊
- オプション:
- 地域,
area
, タグ(複数指定可)
- 国,
APIの設定
ロールの設定
次に読み書き用の API の設定をしたいところですが、そのためにはAPIに対してロールを割り当てる必要があるので、ロールを先に作成します。
左下の「Setting」から「ROLE & PERMISSIONS」をクリック。
右下の「ADD ROLE」ボタンをクリック。
まずは API を使ってデータを投入(POST)する方のロールを設定します。
一番下のコンテンツモデル hello
に対しては Read, Create, Update, Delete, Publish の全ての権限を付与します。
次に読み取り専用のロールも作成します。
こちらは Read のみ。
ユーザ作成
次にこのロールを持つユーザを作成します。ユーザ一覧画面で「ADD USER」ボタンをクリック。
hello
へ書き込み可能なユーザと読み取り専用のユーザを作成します。各ユーザの作成画面でAPIの生成と控えるのを忘れずに (後で生成もできますが)。
API設定
ロールとユーザを作成し終えたら、満を持して API 設定に進みます。左のペインから「API」をクリック。
「ACCESS」下の「Public API」に対して右の「CONFIGURE」ボタンをクリック。分かりづらいですが、「Role」下の部分をクリックするとセレクトボックスが展開されるので、先程のコンテンツ生成用のロールを割り当てます (末尾に記載しましたが、これを運用すると危険なのであくまで今回の試験のための一時的なもの。後でこの設定は削除することを忘れないように)。
なお右のスライダーをクリックすると既存のROLEを確認することができます(先程既に設定を実施したので今回は編集しない)。
これで以下のようなコードを準備。
データ投入用のプログラム
<?php
date_default_timezone_set('Asia/Tokyo');
mb_language('ja');
mb_internal_encoding('UTF-8');
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
$config = [
'rmtSrv' => [
'BaseURL' => 'https://www.example.jp/cms/api/content/item/',
'APIkey' => 'USR-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
],
'content' => [
'model' => 'hello',
],
];
$data = [
[
'country' => '大英帝国',
'country_en' => 'British Empire',
'existent' => '崩壊',
'area' => [
'ヨーロッパ',
],
],
[
'country' => 'ローマ帝国',
'country_en' => 'Roman Empire',
'existent' => '崩壊',
'area' => [
'ヨーロッパ',
'アフリカ',
'アラブ',
],
],
[
'country' => 'ソビエト連邦',
'country_en' => 'Soviet',
'existent' => '崩壊',
'area' => [
'アジア',
'シベリア',
],
],
];
$succeededArray = [];
$failedArray = [];
foreach( $data as $key => $val ) {
$options = [
'headers' => [
'Content-Type' => 'application/json; charset=UTF-8',
'api-key' => $config['rmtSrv']['APIkey'],
],
'json' => [
'data' => $val,
],
];
$client = new Client();
$res = $client->request('POST', $config['rmtSrv']['BaseURL'] . $config['content']['model'], $options);
if( $res->getStatusCode() !== 200 ) {
$failedArray[] = [
'country' => $val['country'],
'reason' => 'Failed to post',
];
}
else {
$succeededArray[] = $val['country'];
}
}
$results = [
'succeeded' => $succeededArray,
'failed' => $failedArray,
];
var_dump($results);
Composer で guzzle をインストールした状態で、 php index.php
のように PHP エンジン直に実行するように指示します。ちなみに、日本語のデータを投入するに当たっての URIエンコード 等は必要なさそうでした(guzzle側でやってくれているのかも)。
$ php post.php
post.php:xx:
array(2) {
'succeeded' =>
array(3) {
[0] =>
string(12) "大英帝国"
[1] =>
string(15) "ローマ帝国"
[2] =>
string(18) "ソビエト連邦"
}
'failed' =>
array(0) {
}
}
実行結果、成功です。
手動でアメリカを追加していたのでその分多いですが、確かにデータを投入できたことが確認できました。
データ取得
さて、続いては API を叩いてデータを取得する部分。ひとまず以下のコードで取得できることは確認しました。
<?php
date_default_timezone_set('Asia/Tokyo');
mb_language('ja');
mb_internal_encoding('UTF-8');
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
$config = [
'rmtSrv' => [
'BaseURL' => 'https://www.example.jp/cms/api/content/item/',
'APIkey' => 'USR-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
],
'content' => [
'model' => 'hello',
],
];
$options = [
'headers' => [
'Content-Type' => 'application/json; charset=UTF-8',
'api-key' => $config['rmtSrv']['APIkey'],
],
// 'debug' => true,
];
$client = new Client();
$res = $client->request('GET', $config['rmtSrv']['BaseURL'] . $config['content']['model'], $options);
//var_dump($res);
//var_dump($res->getStatusCode());
var_dump(json_encode(
json_decode($res->getBody(), true),
JSON_HEX_TAG | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
));
実行結果は以下。
$ php get.php
get.php:xx:
string(1493) "[
{
"country": "アメリカ合衆国",
"country_en": "America",
"existent": "現存",
"area": [
"アメリカ"
],
"_state": 1,
"_modified": uuuuuuuuuu,
"_mby": "xxxxxxxxxxxxxxxxxxxxxxxx",
"_created": uuuuuuuuuu,
"_cby": null,
"_id": "iiiiiiiiiiiiiiiiiiiiiiii"
},
{
"country": "大英帝国",
"country_en": "British Empire",
"existent": "崩壊",
"area": [
"ヨーロッパ"
],
"_state": 1,
"_modified": uuuuuuuuuu,
"_mby": null,
"_created": uuuuuuuuuu,
"_cby": null,
"_id": "iiiiiiiiiiiiiiiiiiiiiiii"
},
{
"country": "ローマ帝国",
"country_en": "Roman Empire",
"existent": "崩壊",
"area": [
"ヨーロッパ",
"アフリカ",
"アラブ"
],
"_state": 1,
"_modified": uuuuuuuuuu,
"_mby": null,
"_created": uuuuuuuuuu,
"_cby": null,
"_id": "iiiiiiiiiiiiiiiiiiiiiiii"
},
{
"country": "ソビエト連邦",
"country_en": "Soviet",
"existent": "崩壊",
"area": [
"アジア",
"シベリア"
],
"_state": 1,
"_modified": uuuuuuuuuu,
"_mby": null,
"_created": uuuuuuuuuu,
"_cby": null,
"_id": "iiiiiiiiiiiiiiiiiiiiiiii"
}
]"
取得できました。
条件を指定してのデータの取得
さて、全件取得はできましたが、条件を指定するとしたらどうでしょうか。これについては PLAYGROUND で色々と検証してみました。
まず PLAYGROUND の上部に API を入力する場所があるので、ここで先程作成した API キーを入力して認証を済ませます。その上で検証。特に一覧取得なので GET /content/items/{model}
の部分を弄っていきます。
filter
いくつか条件が選べるのですが、 filter
は JSON 形式の指定とのことなので試しに {"area":"ヨーロッパ"}
とします。
これで絞り込みができました。
なお、プログラムから API を叩く時の URL は以下のようになります。
https://www.example.jp/cms/api/content/items/hello?filter={"area":"ヨーロッパ"}
では、 {"area":"ヨーロッ.*"}
や {"area":"ヨーロッ*"}
にするとどうでしょうか。ワイルドカードや正規表現が使えるか、というところですね。
長さ0の配列、条件にマッチしないのでワイルドカードや正規表現はダメそうですね。
それでは、 {"area":"ヨーロッ"}
という部分一致を狙ってみます。
が、これもダメでした。うーん、 filter の絞り込みは完全一致でないとダメそうです。
fields
次にフィールドです。これも結果から言うと若干謎な挙動を示しました。 {"country":"some","country_en":"some"}
とすると確かに country
, country_en
のみ(id
は含まれますが)のデータを取得できました。なお、 some
の部分は1文字以上の任意の文字列で良さそうです。
先程と同様にプログラムから API を叩く時の URL は以下。
https://www.example.jp/cms/api/content/items/hello?filter=?fields={"country":"some","country_en":"some"}
ところが {"country":"some","country_en":""}
として0文字の指定にしたり、 ["country","country_en"]
とキーだけの配列にするとその分データが省かれていきました。どうやらキーと任意の1文字以上の文字列の指定をしないと、意図したフィールドが出力されないようです。
limit, skip
続いて limit や skip 。これは単純に数値を指定すればその分スキップされたり出力上限値を指定できたりします。
件数
……さて、ここで気になったのは全部で何件あるか、という件数の取得。ひとまず、 limit, skip の2つを指定すると meta
, total
に件数が表示されます。ただし、これだと他のデータも諸々付いてきてしまうので fields に ["id"]
を指定して最低限の出力に抑えればデータ量は減らせるのかな、というところです。
ただ、素直に件数が取れなさそうなのはしっくりこない。
ちなみに、 ["id"]
のみだと件数は表示されません。
また、 limit, skip のどちらかのみを指定する場合も、件数は表示されないようです。 limit, skip の両方を指定する必要があるようです。
なので、 fields を ["id"]
, limit: 1
, skip: 0
とするのが最も出力データ数を抑えられるのかな、というのが現時点での所感です。
https://www.example.jp/cms/api/content/items/hello?filter=?fields={"id":"some"}&limit=1&skip=0
補足1: Public API
ちなみに、先程 Public API を設定しましたが、どうもここには1つしか設定できないようです。……書き込み用と読み取り用の2つを用意したのに……。
とはいえ、元々 Public API は認証なしで外部アクセスできるようにするための API の設定らしいので、書き込み権限のある API をここに設定しておくのは良くなさそうです(公式ドキュメントより)。
ただ、そうすると認証ありで API を叩く方法は……公式ドキュメントの Authentication の部分を読んでも見当たらないので現時点では謎、というか課題です。
理想としては、書き込み用の API は認証ありで、読み取り専用のものを Public API に指定するのが望ましいのかな、と思っています(ただしそれが正しいかは確証なし)。
補足2: 言語設定
いつの間にか以前とはやり方が変わっていました。言語のファイル構成も変わっているので、過去の記事は流用できません。
やり方としては、 /config/i18n/
以下に言語ファイルPHP一式を収めたディレクトリをアップロードするらしいです。……が、上述の公式リポジトリには de
、オランダ語しか存在しないのでとりあえず英語で使う漢字でしょうか。
参考
言語設定
CORS
Config Options の末尾に CORS 指定の記述がありますが、今回の検証環境ではなくても動きました。 PHP エンジンに直に実行を指定していたから可能性が高そうですが。
取得 (一覧)
API で特定のモデルからデータのリストを取得するときの参考。