マークダウン記法をcakephp3で表示する
パーサ
何種類か試した結果、比較的レンダリングが早いParsedownにしました。
composer require erusev/parsedown
composer require erusev/parsedown-extra
Parsedown と Markdown Extra 両方インストールします。
色々な図が書けるmermaid.js と シンタックスハイライトに
Google Code Prettifyを使いたいので
ParsedownExtraの拡張クラスを作ります。
<code>のクラスがlanguage-mermaidの時はmermaidに
それ以外のときは<pre>のクラスをprettyprintにします。
<?php
class ParsedownExtraPlus extends ParsedownExtra
{
# config
public $blockCodeClassFormat = 'language-%s';
public $blockPreClassHighlight = 'prettyprint';
const version = '0.0.0-beta-1';
public function __construct()
{
if (version_compare(parent::version, '0.8.1') < 0) {
throw new Exception('ParsedownExtraPlugin requires a later version of Parsedown');
}
parent::__construct();
}
protected function blockFencedCode($Line)
{
$Block = parent::blockFencedCode($Line);
if (isset($Block['element']['name']) && $Block['element']['name'] == 'pre' && isset($Block['element']['text']['name']) && $Block['element']['text']['name'] == 'code') {
if (isset($Block['element']['text']['attributes']['class'])) {
if (strpos($Block['element']['text']['attributes']['class'], 'mermaid') === false) {
$Block['element']['attributes']['class'] = $this->blockPreClassHighlight;
} else {
$Block['element']['text']['attributes']['class'] = str_replace(sprintf($this->blockCodeClassFormat, 'mermaid'), 'mermaid', $Block['element']['text']['attributes']['class']);
}
}
}
return $Block;
}
}
この拡張クラスをvendorに適当にフォルダーを作ってcomposer.jsonのautoloadを書き加えます。ここを参考にして
下さい。
はい、パッケージを作りました。下記でインストールできます。
composer require adjmpwgt/parsedown-extra-plus
ヘルパーを作ります。
bin/cake bake helper Markdown
src/View/Helper/MarkdownHelper.phpが出来ます。
下記のuseを追加します。
use Parsedown;
use ParsedownExtra;
use ParsedownExtraPlus;
ファンクションを追加します。
public function transform($text)
{
if (!is_string($text)) {
return;
}
$parser = new ParsedownExtraPlus();
// $perser->blockPreClassHighlight = 'highlight';
return $parser->text($text);
}
Viewに記述
スタイルシートはgithub由来のものにする。
// github markdown
$this->Html->css('//cdnjs.cloudflare.com/ajax/libs/github-markdown-css/4.0.0/github-markdown.min.css', ['block' => true]);
様々な図が表現出来るマーメイドを導入
// mermaid
$this->Html->script('//cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.1/mermaid.min.js', ['block' => true]);
$this->Html->scriptBlock('mermaid.initialize({startOnLoad:true});', ['block' => true]);
syntax highlightの設定
Google Code Prettify を使う場合
下記をview.ctpの先頭に追加して下さい。
// prettify <pre class="prettyprint">
$this->Html->script('//cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js', ['block' => true]);
highlight.js を使う場合
下記をヘルパーに追加。
$perser->blockPreClassHighlight = 'highlight';
下記をview.ctpの先頭に追加。
// highlight <pre class="highlight">
$this->Html->css('//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.2.1/build/styles/default.min.css', ['block' => true]);
$this->Html->script('//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.2.1/build/highlight.min.js', ['block' => true]);
$this->Html->scriptStart(['block' => true]);
echo <<<EOD
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('pre.highlight code').forEach((block) => {
hljs.highlightBlock(block);
});
});
EOD;
$this->Html->scriptEnd();
mermaid.js と highlight.js を同時に使う場合はmermaid.jsのブロックはhighlight.jsが動作しないように細工をしないといけません。MarkdownHelperに以下の行を足すか、
ヘルパーの*$blockPreClassHighlight
*を変更しない場合は以下のようにpreタグのクラスをprettyprintにして下さい。
document.querySelectorAll('pre.prettyprint code').forEach((block) =>
mermaid.jsを使わない場合は下記でよい。
// highlight <pre class="highlight">
$this->Html->css('//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.2.1/build/styles/default.min.css', ['block' => true]);
$this->Html->script('//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.2.1/build/highlight.min.js', ['block' => true]);
$this->Html->scriptBlock('hljs.initHighlightingOnLoad();', ['block' => true]);
実際のマークダウンの出力は
<div class="forumcontent">
<div class="markdown-body markdown">
<?= $this->Markdown->transform($forum->description); ?>
</div>
</div>
参考ページ
CakePHP 4 で Parsedown を使い Markdown をパースして HTML に変換する方法
@tomgoodsunさんの
MarkdownをHTML上に表示するにはどうしたらいいか
見出しのアンカーリンクが作られていないので、作ってみました。
Parsedownを書き換える
を参考にさせて貰いました。