サンプルソリューションのデータは、新しいサンプルデータを作る を参照してください。
この記事で使用するソースコードは、PHP 化の叩き台にする HTML ソースコード 以下にある次のソースコードです。
- edit-record.html
- bootstrap-common.css
- fmda-form.css
- fmda-form-check.js
以上を、前回記事で、変更しており、以下の構成になっています。
.
├── css
│ ├── bootstrap-common.css
│ └── fmda-form.css
├── html
│ └── edit-record.html
├── index.php
├── js
│ └── fmda-form-check.js
├── json
│ └── html-contents.json
└── php
├── bs-frame-class.php
├── php-functions.php
└── preferences.php
ページの識別
現在のプランでは、共通の骨組みを使って、FileMaker Data API の作成・編集・削除・複製のリクエストを実行するページを作るという方針になっています。
しかし、ほとんど同じなのに、作成・編集・削除・複製で4つもページを作るのは、なんだか面倒な気がします。
それよりもシングルページで4つの機能を持っている方が、現在は、PHP ですが、後々、JavaScript で非同期スタイルに改めたときを考えると良さそうです。
しかし、4つの機能は、前後の処理や、FileMaker Data API のリクエストが異なりますので、なんらかの方法で判別させなければいけません。
大昔の CGI プログラムなら、GET で、?page=1
のような URL で解決したと思います。
同じような考え方で、GET ではなく、POST でやる想定をしておきましょう。
最初のアクセス、またはフッタ部分のリンクのクリックで、機能を選べるようにページの識別子を仕込んでおくことにします。
ページの識別子 | 機能 | FileMaker Data API リクエスト |
---|---|---|
0 | 新規作成 | records - Create Record |
1 | 編集 | records - Edit Record |
2 | 削除 | records - Delete Record |
3 | 複製 | records - Duplicate Record |
以上のように規定し、デフォルトを 1 とします。
これをname="page-identity"
で POST で送信し、$_POST['page-identity']
を調べます。
これがエラーになった場合は、デフォルトの 1 を選択したと解することにして、index.php
の冒頭に、この処理を preferences.php
を読み込む前に追加します。
index.php 冒頭部分
<?php
require_once(__DIR__ . '/php/bs-frame-class.php');
require_once(__DIR__ . '/php/php-functions.php');
@$page_indentity = $_POST['page-identity'];
$page_indentity = $page_indentity === null ? 1 : $page_indentity;
require_once(__DIR__ . '/php/preferences.php');
これで、識別子を取得できれば、ページの機能の特定はできそうです。
残りの HTML ソースコードの PHP 化
残りは、固有のコンテンツ部分になります。
手法としては、前回と同様、
- preferences.php に設定項目を持たせて
- その実際の内容は、html-contents.json に書き、
将来の FileMaker Pro からの更新に対応させる - HTML の出力部分は、クラスのスタティックメソッドとする
このような方針で、前述のページ識別を含め、一気に以下の条件で PHP 化してしまいます。
- FileMaker Data API のリクエストのコードはまだ書かない
- FileMaker ソリューションから得るべきデータは仮
- フッタ部分のリンクは、ページ識別に対応させる
- それ用に、JavaScript のコードを追加する
- コンテンツ固有の HTML ソースコードは、別のクラスファイルにしておく
index.php
<?php
require_once(__DIR__ . '/php/bs-frame-class.php');
require_once(__DIR__ . '/php/bs-builder-class.php');
require_once(__DIR__ . '/php/php-functions.php');
require_once(__DIR__ . '/php/filemaker-data-api-class.php');
@$page_indentity = $_POST['page-identity'];
$page_indentity = $page_indentity === null ? 1 : $page_indentity;
require_once(__DIR__ . '/php/preferences.php');
BsFrame::HtmlBegin($color_modes_js, $description, $author, $title, $bootstrap_css, $bs_css_integrity, $bs_frame_css, $custom_css);
BsFrame::LoadColorModesSvg();
BsFrame::ColorModes();
BsBuilder::MainBegin($logo_svg, $logo_svg_width, $page_heading[$page_indentity], $page_lead[$page_indentity]);
BsBuilder::InfoItem($info_heading, $info_number_of_items, $info_item_heading, $info_item_content);
BsBuilder::GetInfo();
BsBuilder::CreateForm($form_preferences, $form_controls);
BsBuilder::CreatePagination($page_indentity);
BsBuilder::MainEnd();
BsBuilder::CreateFooter($copyright_year, $copyright_holder, $link_items);
BsFrame::HtmlEnd($bootstrap_js, $bs_js_integrity, $custom_js);
bootstrap-common.css
fmda-form.css
bs-builder-class.php
<?php
class BsBuilder {
static function MainBegin (
string $logo_svg,
string $logo_svg_width,
string $heading,
string $lead
): void
{
echo <<<_HTML_
<div class="container">
<main>
<div class="py-5 text-center">
<img class="d-block mx-auto mb-4" src="{$logo_svg}" alt="" width="{$logo_svg_width}">
<h2>{$heading}</h2>
<p class="lead">{$lead}</p>
</div> <!-- / .py-5 .text-center -->
<div class="row g-5">
_HTML_;
}
static function InfoItem (
string $info_heading,
int $info_number_of_items,
array &$info_item_heading,
array &$info_item_content
): void
{
echo <<<_HTML_
<div class="col-md-5 col-lg-4 order-md-last">
<h4 class="d-flex justify-content-between align-items-center mb-3">
<span class="text-primary">{$info_heading}</span>
<span class="badge bg-primary rounded-pill">{$info_number_of_items}</span>
</h4>
<ul class="list-group mb-3">
_HTML_; // begin
for ($i = 0 ; $i < count($info_item_heading) ; $i++) {
echo <<<_HTML_
<li class="list-group-item d-flex justify-content-between lh-sm">
<div>
<h6 class="my-0">{$info_item_heading[$i]}</h6>
_HTML_;
if (is_numeric($info_item_content[$i])) {
echo <<<_HTML_
</div>
<span class="text-success">$info_item_content[$i]</span>
</li>
_HTML_;
} else {
echo <<<_HTML_
<small class="text-body-secondary">standings</small>
</div>
</li>
_HTML_;
} // if (is_numeric($info_item_content[$i]))
} // for ($i = 0 ; $i < count($info_item_heading) ; $i++)
echo "</ul>\n"; // end
}
static function GetInfo ( ): void {
echo <<<_HTML_
<form action="{$_SERVER['PHP_SELF']}" method="POST" class="card p-2">
<div class="input-group">
<input type="text" class="form-control" name="recordId" placeholder="recordId">
<input type="hidden" class="visually-hidden" name="getInfo" value="true">
<button type="submit" class="btn btn-secondary">取得する</button>
</div>
</form>
</div>
_HTML_;
}
static function CreateForm (
array &$form_prefernces,
array &$form_controls
): void {
echo <<<_HTML_
<div class="col-md-7 col-lg-8">
<h4 class="mb-3">{$form_prefernces[0]}</h4>
<form action="{$form_prefernces[1]}" method="POST" class="needs-validation" novalidate>
<div class="row g-3">
_HTML_; // begin
foreach ($form_controls as $key => $value) {
$required = '';
$secondary_label = '';
if ($value['required'] === '1')
$required = 'required';
else
$secondary_label = '<span class="text-body-secondary">(任意)</span>';
if ($value['type'] === 'select') {
echo <<<_HTML_
<div class="col-sm-6">
<label for="{$value['id']}" class="form-label">{$value['label']}{$secondary_label}</label>
<select class="form-select" id="{$value['id']}" name="{$value['name']}" {$required} {$value['disabled']}>
<option value="">選択...</option>
_HTML_;
foreach ($value['option'] as $option_key => $option_value) {
echo "<option>{$option_value['value']}</option>\n";
}
echo "</select>\n";
} else {
echo <<<_HTML_
<div class="col-sm-6">
<label for="{$value['id']}" class="form-label">{$value['label']}{$secondary_label}</label>
<input type="{$value['type']}" class="form-control" id="{$value['id']}" name="{$value['name']}" placeholder="{$value['placeholder']}" {$required} {$value['disabled']}>
_HTML_;
} // if ($value['type'] === 'select')
if (!empty($required)) {
echo <<<_HTML_
<div class="invalid-feedback">
{$value['feedback']}
</div>
_HTML_;
} // if (!empty($required))
echo "</div>\n";
} // foreach ($form_controls as $key => $value)
echo <<<_HTML_
</div> <!-- / .row -->
<hr class="my-4">
<button class="w-100 btn {$form_prefernces[2]} btn-lg" type="submit">{$form_prefernces[3]}</button>
</form> <!-- / .needs-validation -->
_HTML_;
}
static function CreatePagination (
int $page_identity
): void
{
if ($page_identity !== 0) {
echo <<<_HTML_
<hr class="my-4">
<nav class="container text-center my-4" aria-label="Search results pages">
<div class="row justify-content-md-center">
<div class="col-auto text-center">
<ul class="pagination">
<li class="page-item disabled">
<a class="page-link">前</a>
</li>
<li class="page-item active" aria-current="page">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">4</a></li>
<li class="page-item"><a class="page-link" href="#">5</a></li>
<li class="page-item"><a class="page-link" href="#">6</a></li>
<li class="page-item"><a class="page-link" href="#">7</a></li>
<li class="page-item"><a class="page-link" href="#">8</a></li>
<li class="page-item"><a class="page-link" href="#">9</a></li>
<li class="page-item"><a class="page-link" href="#">10</a></li>
<li class="page-item">
<a class="page-link" href="#">次</a>
</li>
</ul>
</div>
</div> <!-- / .row -->
</nav>
_HTML_;
} // if ($page_identity !== 0)
}
static function CreateFooter (
string $copyright_year,
string $copyright_holder,
array &$link_items
): void
{
echo <<<_HTML_
<footer class="my-5 pt-5 text-body-secondary text-center text-small">
<p class="mb-1">Copyright © {$copyright_year} {$copyright_holder}. All rights reserved.</p>
<ul class="list-inline">
_HTML_;
foreach ($link_items as $key => $value) {
echo <<<_HTML_
<li class="list-inline-item">
<form id="{$value['formId']}" method="POST">
<input type="hidden" name="page-identity" value="{$value['pageIdentity']}">
</form>
<a href="#" id="{$value['anchorId']}">{$value['textContent']}</a>
</li>
_HTML_;
}
echo <<<_HTML_
</ul>
</footer>
</div> <!-- / .container -->
_HTML_;
}
}
bs-frame-class.php
filemaker-data-api-class.php
<?php
class FileMakerDataApi
{
public $host, $version, $database, $username, $password, $layout;
public $bearer_session_token;
function __construct (string $host, string $version)
{
$this->host = $host;
$this->version = $version;
}
/**
* リクエスト
**/
// ログイン
public function login (string $database, string $username, string $password): string
{
$this->database = $database;
$this->username = $username;
$this->password = $password;
$auth_value = $username . ':' . $password;
$authorization = 'Authorization: Basic ' . base64_encode($auth_value);
$content_type = 'Content-Type: application/json';
$curlopt_httpheader = array($authorization, $content_type);
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/databases/{$database}/sessions";
return self::executeCurl($endpoint, 'POST', $curlopt_httpheader);
}
// ログアウト
public function logOut(string $database, string $bearer_session_token): string
{
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/databases/{$database}/sessions/{$bearer_session_token}";
return self::executeCurl($endpoint, 'DELETE');
}
// データベースセッションの検証
public function validateSession (string $session_token): string
{
$authorization = 'Authorization: Bearer ' . $session_token;
$curlopt_httpheader = array($authorization);
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/validateSession";
return self::executeCurl($endpoint, 'GET', $curlopt_httpheader);
}
// レコード範囲の取得
public function getRecords(
string $database,
string $layout,
string $bearer_session_token,
array &$script_name_array,
array &$script_param_array,
int $_offset = 1,
int $_limit = 100,
string $_sort = ''
): string
{
$this->layout = $layout;
$authorization = 'Authorization: Bearer ' . $bearer_session_token;
$curlopt_httpheader = array($authorization);
// ソートの設定
$_sort_string = !empty($_sort) ? '&_sort=' . urlencode($_sort) : '';
$script_prerequest = '';
$script_prerequest_param = '';
$script_presort = '';
$script_presort_param = '';
$script = '';
$script_param = '';
// リクエスト処理前のスクリプト
if ($script_name_array[0] !== '') {
$script_prerequest = '&script.prerequest=' . $script_name_array[0];
$script_prerequest_param = '&script.prerequest.param=' . urlencode(json_encode($script_param_array[0]));
}
// ソート前のスクリプト
if ($script_name_array[1] !== '') {
$script_presort = '&script.presort=' . $script_name_array[1];
$script_presort_param = '&script.presort.param=' . urlencode(json_encode($script_param_array[1]));
}
// リクエスト処理後のスクリプト
if ($script_name_array[2] !== '') {
$script = '&script=' . $script_name_array[2];
$script_param = '&script.param=' . urlencode(json_encode($script_param_array[2]));
}
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/databases/{$database}/layouts/{$layout}/records?_offset={$_offset}&_limit={$_limit}{$_sort_string}{$script_prerequest}{$script_prerequest_param}{$script_presort}{$script_presort_param}{$script}{$script_param}";
return self::executeCurl($endpoint, 'GET', $curlopt_httpheader);
}
// 検索の実行
public function findRecords(
string $database,
string $layout,
string $bearer_session_token,
string $request_body
): string
{
$authorization = 'Authorization: Bearer ' . $bearer_session_token;
$content_type = 'Content-Type: application/json';
$curlopt_httpheader = array($authorization, $content_type);
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/databases/{$database}/layouts/{$layout}/_find";
return self::executeCurl($endpoint, 'POST', $curlopt_httpheader, $request_body);
}
// スクリプトの実行
public function executeScript(
string $database,
string $layout,
string $bearer_session_token,
string $script_name,
string $script_parameters = ''
): string
{
$authorization = 'Authorization: Bearer ' . $bearer_session_token;
$curlopt_httpheader = array($authorization);
$script_parameters = !empty($script_parameters) ? '?script.param=' . json_encode(json_decode($script_parameters)) : '';
$endpoint = "https://{$this->host}/fmi/data/{$this->version}/databases/{$database}/layouts/{$layout}/script/{$script_name}{$script_parameters}";
return self::executeCurl($endpoint, 'GET', $curlopt_httpheader);
}
/**
* ユーティリティ
**/
// レスポンスコードの取得
public function getMessagesCode (string $response): string
{
$response_array = self::json2array($response);
return $response_array['messages'][0]['code'];
}
// Bearer Session Token 取得
public function getBearerSessionToken (string $response): string
{
$response_array = self::json2array($response);
$this->bearer_session_token = $response_array['response']['token'];
return $this->bearer_session_token;
}
// JSON を 配列へ変換
public function json2array (string $json): array
{
$json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
return json_decode($json, true);
}
// cURL 実行
static function executeCurl(string $curlopt_url, string $curlopt_customrequest, array $curlopt_httpheader = [], string $current_postfields = ''): string
{
$current_postfields = !empty($current_postfields) ? json_encode(json_decode($current_postfields)) : '{}';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $curlopt_url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $curlopt_customrequest,
CURLOPT_POSTFIELDS => $current_postfields,
CURLOPT_HTTPHEADER => $curlopt_httpheader,
));
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
}
php-functions.php
preferences.php
<?php
$html_contents_json_file = __DIR__ . '/../json/html-contents.json';
$temp_array = json_get_element($html_contents_json_file);
// for HtmlBegin(), LoadColorModesSvg(), ColorModes()
$color_modes_js = $temp_array['contents']['colorModesJs'];
$description = $temp_array['contents']['description'];
$author = $temp_array['contents']['author'];
$title = $temp_array['contents']['title'];
$bootstrap_css = $temp_array['contents']['bootstrapCss'];
$bs_css_integrity = $temp_array['contents']['bsCssIntegrity'];
$bs_frame_css = $temp_array['contents']['bsFrameCss'];
$custom_css = [];
foreach ($temp_array['contents']['customCss'] as $key => $value) {
array_push($custom_css, $value['location']);
}
// for MainBegin()
$logo_svg = $temp_array['contents']['logoSvg'];
$logo_svg_width = $temp_array['contents']['logoSvgWidth'];
$page_heading = [];
$page_lead = [];
foreach ($temp_array['pageContents'] as $key => $value) {
array_push($page_heading, $value['heading']);
array_push($page_lead, $value['lead']);
}
$form_heading = [];
$form_action = [];
$form_button_color = [];
$form_button_text = [];
foreach ($temp_array['formContents'] as $key => $value) {
array_push($form_heading, $value['heading']);
if ($value['action'] === 'PHP_SELF')
$value['action'] = "\$_SERVER['PHP_SELF']";
array_push($form_action, $value['action']);
array_push($form_button_color, $value['buttonColor']);
array_push($form_button_text, $value['buttonText']);
}
// for InfoItem(), GetInfo()
$info_heading = $temp_array['contents']['infoHeading'];
$info_number_of_items = $temp_array['contents']['infoNumberOfItems'];
$info_item_heading = [];
$info_item_content = [];
foreach ($temp_array['infoItems'] as $key => $value) {
array_push($info_item_heading, $value['heading']);
array_push($info_item_content, $value['content']);
}
// for CreateForm(), CreatePagination(), MainEnd()
$form_controls = $temp_array['formControls'];
for ($i = 0; $i < count($form_controls); $i++) {
$form_controls[$i]['disabled'] = $page_indentity >= 2 ? 'disabled' : '';
}
$form_preferences = [
$form_heading[$page_indentity],
$form_action[$page_indentity],
$form_button_color[$page_indentity],
$form_button_text[$page_indentity]
];
// for CreateFooter()
$copyright_year = $temp_array['contents']['copyrightYear'];
$copyright_holder = $temp_array['contents']['copyrightHolder'];
$link_items = $temp_array['linkItems'];
// for HtmlEnd()
$bootstrap_js = $temp_array['contents']['bootstrapJs'];
$bs_js_integrity = $temp_array['contents']['bsJsIntegrity'];
$custom_js = [];
foreach ($temp_array['contents']['customJs'] as $key => $value) {
array_push($custom_js, $value['location']);
}
html-contents.json
{
"contents" : {
"colorModesJs" : "https://getbootstrap.com/docs/5.3/assets/js/color-modes.js",
"description" : "FileMaker Data API テスト 及び サンプル",
"author" : "",
"title" : "records - Edit Record",
"bootstrapCss" : "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
"bsCssIntegrity" : "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH",
"bsFrameCss" : "./css/bootstrap-common.css",
"customCss" :
[
{
"location" : "./css/fmda-form.css"
}
],
"infoHeading" : "dataInfo",
"infoNumberOfItems" : 6,
"copyrightYear" : "2025",
"copyrightHolder" : "FileMaker Data API を使う",
"bootstrapJs" : "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js",
"bsJsIntegrity" : "sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz",
"customJs" :
[
{
"location" : "./js/fmda-form-check.js"
},
{
"location" : "./js/fmda-select-page.js"
}
],
"logoSvg" : "https://www.mlbstatic.com/team-logos/league-on-dark/1.svg",
"logoSvgWidth" : "72"
},
"pageContents" : [
{
"heading" : "records - Create Record",
"lead" : "Claris FileMaker Data API のリクエスト records - Create Record のテスト。フォームから PHP を実行して、FileMaker Server へリクエストを送信して新規レコードを作成します。"
},
{
"heading" : "records - Edit Record",
"lead" : "Claris FileMaker Data API のリクエスト records - Edit Record のテスト。フォームから PHP を実行して、FileMaker Server へリクエストを送信してレコードを更新します。"
},
{
"heading" : "records - Delete Record",
"lead" : "Claris FileMaker Data API のリクエスト records - Delete Record のテスト。フォームから PHP を実行して、FileMaker Server へリクエストを送信してレコードを削除します。"
},
{
"heading" : "records - Duplicate Record",
"lead" : "Claris FileMaker Data API のリクエスト records - Dupulicate Record のテスト。フォームから PHP を実行して、FileMaker Server へリクエストを送信して現在のレコードを複製します。"
}
],
"formContents" : [
{
"heading" : "チーム - レコード作成",
"action" : "PHP_SELF",
"buttonColor" : "btn-primary",
"buttonText" : "新規レコード作成"
},
{
"heading" : "チーム - レコード編集",
"action" : "PHP_SELF",
"buttonColor" : "btn-warning",
"buttonText" : "レコード更新"
},
{
"heading" : "チーム - レコード削除",
"action" : "PHP_SELF",
"buttonColor" : "btn-danger",
"buttonText" : "レコード削除"
},
{
"heading" : "チーム - レコード複製",
"action" : "PHP_SELF",
"buttonColor" : "btn-warning",
"buttonText" : "レコード複製"
}
],
"infoItems" : [
{
"heading" : "ソリューション",
"content" : "MLB サンプル"
},
{
"heading" : "レイアウト",
"content" : "standings"
},
{
"heading" : "テーブル",
"content" : "standings"
},
{
"heading" : "テーブルのレコード数",
"content" : "27"
},
{
"heading" : "現在のレコード数",
"content" : "27"
},
{
"heading" : "取得したレコード数",
"content" : "27"
}
],
"formControls" :
[
{
"id" : "mlb_site_id",
"label" : "MLB 公式サイト ID",
"type" : "text",
"name" : "mlb_site_id",
"placeholder" : "100",
"required" : "1",
"disabled" : "",
"feedback" : "MLB 公式サイトの チーム ID を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "year",
"label" : "シーズン",
"type" : "text",
"name" : "year",
"placeholder" : "2024",
"required" : "1",
"disabled" : "",
"feedback" : "シーズン(西暦)を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "league",
"label" : "リーグ",
"type" : "select",
"name" : "league",
"placeholder" : "",
"required" : "1",
"disabled" : "",
"feedback" : "リーグを選択してください。",
"option" : [
{
"value" : "アメリカン・リーグ"
},
{
"value" : "ナショナル・リーグ"
}
]
},
{
"id" : "division",
"label" : "地区",
"type" : "select",
"name" : "division",
"placeholder" : "",
"required" : "1",
"disabled" : "",
"feedback" : "地区を選択してください。",
"option" : [
{
"value" : "東地区"
},
{
"value" : "中地区"
},
{
"value" : "西地区"
}
]
},
{
"id" : "team",
"label" : "チーム名",
"type" : "text",
"name" : "team",
"placeholder" : "",
"required" : "1",
"disabled" : "",
"feedback" : "チーム名を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "wins",
"label" : "勝利数",
"type" : "text",
"name" : "wins",
"placeholder" : "81",
"required" : "1",
"disabled" : "",
"feedback" : "レギュラーシーズンの勝利数を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "losses",
"label" : "敗戦数",
"type" : "text",
"name" : "losses",
"placeholder" : "81",
"required" : "1",
"disabled" : "",
"feedback" : "レギュラーシーズンの敗戦数を入力してください。。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "winning_percentage",
"label" : "勝率",
"type" : "text",
"name" : "winning_percentage",
"placeholder" : ".500",
"required" : "",
"disabled" : "",
"feedback" : "",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "runs_scored",
"label" : "得点",
"type" : "text",
"name" : "runs_scored",
"placeholder" : "",
"required" : "1",
"disabled" : "",
"feedback" : "レギュラーシーズンの得点を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "runs_allowed",
"label" : "失点",
"type" : "text",
"name" : "runs_allowed",
"placeholder" : "",
"required" : "1",
"disabled" : "",
"feedback" : "レギュラーシーズンの失点を入力してください。",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "winning_percentage",
"label" : "ピタゴラス勝率",
"type" : "text",
"name" : "pythagorean_expectation",
"placeholder" : "",
"required" : "",
"disabled" : "",
"feedback" : "",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "expected_win_loss",
"label" : "X-W/L",
"type" : "text",
"name" : "expected_win_loss",
"placeholder" : "",
"required" : "",
"disabled" : "",
"feedback" : "",
"option" : [
{
"value" : ""
}
]
},
{
"id" : "winning_percentage_difference",
"label" : "勝率差分",
"type" : "text",
"name" : "winning_percentage_difference",
"placeholder" : "",
"required" : "",
"disabled" : "",
"feedback" : "",
"option" : [
{
"value" : ""
}
]
}
],
"linkItems" : [
{
"pageIdentity" : 0,
"formId" : "gotoCreateRecord",
"anchorId" : "createRecord",
"textContent" : "レコード作成"
},
{
"pageIdentity" : 1,
"formId" : "gotoEditRecord",
"anchorId" : "editRecord",
"textContent" : "レコード編集"
},
{
"pageIdentity" : 2,
"formId" : "gotoDeleteRecord",
"anchorId" : "deleteRecord",
"textContent" : "レコード削除"
},
{
"pageIdentity" : 3,
"formId" : "gotoDuplicateRecord",
"anchorId" : "duplicateRecord",
"textContent" : "レコード複製"
}
]
}
fmda-form-check.js
fmda-select-page.js
'use strict';
const createRecordForm = document.querySelector('#gotoCreateRecord');
const editRecordForm = document.querySelector('#gotoEditRecord');
const deleteRecordForm = document.querySelector('#gotoDeleteRecord');
const duplicateRecordForm = document.querySelector('#gotoDuplicateRecord');
const createRecord = document.querySelector('#createRecord');
const editRecord = document.querySelector('#editRecord');
const deleteRecord = document.querySelector('#deleteRecord');
const duplicateRecord = document.querySelector('#duplicateRecord');
createRecord.addEventListener('click', function( ) {
createRecordForm.submit();
});
editRecord.addEventListener('click', function( ) {
editRecordForm.submit();
});
deleteRecord.addEventListener('click', function( ) {
deleteRecordForm.submit();
});
duplicateRecord.addEventListener('click', function( ) {
duplicateRecordForm.submit();
});
フッタのリンク部分
フッタ部分は、bs-builder-class.php
の BsBuilder
クラス、スタティックメソッド CreateFooter()
に記述されています。
id
付きの <a>
タグの前に、type="hidden"
の form コントロールを持つ、フォームが仕込まれており、こちらにもそれぞれ id
が設定されていて、ページの識別子と紐付けています。
id
は、html-contents.json
の linkItems
JSON配列内で設定されており、これを、preferences.php
で、$link_items
配列として展開しています。
これを fmda-select-page.js
でクリックを監視。クリックされた id に基づいて、name="page-identity"
の値を、POST で送信しています。
これに、index.php
の以下が反応し、ページを識別します。
@$page_indentity = $_POST['page-identity'];
$page_indentity = $page_indentity === null ? 1 : $page_indentity;
これで、リクエスト機能なしのページは完成です。シングルページで、4つのフォームを表示できるようになりました。
次回から、FileMaker Data API のリクエストを追加していきます。