質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

1375閲覧

add_rewrite_rule を現在URLから動的に指定する方法

gorimaz

総合スコア26

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2022/02/28 13:06

編集2022/03/08 13:55

前提・実現したいこと

WordPressで独自テーブルwp_colorsのデータを出力しています。
このときデータがなければ404でいいのですが、データがあるときは404を回避したいです。

発生している問題・エラーメッセージ

404を回避できず、コンソール画面にこのエラーメッセージが表示されてしまいます。

GET https://example.com/red/ 404

該当のソースコード

こんな風にwp_colorsテーブルを作り、値をinsertしておきます。

SQL

1create table wp_colors ( 2 ID int(10), 3 color varchar(100), 4 details varchar(1000) 5); 6 7insert into wp_colors 8values (1, 'red', '情熱の色'); -- 実際のレコードは10万行ほどあります

そして "https://example.com/red" にアクセスしたときに以下のPHPでredのレコードを取得し、JSでHTMLを描画します。

php

1/* 2functions.php 3*/ 4 5// wp_localize_script でレコードを出力 6add_action( 'wp_enqueue_scripts', 'enqueue_data' ); 7function enqueue_data(){ 8 9 // レコードを取得 10 $color = 'red'; 11 $colors = get_colors( $color ); 12 13 // visit.jsへ出力 14 $colors = json_encode( $colors ); // $colors = [ {"details": "情熱の色"} ] 15 wp_localize_script( 'visit.js', 'colors', $colors ); 16} 17 18// レコードを取得 19function get_colors( $color ){ 20 $query = "select details from wp_colors where color = '{$color}';"; 21 return $wpdb->get_results( $query, ARRAY_A ); 22}

JavaScript

1/* 2visit.js 3*/ 4 5console.log(colors); // wp_localize_script から値を受け取れていることを確認 6colors.forEach((color)=>{ 7 $('body').append(`<p>${color.details}<p>`); // HTMLを描画 8});

この結果、HTMLは<p>情熱の色<p>と描画できますが、しかしこのページは404ステータスになってしまいます。

どのようにすれば普通のwp_posts等と同じように、「データがあるので404ではないですよ」とステータスを返すことができるでしょうか?

試したこと

以下add_rewrite_ruleを用いて、"https://example.com/red" にアクセスしたときに無理矢理post=1(つまりWordPressインストール時にあるHello,World!の記事)に飛ばしましたところ、404は回避できました。

PHP

1/* 2functions.php 3*/ 4 5// 404を回避 6add_action('init', 'rewrite_404_v1'); 7function rewrite_404_v1() { 8 // リライトを実行 → こうすると404は回避できる 9 $regex = 'red'; 10 add_rewrite_rule($regex, 'index.php?post=1', 'top'); 11}

あとはこれを現在URLに応じて動的に指定するだけだと思い次のように修正したところ、また404になってしまいました。つまりどうやら、現在URLに応じて動的に指定する、ということがadd_rewrite_ruleではできない様子なのです。

PHP

1/* 2functions.php 3*/ 4 5// 現在URLを定義 6define( 'URL', (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . ( $_SERVER['REQUEST_URI'] === '/undefined/' ? '' : $_SERVER['REQUEST_URI'] ) ); 7 8// 404を回避 9add_action('init', 'rewrite_404_v2'); 10function rewrite_404_v2() { 11 12 // 現在URLからcolorを取得 13 $paths = explode( '/', parse_url(URL)['path'] ); 14 $color = urldecode( $paths[1] ); // $color = 'red' や 'blue' など 15 16 // レコードを取得 17 $colors = get_colors( $color ); 18 if ( empty($colors) ) { 19 20 // レコードがない場合は 404 なので何もしない 21 22 } else { 23 24 // リライトを実行 → こうすると効かない! 25 $regex = $color; 26 add_rewrite_rule($regex, 'index.php?post=1', 'top'); 27 28 } 29}

続いて、URL構造を "https://example.com/red" でなく "https://example.com/color/red" にした場合として下記rewrite_404_v3を試しましたが、こちらも上記rewrite_404_v2同様にできませんでした。

PHP

1/* 2functions.php 3*/ 4 5// 現在URLを定義 6define( 'URL', (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . ( $_SERVER['REQUEST_URI'] === '/undefined/' ? '' : $_SERVER['REQUEST_URI'] ) ); 7 8// 404を回避 9add_action('init', 'rewrite_404_v3'); 10function rewrite_404_v3() { 11 12 // 現在URLからcolorを取得 13 $paths = explode( '/', parse_url(URL)['path'] ); 14 $color = urldecode( $paths[2] ); // $color = 'red' や 'blue' など 15 16 // レコードを取得 17 $colors = get_colors( $color ); 18 if ( empty($colors) ) { 19 20 // レコードがない場合は 404 なので何もしない 21 22 } else { 23 24 // リライトを実行 → これもやはり効きませんでした 25 $regex = 'colors/(\w+)/?'; // ご回答の '/colors/(\w+)/?' と、左記の 'colors/(\w+)/?' で試しました 26 add_rewrite_rule($regex, 'index.php?post=1', 'top'); 27 28 } 29}

補足

一.wp_colorsの使用は絶対でして、wp_tagsなどデフォルトのテーブルは使わないものとします。

二.すべてのソースコードは簡易版であり、実際はインジェクション対策などを介していますのでご安心ください。

三.HTMLをPHPで描画することは一切なく、該当のソースコードの流れのようにwp_localize_scriptで出力された後にJSで描画されるか、もしくはページ遷移の際はajaxでechoされた後にJSで描画されます。そのため index.php、single.php、page.php は全て以下の3行だけです。(なのでWordPressを使う必要性はあまりないわけですが…)

PHP

1<?php 2get_header(); 3get_footer();

四.もちろん「パーマリンク設定 > 変更を保存」は実行済みです。

五.add_rewrite_ruleにこだわりはないので、他の方法でも全く構いません。

六.add_rewrite_ruleの直後にflush_rewrite_rulesを実行したら解決しましたが、高コストらしいので避けたい操作です。

以上です。
魔改造で申し訳ございませんが、宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KazuhiroHatano

2022/02/28 13:51

やりたいことは、 「全てのページで別のデザインでの表示ができるようにする」 ですか?
gorimaz

2022/02/28 15:01 編集

コメントありがとうございます。いいえです。 質問のようなURLのページでは以下すべて同じデザインです。 "https://example.com/red" "https://example.com/green" "https://example.com/blue" また次のURLなら上の3つとは別のデザインです。 "https://example.com/favorite/colors" このようにURLに応じていくつかのデザイン(HTML構造)がありますが、全て別のデザインではございません。 ちなみに「補足の三」にあるように、それらのHTMLは全てJavaScriptで描画されます。
gorimaz

2022/02/28 14:24

colorはデザインではなくて、コンテンツの意味です。wp_postsと同じ感じでwp_colorsがあると思ってください。
guest

回答1

0

ベストアンサー

特段できない理由がないのであれば
普通にcolorsが変更されたらその内容から(red|green|blue)/?のようなパターンを作って
add_rewirte_ruleしてflush_rewrite_rulesする、でいいと思います

colorsの内容が実はかなり膨大、colorsの内容は数分ごとに更新される、
colorsの内容の変更をWordPressは感知できない、というような理由で
リライトルールのパターンに落とし込んだり、都度flush_rewrite_rulesをすることが現実的でない場合
404になってからadd_rewrite_ruleするようなことをするよりは
parse_requestでリクエストの404が確定する直前にそれを上書きしてしまう方が
処理としては適切に思います

投稿2022/02/28 15:30

KazuhiroHatano

総合スコア7834

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gorimaz

2022/02/28 15:49 編集

> 404が確定する直前にそれを上書き まさにそのようなアクションフックを探しておりました。あったんですね。博識な方のお目に留まり嬉しく思います。 さてご提示のリンクにあるサンプルコードを拝見したのですが、引数に$queryとありますし、「>The parse_request hook affects only the main query」(メインクエリにだけ効く)ともありますよね。 しかし質問の get_colors() はメインクエリなどではなく自前のSQLなので、残念すぎますが恐らく使えないのではないかと思います。もし使えるとしたらどんな記述をお考えでしょうか? またもし使えないとしたら、悪手かもしれませんが > 404になってからadd_rewrite_ruleする の方法でも、今回の場合は致し方ないかと思っています。こちらのアクションフックについても教えて頂けますでしょうか?
gorimaz

2022/02/28 15:44

ちなみに、wp_colorsはinsert intoにあるように10万行ほどございますので、`(red|green|blue)/?`のような個別指定でなく、URLからの動的な指定が必要と考えています。
gorimaz

2022/02/28 15:59

その後見つけた解決策としては、まずリライトはこのようにしておき、 add_action( 'init', 'rewrite_404' ); function rewrite_404(){ $regex = 'color/([^/]+)/?$'; add_rewrite_rule($regex, 'index.php?color=$matches[1]', 'top'); } そしてカスタム投稿タイプcolorを作り、red, green, blue, , , というタイトルで投稿しまくる(wp_colorsテーブル の colorカラムを、カスタム投稿タイプのタイトルとしてコピーする)ことですが、あほくさすぎますよねw
KazuhiroHatano

2022/02/28 16:13

このケースでparse_requestを使う目的は404判定になったメインクエリを上書きすることです parse_requestのコールバックに渡される引数であるWPクラスのオブジェクト$queryの requestプロパティにはリクエストパスが入ってるので parse_requestの段階で$queryが404の状態で、requestがcolorsのいずれかにマッチするならば query_varsをマッチしたcolorsの値に応じて上書きする というような処理になるかと思います また、colorsの情報を受け渡すためのquery_varsを登録し そのquery_varsがメインクエリにある場合は template_includeでcolorsのコンテンツを表示するテンプレートで ページを表示させる、といった処理も必要かと思います
gorimaz

2022/02/28 18:43 編集

ご返信ありがとうございます。なるほど、メインクエリの上書きですか。 > query_varsをマッチしたcolorsの値に応じて上書き というのがピンと来ませんが、 応じた値というのがわからず、とりあえず空配列を試しに $query->query_vars = [] のようにしたら… できました。めちゃくちゃすごいです!こんな処理をよくご存じですね。 こんな時間まで苦労した甲斐がありました。本当にありがとうございます。 ところで、次の2つの引用については理解できず放置しているのですが、放置すると何が問題になりますか? 引用1 > colorsの情報を受け渡すためのquery_varsを登録 引用2 > そのquery_varsがメインクエリにある場合は > template_includeでcolorsのコンテンツを表示するテンプレートで > ページを表示させる
gorimaz

2022/02/28 18:28

まず引用1についてはこうでしょうか?しかしこれがなくても機能しました。 add_filter('query_vars', 'add_query_vars' ); function add_query_vars( $query_vars ){ $query_vars[] = 'color'; return $query_vars; } 次に引用2については、補足の三にあるように「>HTMLをPHPで描画することは一切なく」で作っているので、恐らくテンプレートに関する処理は不要でいいのかなと思っています。
KazuhiroHatano

2022/03/01 01:26

> これがなくても機能しました メインクエリにどのcolorsの値がリクエストされたかを持たせられるようにしておけば template_includeでその値を取り出して使うことができたり メインクエリで無駄な投稿の取得をさせないようにすることもしやすくなります やろうとしていることに付帯的に必要になってくることです > 恐らくテンプレートに関する処理は不要でいいのかな 対応するcolorsのあるURLでアクセスされたらそのHTMLでページを表示したいのですよね? template_includeはリクエストされたページを テーマのどのテンプレートで表示するかを書き換えるフィルタフックです
gorimaz

2022/03/01 01:54

なるほど、query_varsというアクションフックを用いて、template_includeでredなどが取り出せるようにしておくのですね。 いや本当にお詳しい…恐れ入ります。今回の質問を頭の中だけで組み立てて必要な修正点を挙げるというのは相当のレベルだと思いました。 改めまして、どうもありがとうございました。
KazuhiroHatano

2022/03/01 04:20

ちなみに、今更ですが、一番いいのはcolorsのURLのパターンを 他のリライトルールのパターンに干渉しないようなものにして 普通にadd_rewrite_ruleで処理できるようにすることです 例えば /colors/(\w+)/? みたいな感じです パス全体を任意の文字列にしたいようなので 404にしてから処理するようなやり方に乗って回答しましたが あまりキレイなやり方とは言えません
gorimaz

2022/03/08 13:59

こんばんは。遅くなりまして申し訳ございません。コメント気が付きませんでした。 僭越ながら、そちらは質問文の rewrite_404_v2 と同じ事ではないでしょうか? 試しに rewrite_404_v3 を質問文に記載し試しましたが、やはりできませんでした。 (うっかりしがちな「パーマリンク設定 > 変更を保存」は実行済みです。) 恐らく現在URLから動的にリライトを判定する、という事ができなそうなのかな。という所感でいるのですが…
KazuhiroHatano

2022/03/09 01:28

_v2ではcolorsのみをパスに使うようになっていますが、これでは問題があります。 colorsが任意の文字列である限り、全てのリクエストのURLがマッチしてしまい、 このリライトルールより後ろのルールに処理がいかなくなってしまうのです。 なのでcolorsのリクエストであると絞り込めるようなルールにする必要があります それが_v2と_v3の違いです。 現在URLから動的にリライトルールを定義するということは flush_rewrite_rulesをリクエストの都度行えばできますが、 質問にも書かれている通りこれは推奨されません しかしルールの一部を曖昧にして それによって処理を変えるということはできます これが本来のリライトルールの使い方です これをするのにcolorsの値を取得する必要はありません colorsの値が入るところを曖昧にしておけばいいのです ただWordPressの投稿でないものからページを生成する場合には 404判定にならないようにする処理は必要になります
gorimaz

2022/03/09 04:10

ご返信ありがとうございます。 >これをするのにcolorsの値を取得する必要はありません この文以前は理解できましたが、この文は質問とはマッチしないかなと思います。 質問は「>データがなければ404でいいのですが、データがあるときは404を回避したい」なので、「>colorsの値を取得する必要」はあるのではないでしょうか? つまりもし "https://example.com/color/red" でなく "https://example.com/color/turquoise" にアクセスされ、しかし turquoise のデータがないとき、colorsの値を取得しておかなければ404に分岐させることはできないのではないでしょうか?
KazuhiroHatano

2022/03/09 04:24

colorsのリライトルールにマッチしてるけど対応するcolorsが存在しない場合に対応するのは リライトルールで対応することではないです リライトルールではマッチしたURLの部品をURLパラメータに引き継いで利用できます これをquery_varsとしてメインクエリに渡されるようにしておけば parse_requestなどのメインクエリに係るアクションフックでその値を使って colorsの有無を判定し、存在しなければ404にするということもできます。
gorimaz

2022/03/10 03:40

>リライトルールで対応することではない 度々のご返信ありがとうございます。仰る意味がやっとわかってきました。 リライトしなければ即座に404となり、リライトすればquery_varsに値が渡せるようになるから、parse_requestでredやturquoiseの有無を判定して404を分岐せよということですね。 深い仕組みの理解がなく、リライトルールかparse_requestかどっちか、でしか考えておりませんでした。早速そちらで試させて頂きます。 できればいいだけではなく、仕様に沿ったカスタム方法のアドバイスまでありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問