今回は掲示板にスレッド機能を実装していきたいと思います。
スレッド機能といっても
- 新規スレッドを作成・既存スレッドに返信ができる
くらいのものです。
親スレッドとレスポンスのインデントをつける
現在bootstrapのpanelを用いて記事の表示を行っているのですが、marginをいじってやることで親スレッドとレスポンスにインデント(パネルの位置ずらし)をつけることにします。
@extends('layout')
@section('content')
<post>
<div class="panel panel-default">
~~
</post>
<post>
<div class="panel panel-default" style="margin:0 0 0 60px">
~~
</post>
@endsection
以下のように、親スレッドとレスポンスにインデントをつけて区別することができました。
返信機能の実装
上の例では親スレッドとレスポンスの内容が全く一緒なので、全然面白くありません。
ここではレスポンス機能の実装を行っていきます。
レスポンス用にテーブルを修正
現在は記事一覧を格納しておくpostsテーブルのみを利用しています。
Schema::create('posts', function (Blueprint $table) {
$table->increments('id'); //インクリメントID*
$table->integer('post_id'); //スレッドID 追加
$table->integer('res_id'); //スレッド内でのID 追加
$table->string('contributor'); //投稿者名
$table->string('title'); //タイトル
$table->string('body'); //本文
$table->string('fig_name')->nullable(); //画像の名前
$table->binary('fig_orig')->nullable(); //画像ファイル
$table->binary('fig_thumbnail')->nullable(); //サムネイル
$table->timestamps(); //投稿日時・編集日時
});
記事一覧ビューではres_id=0の記事のみ表示、記事詳細ビューではpost_idが一致する記事をres_id順に表示、という形で試しに実装してみます。
新規記事作成で作成した場合、post_id = id, res_id = 0 として投稿する
createビューに hidden type で post_idを-1として送信します。
~~
{!! Form::open(['route' => 'posts.store', 'files' => true]) !!}
<input type="hidden" name="post_id" value="-1" > //追加
@include('posts.form', ['title' => null, 'submitButton' => 'Add post'])
{!! Form::close() !!}
~~
既存スレッドに返信した場合、post_id = 親スレッドのpost_id, res_id += 1 としていく
showビューに記事作成フォームを追加し、post_idをその詳細記事のpost_idに設定し、送信します。
また、前回からの変更点としてスレッド形式にしたため、foreachを使い、同スレッド内の投稿内容を投稿日時順に表示するようにしました。
@extends('layout')
@section('content')
@foreach($posts as $post)
<post>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
【{{ $post->post_id }}】
【{{ $post->res_id }}】
<a href="{{url('posts', $post->id) }}">
<font size="5" color="ff0000"><b>{{ $post->title }}</b></font>
</a>
投稿者名: {{ $post->contributor }}
投稿日時: {{ $post->created_at }}
</h2>
</div>
<div class='panel-boby'>
<div style="padding:10px">
{{ $post->body }}
</div>
</div>
{{-- Image view start --}}
@if (!empty($post->fig_orig))
<?php
$image_base64 = base64_encode($post->fig_orig);
?>
<div style="padding:10px">
<img src='data:img/png;base64,{{$image_base64}}'>
</div>
@endif
{{-- Image view end --}}
{!! link_to(action('PostsController@edit', [$post->id]), '編集', ['class' => 'btn btn-primary']) !!}
<br/>
{!! delete_form(['posts', $post->id]) !!}
</div>
</post>
@endforeach
<hr/>
{!! Form::open(['route' => 'posts.store', 'files' => true]) !!}
<input type="hidden" name="post_id" value= {{ $posts[0]['post_id'] }} >
@include('posts.form', ['title' => "Re:".$posts[0]['title'], 'submitButton' => 'Add post'])
{!! Form::close() !!}
@endsection
post_idは $posts[0]['post_id']
、 スレッドへの返信ではタイトルのデフォルト値を"Re:".$posts[0]['title']
としています。
コントローラーの修正
受け取ったpost_idが -1 か それ以外によって処理を分けます。
~~
public function index() {
$posts = Post::where('res_id', '=', '0') //res_idが0の投稿のみを取得し、indexビューに送る
->latest('updated_at')
->get();
return view('posts.index', compact('posts'));
}
public function show($id){
$posts = Post::where('post_id', '=', $id)->get(); //同スレッドのレスポンスをすべて取得し、showビューに送る
return view('posts.show', compact('posts'));
}
public function store(PostRequest $request){
$this->post = new Post();
$this->post->fill($request->all());
// 変更点 ------
$tmp_post_id = $_POST['post_id'];
if ( $tmp_post_id == '-1') { //新規スレッド作成
$this->post->post_id = Post::max('post_id') + 1;
$this->post->res_id = 0;
} else {
$this->post->post_id = $tmp_post_id;
$this->post->res_id = Post::where('post_id', '=', $tmp_post_id)->max('res_id') + 1;
}
//-------------
$image = Input::file('data');
if(!empty($image)) $this->post->fig_orig = file_get_contents($image);
$this->post->save();
\Flash::success('記事が投稿されました。');
if ( $tmp_post_id == '-1') { //新規作成
return redirect()->route('posts.index');
}
return redirect()->route('posts.show', [$tmp_post_id]);
}
~~
最後のほうで、redirect先を変更しています。
まとめ
なんとかスレッド掲示板っぽく出来ましたが、急ごしらえ感がすごいです。
将来的には
- スレッド用のテーブルとレスポンス用のテーブルに分けて管理する
- indexビューに、最新3件までのレスポンスを表示する
くらいにはしたいです。