【Laravel】お知らせにハイパーリンク機能を実装する
前書き
先日、社内でLT発表会が開催されまして、その時に発表した「お知らせにハイパーリンク機能を実装する」という内容について今回はご紹介したいと思います。
背景としては、クライアント様より「お知らせの文章にハイパーリンク機能を実装してほしい」と依頼がいただいたので、Laravelでハイパーリンク機能を実装することになりました。
Markdown::parse
まず最初にハイパーリンクを実装する方法として思いついたのが、Mailableでも使用されているMarkdown::parse()
です。
Markdown記法に準拠した書き方でお知らせを書き、表示させるタイミングでMarkdownからHTMLに変換させることが出来ます。
使用例としては以下のような感じです。
<?php
use Illuminate\Mail\Markdown;
$text = "[Github](https://github.com)";
echo Markdown::parse($text);
// <a href="https://github.com">Github</a>
- Laravelの標準機能でハイパーリンクが実現できる
- 他のMarkdownにも対応
- たった2行で実装できる
上記の理由から、Markdownファザードを採用するのが最適解なような気がしていました。
チームに提案したところ、さまざまな欠陥が。
先述した理由から、私は自信満々にMarkdownファザードを提案したところ、チームメンバーからは様々な懸念点を教えてもらいました。
その中でも大きな問題点は以下の2点です。
- Markdownファザードでは、直打ちされたURLはリンクにならない
- 予期せず見出しなどに変換される恐れがある
この2点はMarkdownファザードを採用するには大きな壁になります。
私はMarkdownファザード以外でハイパーリンクを実現できる方法を探しました。
正規表現でハイパーリンク機能を実装する
ハイパーリンク機能を実装するにあたり、Markdown記法は採用することにしました。リンク化する文言が判別しやすいためです。([TEXT](URL)
という書き方)
ただし、ハイパーリンク以外のMarkdownの機能を実装したくありません。先述した通り、見出しの機能などは実装しない方法で実現したい。
そんな時、正規表現で以下の2種類を判別する方法を思いつきました。
- 直打ちのURL(
https://~
) - Markdown記法に則ったURL(
[TEXT](https://~)
)
この方法であれば、直打ちのURLをリンク化しつつハイパーリンクも実装可能です。
順に説明していきます。
<?php
$string = mb_ereg_replace(
"((?<!]\()https?://[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]+)",
"<a href="\1">\1</a>",
$string
);
$pattern = "|\[(.*?)]\((https?://[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]+)\)|mis";
preg_match(
$pattern,
$string,
$matches
);
if($matches){
$str = $matches[1];
$url = $matches[2];
$tag = "<a href='$url'>$str</a>";
$string = preg_replace(
$pattern,
$tag,
$string
);
}
リプレイスするコード
<?php
$string = mb_ereg_replace(
"((?<!]\()https?://[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]+)",
"<a href="\1">\1</a>",
$string
);
ここでは、直貼りされたURLを判別してaタグに変換しています。
正規表現は「否定後読み」という方法で、Markdown記法で書かれたURLかどうかを判断しています。
https://~
の形式の文字列とマッチして、httpの直前に](
があるかどうかをどうかを見に行きます。
mb_ereg_replace()
は第3引数に渡された文字列から、第1引数に渡した正規表現にマッチした部分を第2引数の形に置換するPHPのメソッドです。
<?php
$pattern = "|\[(.*?)]\((https?://[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]+)\)|mis";
preg_match(
$pattern,
$string,
$matches
);
続いてのコードですが、ここではMarkdown記法に準拠したURLをURL部分と文字列部分で分けています。
大括弧[]の中身と括弧()の中身を正規表現でマッチさせ、$matches
の中に配列として格納しています。
preg_match()
メソッドは、正規表現にマッチした文字列を第3引数に配列として格納するためのメソッドです。
if($matches){
$str = $matches[1];
$url = $matches[2];
$tag = "<a href='$url'>$str</a>";
$string = preg_replace(
$pattern,
$tag,
$string
);
}
最後のコードでは、$matches
の中身があった時に$matches
のindex=1
に文字列、index=2
にURLが格納されているので、分かりやすいように変数に代入しています。
変数に代入した文字列とURLを、aタグの形にして$tag
に代入しています。
最後に、preg_replace()
で正規表現にマッチした文字列と$tag
を入れ替えています。
終わりに
今回正規表現で実現しましたが、もっとスマートな方法があるかもしれません。
知っている方いたら、教えていただけると嬉しいです。
正規表現についてはほとんど触れてこなかったので、もっと触る機会を増やしていかなければならないなと感じました。
Discussion