WinRoadのLaravel4初心者講座

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

Winroad徒然草の管理人がお届けします
開発メモ7_Historyモデルの作成

開発メモ7_Historyモデルの作成

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

データの更新情報を記録するためにhistoriesテーブルを作成することにします。

考察

  1. 過去データの更新履歴を1つのテーブルで管理するか、各テーブルごとの履歴専用テーブルを作成した方がいいのか思案中。
  2. とりあえず全てのプロフィールの更新履歴を保存するためのhistoriesテーブルを作成してから考えることにしよう。
  3. まず、必要な情報を整理してみよう。
    1. id=>increments(‘id’)
    2. 更新者名=>intger(‘user_id’)
    3. プロフィールID=>string(‘profile_id’)
    4. 更新項目名=>integer(‘item_id’)
    5. 更新前データ=>text(‘old’)
    6. 更新後データ=>text(‘new’)
    7. 更新理由=>text(‘reason’)->nullable()
    8. 作成日&更新日=>timestamps()……作成日だけでいいのかな?
    9. ソフトデリート用=>softDeletes()

SetupControllerに追加

SetupControllerにhistoriesテーブルとhistory_profileテーブルの作成を追加しよう。

app/controllers/SetupController.php
/*
|---------------------------------------------
| histories(履歴)テーブルの作成
|---------------------------------------------
| 履歴管理のテーブル
*/
 public function getHistories(){
 //categoriesテーブルの存在確認
 if(Schema::hasTable('histories')){
 $data['warning']='historiesテーブルが存在しますので、処理を中止します。';
 return View::make('setup/index',$data);
 }
 //categoriesテーブルの作成
 Schema::create('histories',function($table){
 $table->increments('id');
 //更新者ID
 $table->integer('user_id');
 //プロフィーID
 $table->integer('profile_id');
 //更新項ID
 $table->integer('item_id');
 //更新前データ
 $table->text('old');
 //更新後データ
 $table->text('new');
 //更新理由
 $table->text('reason')->nullable();
 //created_atとupdated_atの同時作成
 $table->timestamps();
 //ソフトデリート用
 $table->softDeletes(); 
 });
 $data['warning']='historiesテーブルを作成しました。';
 return View::make('setup/index',$data);
 }
app/controller/SetupController.php
/*
|---------------------------------------------
| history_profileピポットテーブルの作成
|---------------------------------------------
| HistoryとProfileテーブル管理用のテーブル
*/
 public function getHistoryProfile(){
 //history_profilesテーブルの存在確認
 if(Schema::hasTable('history_profile')){
 $data['warning']='history_profileテーブルが存在しますので、処理を中止します。';
 return View::make('setup/index',$data);
 }
 //history_profileテーブルの作成
 Schema::create('history_profile',function($table){
 $table->increments('id');
 //更新履歴
 $table->integer('history_id');
 //テーブル
 $table->integer('profile_id');
 //ソフトデリート用
 $table->softDeletes(); 
 });
 $data['warning']='history_profileピポットテーブルを作成しました。';
 return View::make('setup/index',$data);
 }

Historyモデルの作成

app/models/History.php
<?php
class History extends Eloquent{

 protected $softDelete=true;
 protected $guarded=array('id');

/*
|------------------------------------
| 多対多関係のリレーション
|------------------------------------
*/
 public function profile(){
 return $this->belongsToMany('Profile','history_profile');
 }
/*
|------------------------------------
| 1対多関係のリレーション
|------------------------------------
*/
 public function item(){
 return $this->belongsTo('Item');
 }
 public function user(){
 return $this->belongsTo('User');
 }
/*
|---------------------------------------------
| クエリースコープ
|---------------------------------------------
*/
 //ログイン中のユーザーの更新履歴を取得
 public function scopeOwner($query){
 $query->where('user_id','=',Auth::user())
 ->orderBy('created_at','desc')
 ->get();
 }
}

Profileデータ更新用のCreateアクション

app/controllers/ProfileController.php
/*
|----------------------------------------
| データ更新ページ
|----------------------------------------
*/
 public function getUpdateList(){
 $data['profile']=Profile::owner()->first();
 $data['address']=Profile::item('address');
 $data['body']=Profile::item('body');
 $data['license']=Profile::item('license');
 $data['labor']=Profile::item('labor');
 $data['family']=Profile::item('family');
 $data['note']=Profile::item('note');
 return View::make('profile/update-list',$data);
 }

 public function getUpdate($cat,$itm){
 //データの取得
 $data['cat']=$cat;
 $data['itm']=array_only(Profile::item($cat),array('name',$itm));
 //データ更新ページの表示
 return View::make('profile/update',$data);
 }

 public function postUpdate(){
 $cat=Input::get('cat');
 $itm=Input::get('itm');
 //データ受信
 $inputs=Input::except('_token','cat','itm','reason','old_itm');
 //バリルール
 foreach($inputs as $key=>$value){
 $rules=array($key=>'required');
 }
 //バリ処理
 $val=Validator::make($inputs,$rules);
 //バリNGなら
 if($val->fails()){
 return Redirect::back()
 ->withInput()
 ->withErrors($val);
 }

 //旧データと新データが同じで無ければ
 if(Input::get('old_itm') != Input::get($key)){

 /*******************
 * データ更新処理
 ********************/

 //受け取ったカテゴリの配列を取得
 $old_pro=Profile::item($cat);
 //受け取ったデータで旧データに修正
 $new_pro=array_merge($old_pro,$inputs);
 //受け取ったデータをシリアライズ
 $new_pro=serialize($new_pro);
 //受け取ったデータを保存
 $pro=Profile::owner()->first();
 $pro->$cat=$new_pro;
 $pro->save();

 /***********************
 * 更新履歴処理
 ***********************/

 //更新履歴の必要項目取得
 $user_id=Auth::user()->id;
 $profile_id=Profile::owner()->pluck('id');
 $item_id=Item::where('name','=',$itm)->pluck('id');
 //古い項目データの取得
 $old_itm=Input::get('old_itm');
 //新しい項目データの取得
 $new_itm=Input::get($key);
 $reason=Input::get('reason');

 $profile=Profile::find($profile_id);
 //更新履歴
 $history=new History();
 $history->user_id=$user_id;
 $history->profile_id=$profile_id;
 $history->item_id=$item_id;
 $history->old=$old_itm;
 $history->new=$new_itm;
 if(isset($reason)){
 $history->reason=$reason;
 }
 //修正プロフィールを取得
 $profile=Profile::find($profile_id);
 //更新履歴を保存
 $profile->history()->save($history);

 }
 //トップページへ戻る
 return Redirect::to('profile/view/'.Auth::user()->id);
 }

Profileモデルの修正

app/models/Profile.php
<?php
class Profile extends Eloquent{
 protected $softDelete=true;
 protected $guarded=array('id');
/*
|--------------------------------------------
| リレーションの指定
|--------------------------------------------
*/
 public function user(){
 return $this->hasOne('User');
 }

 public function history(){
 return $this->belongsToMany('History');
 }

/*
|--------------------------------------------
| 各項目内の配列を取得する
|--------------------------------------------
| 第2引数がtrueなら、未入力配列をリターンする
*/
 static public function item($item,$empty=false){
 //ログイン中のユーザーの最新情報を取得
 $query=Profile::where('user_id','=',Auth::user()->id)
 ->orderBy('created_at','desc')
 ->first();
 //第2引数がfalseなら項目内の配列をリターン
 if($query->$item != null and $empty==false){
 //アンシリアライズして、配列データを返します。
 $data=unserialize($query->$item);
 return $data;
 //第2引数がtrueなら項目内の未入力配列をリターン
 }elseif($query->$item != null and $empty == true){
 //カテゴリIDの取得
 $cat=Category::where('name',$item)->first();
 //登録アイテムを取得
 $regist_item=Item::where('category_id',$cat->id)->lists('id','name');
 //登録済みアイテムを取得
 $my_item=unserialize($query->$item);
 //登録アイテムから登録済みアイテムを取り除く
 foreach($my_item as $key=>$value):
 if(array_key_exists($key,$my_item)){
 array_pull($regist_item,$key);
 }
 endforeach;
 return $regist_item;
 //項目内が未入力で、第2引数がtrueなら
 }elseif($query->$item == null and $empty == true){
 //カテゴリIDの取得
 $cat=DB::table('categories')->where('name','=',$item)->pluck('id');
 //登録アイテムを取得
 $regist_item=DB::table('items')->where('category_id','=',$cat)->lists('id','name');
 return $regist_item;
 }else{
 //項目がNULLなら
 return null;
 }
 }
/*
|--------------------------------------------
| スコープ
|--------------------------------------------
*/
 public function scopeOwner($query){
 return $query->where('user_id','=',Auth::user()->id)
 ->orderBy('created_at','desc');
 }
}

更新履歴用ビューの作成

とりあえず、レイアウトを考えるのが面倒になってきたので、ほとんど全部リスト<ul>で作成しています。レイアウトは後で考えよう。

app/views/history/index.blade.php
@extends('layouts.f4.admin.base')
@section('content')
<h2>更新履歴</h2>
<ul>
@foreach($profiles as $profile)
 <li>プロフィール : {{ HTML::link('history/view/'.$profile->id,$profile->user->name) }} </li>
@endforeach
</ul>
@stop
app/views/history/view.blade.php
@extends('layouts.f4.admin.base')
@section('content')
@if(count($histories) != 0)
<ul>
 @foreach($histories as $history)
 <li>項目名:{{ Item::find($history->item_id)->name }}</li>
 <li>更新内容:{{ $history->old }}=>{{ $history->new }}</li>
 <li>更新日:{{ $history->created_at }}</li>
 <li>更新者:{{ $history->user->name }}</li>
 <hr>
 @endforeach
 </li>
</ul>
@else
<h2>更新履歴はありません</h2>
@endif
@stop

課題

作成していく上で、いくつかの課題が発生したので下記に記述しておきます。

  1. 今回は、ユーザー自身が更新した場合でプログラムを組んだので簡単だったが、Adminが修正する場合は、ユーザーの特定を間違わないようにする事。
  2. profile内のデータを配列で保存すると、配列内のデータの追加は簡単なのだが、配列内のキー(項目名)の修正がちょっとやっかいだ。値の修正は、各ユーザーごとなのだが、キーは、全ユーザー一斉に変更しなければならない。普通にテーブルで組んだ方が簡単なのだが。(^0^)

とりあえず、あとは、細かい修正を加えれば使用できると思うので、次回からは、Messsage管理用のプログラムの制作にかかろう。

« »

コメントを残す

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