WinRoadのLaravel4初心者講座

次世代PHPフレームワークのLaravel4を初心者向けに解説して参ります。

Winroad徒然草の管理人がお届けします
開発メモ10_MessageControllerの作成

開発メモ10_MessageControllerの作成

このエントリーをはてなブックマークに追加

今日は、メッセージ管理の基本となるMessageControllerを作成したいと思います。

  • 当初、messagesテーブルには、グループ(ロール)宛てのメッセージ用をrole_idで管理しようと作成していましたが、計画変更で、role_nameを配列(シリアライズ)で保存することにしました。
  • 前回のSetupControllerのmessagesテーブル作成の19行目は下記に変更します。
    $table->text(‘role_name’)->nullable();

それでは、MessageControllerを下記のように作成します。ちょっと長いので、分解して表示しています。

app/controllers/MessageController.php

<?php
 class MessageController extends BaseController{
 /*
 |----------------------------------------
 | コンストラクター
 |----------------------------------------
 */
 public function __construct(){
 //authフィルター
 $this->beforeFilter('auth');
 //全POSTにcsrfフィルターの適用
 $this->beforeFilter('csrf',array('on'=>'post'));
 }
//ユーザー受信メッセージ
 protected function own(){
 $user=Auth::user();
 $role_id=DB::table('role_user')
 ->where('user_id',$user->id)
 ->lists('role_id');
 $role_names=Role::whereIn('id',$role_id)->lists('name');
 $query=Message::where('recipient_id',$user->id)
 ->orderBy('created_at','desc');
 foreach($role_names as $role_name):
 $query->orWhere('role_name','LIKE',"%$role_name%");
 endforeach;
 $query->where('user_id','<>',$user->id);
 return $query;
 }
 //ユーザー送信メッセージ
 protected function sent(){
 $user=Auth::user();
 $query=Message::where('user_id',$user->id)
 ->orderBy('created_at','desc');
 return $query;
 }
 //カラム内の配列の取得
 protected function order($column){
 $work=Work::find(Auth::user()->id);
 $order=count($work->$column == 0) ? unserialize($work->$column) : array();
 return $order;
 }
  •  とりあえず、MessageController内で使用するメンバ関数を作成しました。
  • own()は、ログインユーザーの受信メッセージを取り出すために作成しています。
  • 4-7行目:EloquentORMでは、簡単にログインユーザーのrole_nameを取り出せなかったので、クエリービルダーで取り出しました。
  • 10-12行目:現状では、ユーザーごとにRoleも一つで管理しようと考えていますが、後々ユーザーに複数のRoleを与えることになった場合の為に作成しています。
  • 13行目:これを入れておかないと、自分で送ったメッセージが自分へ届くことになります。
  • 17-23行目:sentメソッドは、特に作るほどのことでもないのですが、own()のついでに作りました。
  • 24-28行目:worksテーブルのカラム内に配列を保存することが多いので、簡単に、取り出すために作成しました。
/*
 |------------------------------------
 | TOPページ
 |------------------------------------
 */
 public function getIndex($action=null){
 if($action == 'sent'):
 $messages=$this->sent()->paginate();
 $data['title']='送信メッセージ一覧';
 $data['action']='sent';
 else:
 //ユーザーの受信メッセージを取得
 $messages=$this->own()->paginate();
 $data['title']='受信メッセージ一覧';
 $data['action']=null;
 endif;
 $data['messages']=(count($messages) != 0) ? $messages : null;
 return View::make('message/index',$data);
 }
  •  TOPページは、メッセージの一覧表示用に使用しますが、受信メッセージと送信メッセージは、全く同じビューで表示しますので、引数で、受信と送信を変えるようにしました。つまり、message/indexで受信メッセージ、message/index/sentで送信メッセージを表示します。
  • 8、13行目:先ほど作成したメンバ関数を呼び出し、ページネーション処理します。
  • 17行目:$messagesは呼び出したのがオブジェクトなので、isset()では、trueになりますので、coutn()で、該当メッセージがあるかどうかを判断しました。
/*
 |------------------------------------
 | 新規メッセージ作成
 |------------------------------------
 */
 public function getCreate($action=null){
 if($action == 'user'){
 $recipi=User::all()->lists('name','id');
 $data['user']=array_except($recipi,array(1));
 $data['title']='個人宛メッセージ';
 $url='/user';
 }elseif($action == 'role'){
 $roles=Role::all()->lists('name','id');
 $data['roles']=array_except($roles,array(1));
 $data['title']='グループ宛メッセージ';
 $url='/role';
 }else{
 $roles=Role::all()->lists('name','id');
 $data['roles']=array_except($roles,array(1));
 $data['title']='全体メッセージ';
 $url='/create';
 }
 return View::make('message'.$url,$data);
 }
  • 8行目:全受信ユーザーのリストを作成します。
  • 9行目:Super Adminは、業務に絡むことはありませんので、リストから除外しました。
  • 13行目:Roleリストを作成します。
  • 14行目:RoleリストからもSuper Adminは除外しました。
  • Createメソッドは、引数で、アクションごとに分けました。http://ドメイン名/message/userでは、個人宛のメッセージ作成、message/roleでは、ロール(グループ)ごとのメッセージ作成、引数指定が無い場合、全体メッセージのビューへ移動します。
  • このビューも同じビューで代用できますが、見づらくなりますので、別々のビューとして作成しました。別々ビューで作成する場合、$data[‘title’]は、不要ですが(^0^)
  • グループ宛メッセージとRoleメッセージは全く同じです。違うのは、デフォルトでRoleが全選択されているのとされていないだけです。
/*
|------------------------------------
| 新規メッセージ作成POST処理
|------------------------------------
*/

public function postCreate(){
$inputs=Input::all();
$rules=array(
'subject'=>'required',
'body'=>'required',
);
//バリデーション
$val=Validator::make($inputs,$rules);
if($val->fails()){
return Redirect::back()
->withInput()
->withErrors($val);
}
//データ登録
$role_name=Input::get('role_name');
$role_name=isset($role_name) ? serialize($role_name) : null;
$inputs['role_name']=$role_name;
$message=Message::create($inputs);
  •  7-19行目:POST処理の一連の流れは特に問題は無いと思います。データを受け取ってバリデーション処理するだけです。
  • 21-23行目:データ登録の時に、受け取ったrole_nameをシリアライズ処理して、$inputs[‘role_name’]に差し替えています。
/*****************************************
* 未読処理
* worksテーブルのmessage項目に配列保存
*****************************************/

$recipient_id=Input::get('recipient_id');
//個人宛メッセージなら
if(isset($inputs['recipient_id'])){
//受信メッセージの整理
$work=Work::find($recipient_id);
//旧メッセージ
$old=isset($work->message) ? unserialize($work->message) : array();
//新メッセージ
$new=array($message->id);
//配列の併合
$merge=!isset($old) ? array_merge($old,$new) : $new;
//登録データの保存
$work->message=serialize($merge);
$work->save();
  •  ここは、個人宛メッセージのworksテーブルへの登録処理になります。worksテーブルは、各ユーザーの未読メッセージ、未読コメント、未処理Todoを保存するために作成しています。
  • 8行目:各Createビューから、送られてきたデータにrecipient_idがあるかどうかで、個人宛かRole宛てかを判断しています。
  • 12行目:旧メッセージをworksテーブルのmessageカラムから取り出しアンシリアライズ処理します。
  • 16-19行目:旧メッセージと新メッセージを併合します。旧メッセージがなければ、新メッセージだけをシリアライズ処理して保存します。
/*****************************************
* メール送信
*****************************************/

$data['recipient']=User::find($recipient_id)->name;
$data['sender']=Auth::user()->name;
$data['title']=Input::get('subject');
$data['body']=Input::get('body');
Mail::send('emails.user.message',$data,function($m){
$user=User::find(Input::get('recipient_id'));
$email=$user->email;
$name=$user->name;
$m->to($email,$name)->subject('Builwing通信');
});
  • メール送信処理をします。個人宛てメッセージには、自動的にメール送信するように作成しました。
  • 5-8行目:メール送信用のビューファイルに送るためのデータです。
  • 10-12行目:ユーザーのメールアドレスとユーザー名を取得するための処理です。
  • ※当たり前のことですが、Mail::send()メソッド内には、外部(Createメソッド内)の変数は使えません。(^0^)。私も最初、勘違いをして、$mailと$nameを5-8行目あたりに、記述して、メールが送れずに悩みましたので、一言書いておきます。
//Role宛てメッセージなら
}else{
//新メッセージ
$new=array($message->id);
//指定ロールIDを取得
$role_id=Role::whereIn('name',Input::get('role_name'))->lists('id');
//指定ユーザーIDを取得
$user_id=DB::table('role_user')
->whereIn('role_id',$role_id)
->lists('user_id');
//指定ユーザーを取得
$users=User::whereIn('id',$user_id)->get();
//ユーザーの数だけ繰り返し
foreach($users as $user):
//旧メッセージの取得
$work=Work::find($user->id);
$old=isset($work->message) ? unserialize($work->message) : array();
//配列の併合
$merge=isset($old) ? array_merge($old,$new) : $new;
//登録データの保存
$work->message=serialize($merge);
$work->save();
endforeach;
  • Role宛てに送るメッセージの未読処理です。
  • 4行目:worksテーブルの各カラムには、idの配列しか登録していませんので、新しく作成したメッセージのidを取得します。
  • 6-12行目:送られてきたrole_nameの配列からユーザーを取得する為の処理です。
  • 6行目:結構whereInメソッドは重宝します。まず、rolesテーブルのnameカラムからgetで取得したrole_nameの配列内に含まれるname(ロール名)をidでリスト化します。
  • 8-10行目:そのロールのidリストからユーザーのidリストを取得しています。Eloquent ORMでもっと簡単にできるのかも知れませんが、私にはこれが一番簡単でした。
  • 試していませんが、9-10行目の間に->distinct()の重複処理が必要かも知れません。
  • 14-23行目:取得したユーザーの数だけ未読データの保存処理をします。
/*****************************************
* メール送信
*****************************************/
if(Input::get('mail') == 1){
//メール送信手続き
$data['recipient']=Role::find(Input::get('role_name'))->name;
$data['sender']=Auth::user()->name;
$data['body']=Input::get('body');
Mail::send('emails.user.message',$data,function($m){
//指定ロールIDを取得
$role_id=Role::whereIn('name',Input::get('role_name'))
->lists('id');
//指定ユーザーIDを取得
$user_id=DB::table('role_user')
->whereIn('role_id',$role_id)
->lists('user_id');
//指定ユーザーを取得
$users=User::whereIn('id',$user_id)
->get();
//ユーザーの数だけ繰り返し
foreach($users as $user):
$m->cc($user->email,$user->name);
endforeach;
$m->subject(Input::get('subject'));
});
}
}
//トップページへ移動
return Redirect::to('message/index');
}
  • Role宛てのメール処理です。
  • 4行目:ビューにメールを送信するかどうかの判定用のラジオボタンを設置しました。
  • 6-8行目:メール送信用のビューに送るデータです。
  • 9-25行目:メールの送信処理です。これもworksテーブルとの処理同様、受け取ったrole_nameの配列からユーザーを取得してユーザーの数だけccに追加してメールを送信しています。
 /*
|------------------------------------
| 未読メッセージ
|------------------------------------
*/
public function getUnread($id=null,$key=null){
//ログインユーザーのWorkオブジェクトを取得
$work=Work::find(Auth::user()->id);
//未読メッセージのID配列取得
$message=$this->order('message');
//未読メッセージがなければ
if(count($message)== 0 or $message == null){
return View::make('message/unread')
->with('warning',' 未読メッセージはありません');
}
//指定IDが未読メッセージの中にあれば
if(isset($id) and in_array($id,unserialize($work->message))){
//未読メッセージIDの配列を取得
$unread=unserialize($work->message);
//未読メッセージを削除
array_pull($unread,$key);
//配列のキーを前に詰める
$unread=array_values($unread);
$message=serialize($unread);
$work->message=$message;
$work->save();
//削除後に明細ページへ移動
return Redirect::to('message/view/'.$id);
}
//配列の数だけオブジェクトを取得
foreach($message as $key=>$value):
$data['message'][$key]=Message::find($value);
endforeach;
return View::make('message/unread',$data);
}
  • Role(グループ)宛て、メッセージの未読処理です。
  • getUnreadメソッドは、2つの引数を持っています。第1引数が、メッセージのID、第2引数が配列のキー番号です。
  • 10行目:最初の方で作成したメンバ関数order()を使って、worksテーブルのメッセージカラムから配列を取得しています。
  • 17-21行目:ビューより指定した$idがユーザーのworksテーブルのメッセージカラム内に配列として保存されていたら、array_pullヘルパー関数で未読メッセージのIDを削除して、既読処理をします。
  • 23行目:この処理を施さないと、配列のキーに隙間ができるので、ビューで指定するキー番号と保存されているキー番号が一致しなくなります。
  • worksテーブルのmessageカラム内にある配列の数だけメッセージオブジェクトを取得します。
/*
|------------------------------------
| 明細ページ
|------------------------------------
*/
public function getView($id=null){
$own=$this->own()->where('id',$id)->get();
if($own->count() == 0 ):
$data['title']='指定のメッセージはありません';
$data['action']=null;
return View::make('message/index',$data);
endif;
$data['message']=Message::find($id);
$data['title']='メッセージ明細';

return View::make('message/view',$data);
}
  • viewメソッドで、明細ページを表示します。
  • 7-12行目:ユーザー所有のメッセージの中から引数で指定したidのオブジェクトを取得します。この処理は、ブラウザからid番号で指定したときにユーザーが受け取っていないメッセージの表示を防ぐために作成しました。この処理を行っていないと、他人宛のメッセージをのぞき見ることができます。
/*
|------------------------------------
| メッセージ検索
|------------------------------------
*/
 public function postSearch(){
 $input=Input::get('search');
 $action=Input::get('action');
 if($action == 'sent'):
 $data['messages']=$this->sent()
 ->where('subject','LIKE','%'.$input.'%')
 ->paginate();
 $data['title']='送信メッセージ検索';
 $data['action']='sent';
 else:
 $data['messages']=$this->own()
 ->where('subject','LIKE','%'.$input.'%')
 ->paginate();
 $data['title']='受信メッセージ検索';
 $data['action']=null;
 endif;
 return View::make('message/index',$data);
 }
  • メッセージ検索するための処理です。
  • ここで大事なのが、やはり10行目と16行目の処理です。この処理を行っていないと、自分宛でないメッセージまで見ることができます。
  • 社内サイトは、表示ページとかに関する事は、基本的にユーザー専用ページとAdmin専用のページですので、難しくはないのですが、一番気を付けないといけないのは、個人情報の点です。
  • とりあえず簡単な曖昧検索で作成しましたが、複合語の曖昧検索とかができるように作り替える予定です。

Messageの各ビュー

ビューは特に難しくはありませんが、indexビューと全体宛てのメッセージ作成用のビューを記述しておきます。

app/views/message/index.blade.php

@extends('layouts.f4.user.base')
@section('content')
<div class='row'>
<div class='large-8 column'>
@if(isset($title))
 <h4>{{ $title }}</h4>
@endif
</div>
<div class='large-4 column'>
<div class='row collapse'>
 {{ Form::open(array('url'=>'message/search')) }}
 <div class="large-9 small-6 columns">
 {{ Form::text('search','',array('placeholder'=>' メッセージ検索')) }}
 </div>
 <div class="large-3 small-6 columns">
 {{ Form::submit('検索',array('class'=>'button small')) }}
 </div>
 {{ Form::hidden('action',$action) }}
 {{ Form::close() }}
 </div>
</div>
</div>
@if(isset($messages))
<table width="100%" border="0">
<tr>
 <th>タイトル</td>
 <th>送信者</td>
 <th>日付</td>
</tr>
 <tr>
@foreach($messages as $message)
 <td width="70%">{{ HTML::link('message/view/'.$message->id,$message->subject,array('class'=>'button expand')) }}</td>
 <td width="12%">{{ User::find($message->user_id)->name }}</td>
 <td width="12%">{{ date('n月j日',strtotime($message->created_at)) }}</td>
 </tr>
@endforeach
</table>
<h5>{{ $messages->links() }}</h5>
@endif
@stop

laravel031

app/views/message/create.blade.php

@extends('layouts.f4.user.base')
@section('content')
{{ Form::open(array('class'=>'custom'))}}
<div class="panel">
<h3>{{ $title }}</h3>
<p>全体メッセージは、メールを送信するかどうかチェックを入れてください。</p>
</div>
<div class="large-4 columns">
<h4>{{ Form::label('送信先部署') }}</h4>
<?php $i=0;?>
<h5>
@foreach($roles as $role)
{{ Form::checkbox("role_name"."[$i]",$role,true) }} {{ $role }}<br>
<?php $i++;?>
@endforeach
</h5>
</div>
<div class="large-8 columns">
<h4>{{ Form::label('タイトル') }}</h4>
{{ Form::text('subject','',array('style'=>'ime-mode:active')) }}
 @if($errors->has('subject'))
 <div data-alert class="alert-box alert radius">
 {{ $errors->first('subject') }}
 </div>
 @endif
<h4>{{ Form::label('内容') }}</h4>
{{ Form::textarea('body','',array('style'=>'ime-mode:active')) }}
 @if($errors->has('body'))
 <div data-alert class="alert-box alert radius">
 {{ $errors->first('body') }}
 </div>
 @endif
 <br>
</div>
<div class="row">
 <div class="large-12 columns">
 <h5>{{ Form::radio('mail',1,true) }} メール送信</H5>
 <h5>{{ Form::radio('mail',0) }} メール未送信</h5>
 </div>
</div>
<div class="row">
 <div class="small-6 large-centered columns">
 {{ Form::submit('メッセージ送信',array('class'=>'button')) }}
 </div>
</div>
{{ Form::hidden('user_id',Auth::user()->id) }}
{{ Form::close() }}
@stop
  • 10-15行目:送信先Roleをチェックボックスで選択するように作成しました。
  • これで、role_nameが配列データとしてCreateアクションに送られます。
  • 受け取ったCreateアクションでは、role_nameカラムにシリアライズ処理して保存します。
  • 当初、role_idを保存して作成していましたが、各ユーザーがRole一つだけならいいのですが、Roleを複数所有するとなると検索が面倒になりそうでしたので、role_nameで保存することにしました。

laravel030

後は、メッセージの削除とそれに伴う、worksテーブルの未読該当データの削除処理あたりですが、この辺はあまり急ぎませんので後日作成します。

本日は以上です。

« »

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です