マークダウン記法をcakephp3で表示する

# マークダウン記法をcakephp3で表示する

### パーサ

何種類か試した結果、比較的レンダリングが早いParsedownにしました。

- [Parsedown](http://parsedown.org/)

```bash
composer require erusev/parsedown
composer require erusev/parsedown-extra
```

[Parsedown](http://parsedown.org/) と [Markdown Extra](https://michelf.ca/projects/php-markdown/extra/) 両方インストールします。

色々な図が書ける[mermaid.js](https://mermaid-js.github.io/mermaid/) と シンタックスハイライトに

[Google Code Prettify](https://github.com/googlearchive/code-prettify/blob/master/docs/getting_started.md)を使いたいので

ParsedownExtraの[拡張](https://github.com/erusev/parsedown/wiki/Tutorial:-Create-Extensions)クラスを作ります。

&lt;code&gt;のクラスがlanguage-mermaidの時はmermaidに

それ以外のときは&lt;pre&gt;のクラスをprettyprintにします。

```php
<?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を書き加えます。[ここ](https://book.cakephp.org/3/ja/core-libraries/app.html#id5)を参考にして

下さい。

はい、パッケージを作りました。下記でインストールできます。

```bash
composer require adjmpwgt/parsedown-extra-plus
```



ヘルパーを作ります。

```bash
bin/cake bake helper Markdown
```

src/View/Helper/MarkdownHelper.phpが出来ます。

下記のuseを追加します。

```php
use Parsedown;
use ParsedownExtra;
use ParsedownExtraPlus;
```

ファンクションを追加します。

```php
    public function transform($text)
    {
        if (!is_string($text)) {
            return;
        }

        $parser = new ParsedownExtraPlus();
        // $perser->blockPreClassHighlight = 'highlight';
        return $parser->text($text);
    }

```



## Viewに記述

###   スタイルシートはgithub由来のものにする。

```php
// github markdown
$this->Html->css('//cdnjs.cloudflare.com/ajax/libs/github-markdown-css/4.0.0/github-markdown.min.css', ['block' => true]);
```
### 様々な図が表現出来るマーメイドを導入

```php
// 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](https://github.com/googlearchive/code-prettify/blob/master/docs/getting_started.md) を使う場合

下記をview.ctpの先頭に追加して下さい。

```php
// prettify <pre class="prettyprint">
$this->Html->script('//cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js', ['block' => true]);

```

###  [highlight.js](https://highlightjs.org/) を使う場合

下記をヘルパーに追加。

```php
$perser->blockPreClassHighlight = 'highlight';
```

下記をview.ctpの先頭に追加。

```php
// 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にして下さい。

```js
document.querySelectorAll('pre.prettyprint code').forEach((block) =>
```

mermaid.jsを使わない場合は下記でよい。

```php
// 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]);
```



実際のマークダウンの出力は

```php+HTML
  <div class="forumcontent">
    <div class="markdown-body markdown">
      <?= $this->Markdown->transform($forum->description); ?>
    </div>
  </div>
```

[a]: 参照リンク	"localhost"

[^b]: 脚注ですが何か



### 参考ページ

[CakePHP 4 で Parsedown を使い Markdown をパースして HTML に変換する方法](https://tt-computing.com/cake4-parsedown)

[erusev/Parsedownのcode部分のみ簡易拡張](https://qiita.com/59e592291982460/items/5441214a8b6c39669754)

@tomgoodsunさんの
[MarkdownをHTML上に表示するにはどうしたらいいか](https://qiita.com/tomgoodsun/items/018a75be41ac2e106eb1)
>見出しのアンカーリンクが作られていないので、作ってみました。
>[**Parsedownを書き換える**](https://qiita.com/tomgoodsun/items/018a75be41ac2e106eb1#parsedown%E3%82%92%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%8B)

を参考にさせて貰いました。