- コーディング
【JavaScript】数値を文字列に変換する | 文字列を数値に変換する
- #JavaScript
忘れないうちに備忘録残しておこうと久々の投稿です。
今回、WordPressを使ったサイト制作をすることになり、
その際の要件に動的にページを生成するということが入ってきました。
データベースに登録している情報を引っ張ってきて、自動でページを生成するということですね。
例えば、データベースのテーブルに設定されているページ件数が10件だったら、自動で10件表示させるようなイメージですね。
ChatGPTやCodeCopilot使ったり、英語のページ漁ったり。。。
なかなかピンポイントで解決策載ってるページもなく、AI使ってもすぐに解決に至らず、数日、時間を費やしてました。。。
今後、再現性高くやるためにも、備忘録に残しておこうと思った次第です。
同じような課題にぶちあったっている人の参考になればと思います。
では本題に入りましょう。
あくまで機能実装の紹介のため、レイアウトは整えていません。
今回使用したプラグインはCustom Post Type UIです。
カスタムで作れる投稿タイプとタクソノミーを使用するので、このプラグインを選びました。
まずはインストールして有効化しましょう!
それではインストールしたCustom Post Type UIでカスタム投稿タイプとカスタムタクソノミーを登録します。
今回はカスタム投稿タイプスラッグを「fruit-post」、カスタムタクソノミースラッグを三つ、「fruit」、「price」、「area」と設定しました。
以下、詳細な設定です。
基本設定 | |
投稿タイプスラッグ | fruit-post |
複数形のラベル | 果物ニュース(※これは任意の名前で可。) |
単数形のラベル | 果物ニュース(※複数形と同じで問題ない) |
追加ラベル | |
ここは全件、空欄で問題なし | |
設定 | |
公開 | True |
一般公開クエリー可 | True |
UI を表示 | True |
ナビゲーションメニューに表示 | True |
ユーザーと一緒に削除 | False |
REST API で表示 | True |
REST API ベーススラッグ | 空欄 |
REST API コントローラークラス | 空欄 |
REST API 名前空間 | 空欄 |
アーカイブあり | True(※ここは必ずTrueにする) アーカイブに使うURLを設定するとその名称が表示される。 空欄にすると今回の設定では「http://sample.local/fruit-post/?fruit%5B%5D=orange」となる。例えば、hogeと設定すると「http://sample.local/hoge/?fruit%5B%5D=orange」となる |
検索から除外 | False |
権限タイプ | post |
階層 | False |
エクスポート可能 | False |
リライト | True |
カスタムクエリー変数スラッグ | 空欄 |
メニューの位置 | 空欄 |
メニューに表示 | True |
メニューアイコン | 空欄 |
メタボックスコールバック | 空欄 |
サポート | 任意でチェック |
カスタム「サポート」 | 空欄 |
タクソノミー | カスタムタクソノミーで作成した「果物」「金額」「エリア」をチェック |
タクソノミーとは「分類」というイメージ。
カスタム投稿タイプ「fruit-post」で使うためのタクソノミーをここで作っていく。
例として、「fruit」という名前で作成する方法を記載するので、
同様のやり方で「price」、「area」も作ってください。
基本設定 | |
タクソノミースラッグ | fruit |
複数形のラベル | 果物(※これは任意の名前で可。) |
単数形のラベル | 果物(※複数形と同じで問題ない) |
利用する投稿タイプ | 果物ニュースにチェック |
追加ラベル | |
ここは全件、空欄で問題なし | |
公開 | True |
公開クエリー可 | True |
階層 | True |
UI を表示 | True |
メニューに表示する | True |
ナビゲーションメニューに表示 | True |
クエリー変数 | True |
カスタムクエリー変数文字列 | 空欄 |
リライト | True |
カスタムリライトスラッグ | 空欄 |
フロントでのリライト | True |
階層リライト | False |
管理画面でカラムを表示 | True |
REST API で表示 | True |
REST API ベーススラッグ | 空欄 |
REST API コントローラークラス | 空欄 |
REST API 名前空間 | 空欄 |
タグクラウドへ表示します。 | False |
クイック編集 / 一括編集パネルに表示。 | False |
並び替え | False |
メタボックスコールバック | 空欄 |
デフォルトのターム | 空欄 |
カスタム投稿ページとアーカイブページのファイル名は決まりがある。
登録済の一覧画面に記載があるので、ここを参考にするとよい。
実際には既存のデータベースからデータを引っ張ってくるという形になるが、
公開は動作検証のため、手動でデータ登録を行った。
私はLocalを使っているので、参考イメージを以下に掲載。
赤枠のところをクリックするとデータベースが開ける。
これが登録後のイメージ。
テーブル名は「wp_sample_fruits」と登録。
カラム名は「page_id」「fruit_name」「price」「area」と登録。
列名 | 型 | 長さ |
page_id | int | 自動で11と入る |
fruit_name | varchar | 任意で20と設定 |
price | varchar | 任意で20と設定 |
area | varchar | 任意で20と設定 |
page_id | fruit_name | price | area |
1 | apple | 100 | aomori |
2 | strawberry | 150 | saga |
3 | strawberry | 120 | ehime |
4 | grapes | 150 | yamanashi |
5 | apple | 150 | aomori |
6 | orange | 100 | ehime |
全てにファイルは使用するテーマの同一階層に格納してください。
/**
* カスタム投稿ページの動的生成
*/
function generate_dynamic_posts() {
global $wpdb;
// カスタム投稿タイプが存在するかを確認
if (!post_type_exists('fruit-post')) {
return;
}
// タクソノミーが存在するかを確認
if (!taxonomy_exists('fruit') || !taxonomy_exists('price') || !taxonomy_exists('area')) {
return;
}
// データベースから値を取得
$results = $wpdb->get_results("SELECT page_id, fruit_name, price, area FROM wp_sample_fruits");
foreach ($results as $row) {
$post_title = 'Container Post ' . $row->page_id;
$post_content = '果物: ' . $row->fruit_name . ', 金額: ' . $row->price . ', エリア: ' . $row->area;
$post_slug = 'page-' . $row->page_id; // スラッグを page_id に設定
// 投稿が存在するかをチェック
$post_exists = get_page_by_path($post_slug, OBJECT, 'fruit-post');
if ($post_exists) {
continue; // スラッグが既に存在する場合はスキップ
}
// 新規投稿の作成
$post_id = wp_insert_post(array(
'post_title' => $post_title,
'post_content' => $post_content,
'post_status' => 'draft', // 初期状態を下書きに設定。公開済:publish
'post_type' => 'fruit-post',
'post_name' => $post_slug // スラッグを指定
));
if (!is_wp_error($post_id)) {
// タクソノミーを設定
wp_set_object_terms($post_id, $row->fruit_name, 'fruit');
wp_set_object_terms($post_id, $row->price, 'price');
wp_set_object_terms($post_id, $row->area, 'area');
} else {
error_log('Failed to create post: ' . $post_id->get_error_message());
}
}
}
add_action('init', 'generate_dynamic_posts');
/**
* 絞り込み検索
*/
function custom_search_query($query) {
// クエリが検索結果ページに対するものであり、且つ管理画面でない場合
if ($query->is_search && !is_admin()) {
$tax_query = array('relation' => 'AND'); // タクソノミークエリ内の全ての条件を満たす場合
// 果物
if (!empty($_GET['fruit_name'])) {
$tax_query[] = array(
'taxonomy' => 'fruit', // 指定のタクソノミーに対してクエリを実行
'field' => 'slug', // スラッグで識別
'terms' => $_GET['fruit_name'], // 指定した'fruit_name'の値に一致するタクソノミーを識別
'operator' => 'IN', // 'terms'に含まれるいずれかの値に一致
);
}
// 金額
if (!empty($_GET['price'])) {
$tax_query[] = array(
'taxonomy' => 'price', // 指定のタクソノミーに対してクエリを実行
'field' => 'slug', // スラッグで識別
'terms' => $_GET['price'], // 指定した'price'の値に一致するタクソノミーを識別
'operator' => 'IN', // 'terms'に含まれるいずれかの値に一致
);
}
// エリア
if (!empty($_GET['area'])) {
$tax_query[] = array(
'taxonomy' => 'area', // 指定のタクソノミーに対してクエリを実行
'field' => 'slug', // スラッグで識別
'terms' => $_GET['area'], // 指定した'area'の値に一致するタクソノミーを識別
'operator' => 'IN', // 'terms'に含まれるいずれかの値に一致
);
}
$query->set('tax_query', $tax_query);
}
}
add_action('pre_get_posts', 'custom_search_query');
<form method="get" action="<?php echo esc_url(get_post_type_archive_link('fruit-post')); ?>">
<p style="background-color: #f3f3f3; font-weight: bold; padding: 3px 8px;">
果物で探す
</p>
<?php
// 取得するタクソノミーが「fruit(果物)」の場合
$terms = get_terms(array(
'taxonomy' => 'fruit',
'hide_empty' => false, // 投稿がない場合でも表示
));
if (!empty($terms) && !is_wp_error($terms)) {
echo '<ul style="margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 24px;">';
foreach ($terms as $term) {
echo '<li style="list-style: none;">';
echo '<label>';
echo '<input type="checkbox" name="fruit[]" value="' . esc_attr($term->slug) . '"> ';
echo esc_html($term->name);
echo '</label>';
echo '</li>';
}
echo '</ul>';
} else {
echo '<p>カスタムタクソノミーが見つかりませんでした。</p>';
}
?>
<p style="background-color: #f3f3f3; font-weight: bold; padding: 3px 8px;">
金額で探す
</p>
<?php
// 取得するタクソノミーが「price(金額)」の場合
$terms = get_terms(array(
'taxonomy' => 'price',
'hide_empty' => false, // 投稿がない場合でも表示
));
if (!empty($terms) && !is_wp_error($terms)) {
echo '<ul style="margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 24px;">';
foreach ($terms as $term) {
echo '<li style="list-style: none;">';
echo '<label>';
echo '<input type="checkbox" name="price[]" value="' . esc_attr($term->slug) . '"> ';
echo esc_html($term->name);
echo '</label>';
echo '</li>';
}
echo '</ul>';
} else {
echo '<p>カスタムタクソノミーが見つかりませんでした。</p>';
}
?>
<p style="background-color: #f3f3f3; font-weight: bold; padding: 3px 8px;">
エリアで探す
</p>
<?php
// 取得するタクソノミーが「area(エリア)」の場合
$terms = get_terms(array(
'taxonomy' => 'area',
'hide_empty' => false, // false:投稿がない場合でも表示
));
if (!empty($terms) && !is_wp_error($terms)) {
echo '<ul style="margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 24px;">';
foreach ($terms as $term) {
echo '<li style="list-style: none;">';
echo '<label>';
echo '<input type="radio" name="area" value="' . esc_attr($term->slug) . '"> '; // 配列ではなく単一値
echo esc_html($term->name);
echo '</label>';
echo '</li>';
}
echo '</ul>';
} else {
echo '<p>カスタムタクソノミーが見つかりませんでした。</p>';
}
?>
<button type="submit">検索</button>
</form>
<?php
get_header();
if (is_singular('fruit-post')) {
global $wpdb;
$post_id = get_the_ID(); // 現在の投稿IDを取得
$post_slug = get_post_field('post_name', $post_id); // 投稿スラッグを取得
// デバッグ情報の表示
echo '<p>Post ID: ' . esc_html($post_id) . '</p>';
echo '<p>Post Slug: ' . esc_html($post_slug) . '</p>';
// スラッグから数値部分を抽出
if (preg_match('/(\d+)/', $post_slug, $matches)) {
$page_id = intval($matches[1]);
// データベースから値を取得して表示する
$result = $wpdb->get_row($wpdb->prepare("SELECT * FROM wp_sample_fruits WHERE page_id = %d", $page_id));
if ($result) {
echo '<h2>ページID ' . esc_html($result->page_id) . '</h2>';
echo '<p>果物: ' . esc_html($result->fruit_name) . '</p>';
echo '<p>金額: ' . esc_html($result->price) . '</p>';
echo '<p>エリア: ' . esc_html($result->area) . '</p>';
} else {
echo '<p>No data found for this page. Page ID: ' . esc_html($page_id) . '</p>';
}
} else {
echo '<p>Invalid slug format. Post Slug: ' . esc_html($post_slug) . '</p>';
}
} else {
echo 'ページIDが取得できません';
}
get_footer();
?>
<?php
get_header();
// クエリをカスタマイズ
$args = array(
'post_type' => 'fruit-post',
);
$tax_query = array();
if (!empty($_GET['fruit'])) {
$tax_query[] = array(
'taxonomy' => 'fruit',
'field' => 'slug',
'terms' => array_map('sanitize_text_field', $_GET['fruit']),
);
}
if (!empty($_GET['price'])) {
$tax_query[] = array(
'taxonomy' => 'price',
'field' => 'slug',
'terms' => array_map('sanitize_text_field', $_GET['price']),
);
}
if (!empty($_GET['area'])) {
$tax_query[] = array(
'taxonomy' => 'area',
'field' => 'slug',
'terms' => sanitize_text_field($_GET['area']), // 単一値としてサニタイズ
);
}
if ($tax_query) {
$args['tax_query'] = $tax_query;
}
$query = new WP_Query($args);
if ($query->have_posts()) :
echo '<ul>'; // ULタグの開始
while ($query->have_posts()) : $query->the_post();
// データベースから値を取得
global $wpdb;
$post_id = get_the_ID();
$post_slug = get_post_field('post_name', $post_id);
// スラッグから数値部分を抽出
if (preg_match('/(\d+)/', $post_slug, $matches)) {
$page_id = intval($matches[1]);
} else {
$page_id = 0;
}
// データベースから値を取得して表示する
$result = $wpdb->get_row($wpdb->prepare("SELECT fruit_name, price, area FROM wp_sample_fruits WHERE page_id = %d", $page_id));
echo '<li>'; // LIタグの開始
echo '<a href="' . get_permalink() . '">' . get_the_title() . '</a>';
// データベースの値を表示
if ($result) {
echo '<p>果物: ' . esc_html($result->fruit_name) . '</p>';
echo '<p>金額: ' . esc_html($result->price) . '</p>';
echo '<p>エリア: ' . esc_html($result->area) . '</p>';
}
echo '</li>'; // LIタグの終了
endwhile;
echo '</ul>'; // ULタグの終了
wp_reset_postdata();
else :
echo '<p>No posts found</p>';
endif;
get_footer();
?>
自分でまとめてみて、かなり長くなってしまったが、いかがでしたでしょうか?
これまでは管理画面で投稿記事を作成して、投稿ページを表示するということしかやったことがなかったので、
データベースから値を取得して、動的にページ生成することは初めての経験だった。
必死に調べてその時は理解出来ても、時間が経つにつれて忘れてしまうので、今回はその為の備忘録でした。