wordpress 投稿内の特定のキーワードに指定のリンクを自動で貼る-ACF対応改修ver 作成日: 2025年8月11日
MTのkotonohaリンクplに似せたfunction.php記述コードで作ってきたが・・・
管理サイトに実装するうち「あれもこれも」となって追記などしてきたが、改めて直してみた。
修正点
- the_contentは動的、acfのフィールドのものは静的=コード記述した時点でDB内の文字列にリンクが貼られるものを、動的に統一した。
ここちょっと疎かにしてて確認してなかったのだが、ページが重くなるリスクはあるが全て動的にしてもとの文章・テキストはRAW状態にした。 - 除外処理の優先度確認。オプション的な機能なので、システム上のエラーで自動リンクが為されなくてもOKだが、pのインラインのbやspanなどまた、除外ワードの厳格化を施した。
- function.php記述のための可読性とか。設定項目を上位にして、設定しやすくね。設定入力inputとかを備えた設定ページ付ければプラグインにもなるけど、エンドユーザーによる利用は考えてない(ここらへんで作ってる機能はみんなそう!)ので、管理者レベルで使いやすく程度かな。
で、コードは以下の通り
/* MTのkotonohalink的-wiki的なコンテンツ制作用重くなること注意。キーワードで重複的になるもの、「日経平均」と「日経」などを指定する場合は重複部分を内包する「日経平均」を処理順上にする */// Type1内部・外部ページへのリンクと除外ページ指定$my_autolink_settings = [ // 内部リンクのキーワードとページID 'internal' => [ 'ことのは' => 38,/////////////////////////////////////////////////-*-初期設定/確認-*- ], // 外部リンクのキーワードとURL 'external' => [ '除外' => 'https://リンク先1',/////////////////////////////////////////////////-*-初期設定/確認-*- 'リンク先2' => 'https://リンク先2', ], // 除外キーワード(これらを含む単語はリンク化しない) 'excluded' => [ 'ことのはリンク',/////////////////////////////////////////////////-*-初期設定/確認-*- '除外2', ], // 処理をスキップするページID(リンク先になるページなど) 'excluded_page_ids' => [9999, 9998],/////////////////////////////////////////////////-*-初期設定/確認-*- // ACFの対象フィールド名'acf_target_fields' => ['access', 'field'],/////////////////////////////////////////////////-*-初期設定/確認-*-!!!ACFフィールドはその出力でpかliにラップされている必要があるのでtextやtextareaではなくリッチエディターにしておく];function my_autolink_keywords_process($content) { global $my_autolink_settings; $internal_keywords_and_ids = $my_autolink_settings['internal']; $external_keywords_and_urls = $my_autolink_settings['external']; $excluded_keywords = $my_autolink_settings['excluded']; // pタグとliタグの中身だけを処理する $content = preg_replace_callback('/<(p|li)([^>]*)>(.*?)<\/\1>/is', function($matches) use ($internal_keywords_and_ids, $external_keywords_and_urls, $excluded_keywords) { $tag_name = $matches[1]; $tag_attributes = $matches[2]; $inner_content = $matches[3]; $placeholders = []; $placeholder_count = 0;// <a>タグとインラインタグ(b, strong, span, em, i)を一時的にプレースホルダーに置き換える//これにより、これらのタグ内のキーワードがリンク化されるのを防ぎます。$inner_content = preg_replace_callback('/(<a\b[^>]*>.*?<\/a>)|(<(b|strong|span|em|i)[^>]*>.*?<\/\3>)/is', function($matches) use (&$placeholders, &$placeholder_count) {$placeholder = '__PLACEHOLDER_' . $placeholder_count++ . '__';$placeholders[$placeholder] = $matches[0];return $placeholder;}, $inner_content); // 除外キーワードをプレースホルダーに置き換える // 除外キーワードを優先して処理するため、長いキーワードからソート $excluded_keywords_sorted = $excluded_keywords; usort($excluded_keywords_sorted, function($a, $b) { return mb_strlen($b) - mb_strlen($a); }); foreach ($excluded_keywords_sorted as $keyword) { $keyword_escaped = preg_quote($keyword, '/'); $inner_content = preg_replace_callback('/(' . $keyword_escaped . ')/u', function($matches) use (&$placeholders, &$placeholder_count) { $placeholder = '__PLACEHOLDER_' . $placeholder_count++ . '__'; $placeholders[$placeholder] = $matches[0]; return $placeholder; }, $inner_content, 1); } // 内部・外部リンクのキーワードをプレースホルダーに置き換える(リンクを生成するため) $link_keywords_to_process = array_merge($internal_keywords_and_ids, $external_keywords_and_urls); uksort($link_keywords_to_process, function($a, $b) { return mb_strlen($b) - mb_strlen($a); }); foreach ($link_keywords_to_process as $keyword => $data) { $keyword_escaped = preg_quote($keyword, '/'); $replacement = ''; if (isset($internal_keywords_and_ids[$keyword])) { $url = get_permalink($internal_keywords_and_ids[$keyword]); $replacement = '<a href="' . esc_url($url) . '" class="totri">' . $keyword . '</a>'; } elseif (isset($external_keywords_and_urls[$keyword])) { $url = $external_keywords_and_urls[$keyword]; $replacement = '<a href="' . esc_url($url) . '" class="totri" target="_blank">' . $keyword . '</a>'; } if ($replacement) { $inner_content = preg_replace('/(' . $keyword_escaped . ')(?![^<]*>)/u', $replacement, $inner_content, 1); } } // 最後に、すべてのプレースホルダーを元の文字列に戻す $inner_content = str_replace(array_keys($placeholders), array_values($placeholders), $inner_content); return '<' . $tag_name . $tag_attributes . '>' . $inner_content . '</' . $tag_name . '>'; }, $content); return $content;}//the_contentフィルターに自動リンク機能を適用function my_autolink_keywords_in_paragraphs($content) { global $my_autolink_settings; $excluded_page_ids = $my_autolink_settings['excluded_page_ids']; $current_page_id = get_the_ID(); if (in_array($current_page_id, $excluded_page_ids)) { return $content; } return my_autolink_keywords_process($content);}add_filter('the_content', 'my_autolink_keywords_in_paragraphs', 10);//ACFフィールドに自動リンク機能を適用function my_autolink_keywords_acf_filter($value, $post_id, $field) { global $my_autolink_settings; $excluded_page_ids = $my_autolink_settings['excluded_page_ids']; $current_page_id = get_the_ID(); $target_fields = $my_autolink_settings['acf_target_fields']; if (in_array($current_page_id, $excluded_page_ids) || !in_array($field['name'], $target_fields) || !is_string($value)) { return $value; } return my_autolink_keywords_process($value);}add_filter('acf/format_value', 'my_autolink_keywords_acf_filter', 10, 3);