@半径とことこ60分

人間の認知範囲なんてそんなもんさと、鳥が囀った

プログラマーじゃないけどLaravel4入門(11)blogをつくる

ブロックに module を表示する

想定しているのは、最新記事とカテゴリ一覧ですが、考え方としては、

  1. ビューレンダー時に、タイプ module のブロックがきたら、別ファイルとして用意したモジュールファイル latest_articles.blade.php (categories_list.blade.php) を @include する
  2. モジュールファイルは、ビューコンポーサーという機能を使ってデータと結びつけておく
  3. ビューコンポーサーは、app/composers.php にまとめて定義し、app/start/global.php で require する
    (参考)Laravel4でView Composerの設定をする場所 - atijust's blog

これでなんとかいけそうですが、その前に、ブロックの登録、保存も少し見直した方がよさそうです。

  1. ブロックのタイプ名見直し
    content -> static、module -> dynamic
  2. 要素 textarea の name 属性変更、および blocks テーブルのカラム名変更
    value -> contents
  3. タイプ dynamic は、保存されたモジュールファイル名からリストを作成し、select 要素で選択できるようにする
  4. ブロック作成の初期ページでは、select、textarea ともに非表示とし、タイプ選択 radio をクリックした場合に、該当要素を表示する
  5. ブロックのタイプにかかわらず、内容は、blocks テーブルの contents に保存する

 

blocks テーブルのカラム名変更

DB:スキーマビルダー

Schema::table('blocks', function($table)
{
  $table->renameColumn('value', 'contents');
});

でいけますが、その前に doctrine/dbal を入れておく必要があるようです。composer.json

"doctrine/dbal": "2.3.5"

を追加して

$ composer update

php artisan migrate

とすれば、カラム value が contents に変更されます。app/models/Block.php のバリデーションルールも変更しておく必要があります。

モジュールリストをファイル一覧から取得する

モジュールリストの作成には新しいクラスを作った方がよさそうです。モジュールのビューファイルは、app/views/modules に置くこととして、

class Helpers {

  const MODULE_PATH = '/app/views/modules';

  public static function getModuleNames()
  {
    $path = base_path() . self::MODULE_PATH;
    $files = File::files($path);
    foreach($files as $file)
    {
      $line = fgets(fopen($file, 'r'));
      $m_name = preg_replace('/[!-\/:-@≠\[-`{-~]|\r|\n/i', '', $line);
      $m_file = preg_replace('{' . $path . '/(((?!\.).)*).*$}', '$1', $file);
      $modules[$m_file] = $m_name;
    }
    return $modules;
  }
}

ファイルの一行目には、モジュールの名前を <!----> でくくって入れておき、正規表現で取りだしています。ファイル名は、ディレクトリからファイル一覧を取得し、こちらも正規表現で必要部分のみ取りだしています。

この際、変数 $path にはエスケープすべき文字 '/' が含まれていますので、当然エラーになってしまいます。どうするんだろうと悩んだんですが、'//' の代わりに '{}' も使えるんですね。下のサイトを参考にさせていただきました。勉強になります。

preg_match() に変数を入れたら”/”でエラーが出てしまう場合の解決法

で、作成したクラスはどこに置けばいいんだろう?ということですが、composer のオートロードを利用すればいいようです。

Laravel4のオートロード

自作のクラスを app/helpers.php として保存、composer.json

"autoload": {
  "classmap": [
    (略)
  ],
  "files": [
    "app/helpers.php"
  ]
},

files 以下を追加し、$ composer dump-autoload を実行すれば使えるようになります。

バックエンド/ブロック新規作成、編集の変更

コードは ausnichts/laravel4-blog · GitHub にあります。新規作成の初期画面は、

f:id:ausnichts:20140925164331p:plain

タイプ static をクリックすると、

f:id:ausnichts:20140925164408p:plain

タイプ dynamic をクリックすると、

f:id:ausnichts:20140925164441p:plain

となります。jquery の slideUp()、slideDown() を使っています。

で、結構苦労したのが、dynamic の場合に select の値を contents に代入する方法です。textarea の値を取り出したり、代入しようとしても全く反映されません。

原因は CKEditor でした。
CKEDITOR.instances.editor.getData()
CKEDITOR.instances.editor.setData()
を使わないと操作できないことが分かりました。

ビューコンポーサー

app/composers.php

View::composer('modules.latest_articles', function($view) {
  $view->latest_articles = Article::where('is_published', TRUE)->orderBy('published_at', 'DESC')->take(5)->get();
});
View::composer('modules.categories_list', function($view) {
  $view->categories_list = Category::all();
});

最新記事の表示数も行く行く変更できるようにしたいですね。

モジュールビューファイル

app/views/modules/categories_list.blade.php

<!--Display Categories-->
<ul>
@foreach($categories_list as $category)
  <li><a href="{{ URL::to('category-index', array('id'=>$category->id)) }}">{{ $category->category }}</a></p>
@endforeach
</ul>

 app/views/modules/latest_articles.blade.php

<!--Latest Articles-->
<ul>
@foreach($latest_articles as $article)
  <li><a href="{{ URL::to('article', array('id'=>$article->id)) }}">{{ $article->title }}</a></li>
@endforeach
</ul>

index.blade.php など、ビューファイルの sidebar 部分

@foreach($blocks as $block)
<div class="sidebar-module sidebar-module-inset">
  @if($block->type)
  <h4>{{ $block->title }}</h4>
    @include('modules.' . $block->contents)
  @else
  <h4>{{ $block->title }}</h4>
  {{ $block->contents }}
  @endif
</div>
@endforeach

ということで、こんな感じになりました。

f:id:ausnichts:20140925171250p:plain

css がオリジナルのままなので見た目は良くありませんが、まあなんとかブログらしくなってきました。後は、ブログタイトルなどの設定画面で変更できるようにしたいですね。