PowerCMS Xのモデルに登録したデータをAlgoliaに登録し、返値のObjectIDsをモデルに保存する

公開

昨日の「PowerCMSのブログに登録したデータをAlgoliaで検索する」続きです。プロジェクトがPowerCMS 5なので頭の中ではPowerCMS 5が前提の話だったのですが、PowerCMS Xだと割と楽にAlgoliaと連携するプラグインが書けるのではないかと思い試してみました。

広島県オープンデータライブラリにある学校のデータがPowerCMS Xのschoolモデルに登録してありますのでそれを利用します。具体的にはschoolモデルの情報をAlgoliaのschoolインデックスに登録し、返値にあるobjectIDをschoolモデルの各オブジェクトに保存します。下記を参照して開発を進めました。

実装結果

まだ汎用的なプラグインではなく、設定画面もないし.envを使っているなどするのですが、1時間30分ぐらいで作成できました。

schoolモデルのデータがAlgoliaのインデックスに100件登録されました。今津小学校のobjectIDは3066891002です。
画面キャプチャ:Algoliaのダッシュボード

PowerCMS Xのオブジェクトのカラムに先程確認したobjectID 3066891002が保存されています。
画面キャプチャ:PowerCMS Xのオブジェクトの編集画面

サンプルコード

モデルにオブジェクトを保存したときに1件ずつAlgoliaに登録する、タスクでまとめてAlgoliaに登録する、などが考えられますが、ひとまずタスクバージョンを作りました。SearchEstraierプラグインが参考になります。今回は使うのを忘れましたが「PluginStarterプラグイン」もあります。

config.json

{
    "label"       : "AlgoliaSupport",
    "id"          : "algoliasupport",
    "component"   : "AlgoliaSupport",
    "version"     : "0.1",
    "author"      : "Hideki Abe",
    "author_link" : "https://www.anothersky.pw/",
    "description" : "",
    "tasks": {
        "alogolia_save_objects": {
            "label"     : "Save objects into Algolia index.",
            "component" : "AlgoliaSupport",
            "priority"  : 100,
            "method"    : "save_multiple_objects",
            "frequency" : 1
        }
    }
}

AlgoliaSupport.php

1件ずつsave()したら負荷がかかるかも?と思ったのですが、update_multi()が便利でした。saveObjects()の返値の説明に「List of objectIDs of the saved objects in order.」とあるのでforで回せば大丈夫なはずです(一応何件かのオブジェクトを無作為に開いて確認しました)。

<?php
require_once LIB_DIR . 'Prototype' . DS . 'class.PTPlugin.php';
require_once 'vendor/autoload.php';

class AlgoliaSupport extends PTPlugin {

    function __construct() {
        parent::__construct();
        $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
        $dotenv->load();
    }

    function save_multiple_objects($app) {
        $result = false;
        $model = 'school';
        $app->get_scheme_from_db($model);
        $db = $app->db;
        $db->update_multi = true;
        $records = [];

        // API Clientの準備
        $client = Algolia\AlgoliaSearch\SearchClient::create(
            $_ENV['APP_ID'],
            $_ENV['ADMIN_API_KEY']
        );
        $index = $client->initIndex($_ENV['INDEX_NAME']);

        // モデルのオブジェクトを取得してインデックス登録
        $args = [
            'limit' => 100,  // NOTE: とりあえず100件
        ];
        $objects = $db->model($model)->load([], $args);
        foreach ($objects as $object) {
            $records[] = [
                'id'      => (int) $object->id,
                'name'    => $object->name,
                'address' => $object->address,
                'tel'     => $object->tel,
            ];
        }
        if (empty($records)) {
            return $result;
        }
        $algolia_result = $index->saveObjects(
            $records,
            [
                'autoGenerateObjectIDIfNotExist' => true,
            ]
        );

        // objectIDをモデルに登録
        if (
            isset($algolia_result[0]) &&
            isset($algolia_result[0]['objectIDs'])
        ) {
            $objectIDs = $algolia_result[0]['objectIDs'];
            $n_records = count($records);
            for ($i = 0; $i < $n_records; $i += 1) {
                $objects[$i]->algolia_object_id = $objectIDs[$i];
            }
            $result = $db->model($model)->update_multi($objects);
        }

        return $result;
    }

}