はじめに
アドベントカレンダー初参加の@ABCanG1015です。
今回はMarkdownでコンテンツを管理できるWebサイトを作ってみた話をします。
Laravel4を使ってます。
これは今自分のサイトの管理に使ってます。
abcang.netを見ながら読むと何を書いてるのかわかりやすいと思います。
コンセプト
1つのページに複数のコンテンツがある場合、コンテンツを追加したり編集したい時は毎回そのページ全体を更新することになります。
名前順に並べたい時も自分で適切な位置に配置してあげなければいけません。
今回はそれを解決できるように以下の3つをコンセプトに作りました。
- 1つのコンテンツにつき1つのMarkdownで管理する
- カテゴリ分けして、名前でソートして表示する
- メニューバーにそれらのコンテンツを表示する
フォルダ構造
フォルダ構造は以下のように設計しました。
site
`-- app
|-- composers.php
|-- controllers
| `-- ContentController.php
|-- routes.php
|-- storage
| `-- data
| |-- contents.md
| |-- hoge
| | |-- about.md
| | |-- bar.md
| | `-- foo.md
| |-- huga
| | |-- about.md
| | |-- bar.md
| | `-- foo.md
| |-- index.md
| `-- updates.md
`-- views
`-- content
|-- index.blade.php
|-- master.blade.php
`-- show.blade.php
Markdownはapp/storage/data
に配置します。
contents.md
にはメニューバーに表示するものを記述します。
例としては以下のようにリスト形式でリンクを作成するだけです。
* [ほげ](./hoge)
* [ふが](./huga)
index.md
やupdates.md
はトップページに表示するためのMarkdownになります。
カテゴリ分けはフォルダを作って、その中にMarkdownファイルを入れる形にしました。
about.md
はそのカテゴリの説明用のMarkdownになっています。
about.md
以外のMarkdownファイルが1つのコンテンツになります。
routes.php
以下のようにトップページはindex
を、それ以外のカテゴリはshow
を呼ぶようにしています。
<?php
Route::pattern('content', '[\w\-]+');
Route::get('/', array(
'as' => 'content.index',
'uses' => 'ContentController@index'
));
Route::get('{content}', array(
'as' => 'content.show',
'uses' => 'ContentController@show'
));
ContentController.php
コントローラーは以下のようにしました。
index
ではただ単に表示しているだけですが、show
ではフォルダ内のMarkdownファイルを読み込んでソートしています。
<?php
use dflydev\markdown\MarkdownParser;
class ContentController extends BaseController {
public $mdp;
public $data_path;
public function __construct()
{
$this->mdp = new MarkdownParser();
$this->data_path = storage_path().'/data/';
}
public function index()
{
return View::make('content/index', array(
'mdp' => $this->mdp,
'data_path' => $this->data_path
));
}
public function show($content)
{
$content_path = $this->data_path.$content.'/';
if (is_dir($content_path)) {
$items = array();
if ($dir = opendir($content_path)) {
while (($file = readdir($dir)) !== false) {
if (ends_with($file, '.md') && $file != 'about.md') {
$items[] = str_replace('.md', '', $file);
}
}
closedir($dir);
sort($items);
}
return View::make('content/show', array(
'mdp' => $this->mdp,
'data_path' => $this->data_path,
'content' => $content,
'content_path' => $content_path,
'items' => $items
));
}
App::abort(404);
}
}
composers.php
メニューバーを表示するときに、もしコンテンツがあった場合はそれらもメニューに加える処理をしています。結構強引な処理をしています…
app/start/global.php
などからロードするようにしておきます。
<?php
View::composer(array('*'), function($view) {
$data = $view->getData();
$contents = file_get_contents(storage_path().'/data/contents.md');
if (isset($data['content'])) {
$array = explode("\n", $contents);
foreach ($array as $key => $val) {
if (ends_with($val, '(./'.$data['content'].')')) {
$items = array();
foreach ($data['items'] as $item) {
$fp = fopen($data['content_path'].$item.'.md', 'r');
$items[] = preg_replace('/^#+ +(.+)\n/', ' * [$1](#'.$item.')', fgets($fp));
fclose($fp);
}
array_splice($array, $key+1, 0, $items);
break;
}
}
$contents = implode("\n", $array);
}
$view->with('contents', $contents);
});
View
show.blade.php
では以下のようにページを生成しています。
<div id="about">
{{ $mdp->transformMarkdown(file_get_contents($content_path.'about.md')); }}
</div>
@foreach($items as $item)
<div class="item" id='{{$item}}'>
{{ $mdp->transformMarkdown(file_get_contents($content_path.$item.'.md')); }}
</div>
@endforeach
index.blade.php
ではindex.md
とupdates.md
を読み込むようにしています。
master.blade.php
ではメニューバーの表示をしています。
おわりに
今回こんな感じで作ってみましたが、結構管理はしやすいです。
現在はコンテンツの部分だけを別のgitのリポジトリとして管理しています。
しかしリポジトリにpushすると自動的にpullして反映する機能は無いので、現在手動でしています。
また、毎回Markdownからページを生成しているので、コンパイルして静的なページを生成とかしてみたいと思いました。
これらの機能は今後の課題という感じです。
明日は@kavao_jpさんです