0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【30分外部連携シリーズ】 SPIRALを用いたTeamsへの定期配信でできること2選

Last updated at Posted at 2025-10-02

はじめに

皆さんこんにちは!
いきなりですが、登録されたレコード情報をただ配信するだけでなく事前に集計したり、登録状況を示した情報をTeamsなどのチャットへ送信したいと思ったことはありますか?
今回の記事ではSPIRALとTeamsを連携したTeamsへの登録レコードの集計データやレコード登録情報の配信、またDBのバックアップ方法等を紹介します!
記載したPHPを張り付けてご自身の設定値やフィールド名に変換するだけの、とっても簡単な実装となっているので、ぜひお試しください!

連携の仕方について

PHPを用いて、DBに登録された内容をTeamsへ定期配信します。
基本的な連携の仕方はこちらの記事をご覧ください。
【簡単30分】 teamsへの定期配信設定

image.png

今回はSPIRAL®ver2を使用します。
SPIRAL®ver2を使用する際の参考にしていただければ幸いです。
SPIRALについて▷【簡単プログラミング!】SPIRALってなあに?

SPIRAL SPIRAL®ver2について▷ https://spiral.pi-pe.co.jp/

定期配信でできること

集計データの配信

image.png

一つ目にDBに登録された情報を,集計しTeamsへ定期配信します。
例として、下記図のような希望日数と希望日程アンケートを用意し、これらのアンケート結果から,解答された情報の合計希望日数と各日程の合計値を計算しTeamsへ配信することができます。

image.png

ご自身の集計したい項目をDBに作成しフォームなどからレコード登録を行った後,スケジュールトリガを作成しPHP実行に下記PHPを張り付けることで,設定した時間に設定したフィールドの集計情報をTeamsへ送信します!
この際ご自身の設定値やフィールド名に変換する箇所がございますので、適宜変更してお試しください!

<?php
// スパイラル設定値
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", " ご自身のAPIキーを入力してください ");
define("APP_ID", " 使用するアプリのIDを入力してください ");
define("DB_ID", " 使用するDBのIDを入力してください "); 
// 集計したいDBのフィールド
define("definition1", "集計したいフィールド名1");
define("definition2", "集計したいフィールド名2");

// Teams Webhook URL
define("TEAMS_WEBHOOK_URL", " 取得したwebhookのURLを入力してください ");

// スパイラルAPI URL
$spiralApiUrl = API_URL."/apps/".APP_ID."/dbs/".DB_ID."/records";
$records  = SPIRAL_API_send($spiralApiUrl);

$total = 0;
// APIからレコード取得
// 日数合計値計算
if (!empty($records["items"]) && is_array($records["items"])) {
    foreach ($records["items"] as $item) {

        if (isset($item[definition1]) && is_numeric($item[definition1])) {
            $total += $item[definition1];
        
        }
    }
} else {
    echo "レコードが取得できませんでした。\n";
}
$message = "合計値は {$total} です。";

// 選択肢ごとの合計値
$dateCounts = [];

// optionsでID→ラベルの対応を取得
$definition2Options = $records['options']['集計したいフィールド名2'] ?? [];

if (!empty($records['items']) && is_array($records['items'])) {
    foreach ($records['items'] as $item) {
        if (isset($item['集計したいフィールド名2']) && is_array($item['schedule'])) {
            foreach ($item['集計したいフィールド名2'] as $definition2Id) {
                // IDからラベル取得
                $label = $ definition2Options[$definition2Id] ?? '不明な日付';

                // カウントアップ
                if (!isset($dateCounts[$label])) {
                    $dateCounts[$label] = 0;
                }
                $dateCounts[$label]++;
            }
        }
    }
} else {
    echo "レコードが取得できませんでした。\n";
    exit;
}

// メッセージ整形
$messageLines = [];
foreach ($dateCounts as $date => $count) {
    $messageLines[] = "{$date}: {$count}件";
}
$message2 = implode("\n", $messageLines);

// Teamsへ通知
$teamsData = [
    "type" => "message",
    "attachments" => [
        [
            "contentType" => "application/vnd.microsoft.card.adaptive",
            "content" => [
                "type" => "AdaptiveCard",
                "version" => "1.4",
                "body" => [
                    [
                        "type" => "TextBlock",
                        "text" => $message,
                        "wrap" => true
                    ],
                     [
                        "type" => "TextBlock",
                        "text" => $message2,
                        "wrap" => true
                    ]
                ]
            ]
        ]
    ]
];

TEAMS_WEBHOOK_send($teamsData);


function SPIRAL_API_send($spiralApiUrl) {
    $header = [
        "Authorization:Bearer " . API_KEY,
        "Content-Type:application/json",
        "X-Spiral-Api-Version: 1.1",
    ];

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_URL, $spiralApiUrl);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    $response = curl_exec($curl);
    if (curl_errno($curl)) echo curl_error($curl);
    curl_close($curl);

    return json_decode($response, true);
}

function TEAMS_WEBHOOK_send($data) {
     $header = array(
    "Content-Type:application/json",
    );
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_URL, TEAMS_WEBHOOK_URL);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");

    $response = curl_exec($curl);
    if (curl_errno($curl)) echo curl_error($curl);
    curl_close($curl);
    return json_decode($response, true);
}


▽SPIRALのトライアルはこちらから!


新規レコードお知らせ配信

2つめは登録されたレコードのお知らせ配信を行います。
具体的に、登録、更新、削除されたレコードIDをTeamsに配信します。

image.png

この機能については基本的な連携作業に加えて下記2点の事前準備が必要になります。
①バックアップDBの作成
②DBとバックアップDBのレコードID連携

バックアップDBの作成

始めに,お知らせ配信したい項目を設定したDBを作成します。(DB①)

image.png

次に,先ほど作成したDBと同じ項目に加えてdb1_idを追加したDBを作成します。(DB②)
db1_idフィールドはDB①のレコードとDB②のレコードを紐づける際に使用します。

image.png

DBとバックアップDBのレコードID連携

スケジュールアクションに以下のPHPをPHP実行に張り付けることで,実行した時刻にDB①にある内容をDB②に保存することができ,DB②がDB①のバックアップとしての役割を持つことができます。
この際ご自身の設定値やフィールド名に変換する箇所がございますので、適宜変更してお試しください!

<?php
// スパイラル設定値
define("API_URL", https://api.spiral-platform.com/v1);
define("API_KEY", " ご自身のAPIキーを入力してください ");
define("APP_ID", " 使用するアプリのIDを入力してください ");
define("DB1_ID", " 使用するDBのIDを入力してください ");
define("DB2_ID", " DB1のバックアップDBのIDを入力してください");

// Spiral API呼び出し
function apiCall($method, $path, $data = null) {
    $headers = [
        "Authorization:Bearer " . API_KEY,
        "Content-Type:application/json",
    ];
    $url = API_URL . "/apps/" . APP_ID . $path;
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    if ($method === 'POST' || $method === 'PUT') {
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
    }
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
    $resp = curl_exec($curl);
    curl_close($curl);
    return json_decode($resp, true);
}

// DB1からレコード取得
$db1Resp = apiCall('GET', '/dbs/' . DB1_ID . '/records');
$db1Items = $db1Resp['items'] ?? [];

// DB2から全レコード取得
$db2Resp = apiCall('GET', '/dbs/' . DB2_ID . '/records');
$db2Items = $db2Resp['items'] ?? [];

// DB2レコードを db1_id でマップ
$db2Map = [];
foreach ($db2Items as $item) {
    if (!empty($item['db1_id']) && is_scalar($item['db1_id'])) {
        $key = (string)$item['db1_id'];
        $db2Map[$key] = $item;
    }
}

// DB1→DB2への同期処理
foreach ($db1Items as $record) {
    $db1_id = (string)$record['_id'];

    // 登録・更新用データ作成
    $data = [
        "DB2のフィールド名1"     => isset($record['対応するDB1のフィールド名1']) ? (string)$record['対応するDB1のフィールド名1] : "",
        " DB2のフィールド名2"    => isset($record['対応するDB1のフィールド名2']) ? array_map('strval', $record['対応するDB1のフィールド名2']) : [],
        "db1_id"  => $db1_id,
    ];

    if (isset($db2Map[$db1_id])) {
        // すでにDB2に登録されている → 更新
        $db2_id = $db2Map[$db1_id]['_id'];
        echo "Updating DB2 record linked to DB1 ID {$db1_id}\n";
        $resp = apiCall('PUT', '/dbs/' . DB2_ID . '/records/' . $db2_id, $data);
        $action = 'Updated';
    } else {
        // 存在しない → 新規登録
        echo "Adding new DB2 record linked to DB1 ID {$db1_id}\n";
        $resp = apiCall('POST', '/dbs/' . DB2_ID . '/records', $data);
        $action = 'Added';
    }

    if (isset($resp['errors'])) {
        echo "Error: " . json_encode($resp['errors']) . "\n";
    } else {
        echo "{$action} record successfully.\n";
    }
}

// DB1のID一覧を取得(文字列化して比較用)
$db1Ids = array_map(fn($rec) => (string)($rec['_id'] ?? ''), $db1Items);
$db1IdMap = array_flip($db1Ids); 

// DB1に存在しないdb1_idを持つDB2レコードを削除
foreach ($db2Map as $db1_id => $db2Record) {
    if ($db1_id === '' || !isset($db2Record['_id'])) {
        // 無効なdb1_idや_idがないレコードはスキップ
        continue;
    }

    if (!isset($db1IdMap[$db1_id])) {
        $db2_id = $db2Record['_id'];

        // ログ表示
        echo "Deleting DB2 record not found in DB1 (db1_id={$db1_id})\n";

        // 削除実行
        $resp = apiCall('DELETE', '/dbs/' . DB2_ID . '/records/' . $db2_id);

        if (isset($resp['errors'])) {
            echo " Error deleting record: " . json_encode($resp['errors']) . "\n";
        } else {
            echo " Deleted DB2 record with db1_id: {$db1_id}\n";
        }
    }
}

レコード比較によるレコード情報の配信

最後にスケジュールトリガのPHP実行に下記PHPを張り付けることで、バックアップDBとDB①での紐づけたレコード同士の比較を行い、レコードが新規登録・更新・削除されたかを確認し,結果をTeamsへ送信します。
この際ご自身の設定値やフィールド名に変換する箇所がございますので、適宜変更してお試しください!

<?php
// スパイラル設定値
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", " ご自身のAPIキーを入力してください ");
define("APP_ID", " 使用するアプリのIDを入力してください ");
define("DB1_ID", " 使用するDBのIDを入力してください ");
define("DB2_ID", " DB1のバックアップDBのIDを入力してください");



// Teams Webhook URL
define("TEAMS_WEBHOOK_URL", " 取得したwebhookのURLを入力してください ");


$current = SPIRAL_API_send(API_URL."/apps/".APP_ID."/dbs/".DB1_ID."/records");
$backup  = SPIRAL_API_send(API_URL."/apps/".APP_ID."/dbs/".DB2_ID."/records");

$currentMap = [];
foreach ($current['items'] ?? [] as $item) {
    $currentMap[$item['_id']] = $item; 
}
$backupMap = [];
foreach ($backup['items'] ?? [] as $item) {
    $backupMap[$item['db1_id'] ?? $item['_id']] = $item;
}

// 差分チェック
$added   = [];
$deleted = [];
$updated = [];
$unchanged = [];

// 追加 or 更新
foreach ($currentMap as $db1_id => $rec) {
    if (!isset($backupMap[$db1_id])) {
        $added[$db1_id] = $rec;
    } else {
        $backupRec = $backupMap[$db1_id];

        // 比較対象フィールドだけ抽出してマッピング
        $currentFields = [
            'schedule' => $rec['schedule'] ?? null,
            'num_days' => $rec['num_days'] ?? null,
        ];

        $backupFields = [
            'schedule' => $backupRec['sche'] ?? null,    // フィールド名対応
            'num_days' => $backupRec['num'] ?? null,
        ];

        // 比較
        if ($currentFields !== $backupFields) {
            $updated[$db1_id] = $rec;
        }
        else {
            $unchanged[$db1_id] = $rec; 
        }

    }
}

// 削除
foreach ($backupMap as $db1_id => $rec) {
    if (!isset($currentMap[$db1_id])) {
        $deleted[$db1_id] = $rec;
    }
}

// ③ Teams通知
$messages = [];
if ($added)   $messages[] = "🟢 追加: " . implode(', ', array_keys($added));
if ($deleted) $messages[] = "🔴 削除: " . implode(', ', array_keys($deleted));
if ($updated) $messages[] = "🟡 更新: " . implode(', ', array_keys($updated));
if ($unchanged) $messages[] = "✅ 差分無し: " . implode(', ', array_keys($unchanged));

sendToTeams($messages);

// ===== 関数定義 =====

function SPIRAL_API_send($url) {
    $header = [
        "Authorization:Bearer " . API_KEY,
        "Content-Type:application/json",
        "X-Spiral-Api-Version: 1.1",
    ];
    $curl = curl_init($url);
    curl_setopt_array($curl, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => $header,
    ]);
    $resp = curl_exec($curl);
    curl_close($curl);
    return json_decode($resp, true);
}

function sendToTeams($messages) {
    $payload = [
        "type" => "message",
        "attachments" => [[
            "contentType" => "application/vnd.microsoft.card.adaptive",
            "content" => [
                "type" => "AdaptiveCard",
                "version" => "1.4",
                "body" => [[
                    "type" => "TextBlock",
                    "text" => implode("\n\n", $messages),
                    "wrap" => true
                ]]
            ]
        ]]
    ];
    $curl = curl_init(TEAMS_WEBHOOK_URL);
    curl_setopt_array($curl, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ["Content-Type: application/json"],
        CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_UNICODE),
        CURLOPT_CUSTOMREQUEST  => "POST",
    ]);
    curl_exec($curl);
    curl_close($curl);
}

まとめ

今回はSPIRAL®ver2とteamを連携した、レコード情報を集計したり、比較した情報を定期配信するPHPの紹介をしました。
作成するのにはかなり時間がかかり大変でした。。。
皆さんは少し値を変更するだけでできるので、ぜひ試してみてください!


私がインターンしているスパイラル株式会社は、ローコードプラットフォーム、SPIRAL ver.1のトライアルアカウント無償提供しています。このアカウントの記事でも紹介するように、たくさんの機能がございます。

▶︎ フォーム
▶︎ 認証エリア
▶︎ ログイン
▶︎ メール送信
▶︎ カスタムプログラム
などの作成ができますので、ぜひ試してみてください!!

そして、今チームでトライアル登録者向けに、オンボーディングコンテンツを作成しています。SPIRAL ver.1にご興味のある方、ぜひこちらもご覧ください👇

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?