2015.7.28
0からはじめるPHP#37【Laravel5で作るデータベース#11(終)-GUIによるデータベース管理-】
今回で一気に管理画面を制作していきます。
前回の最後に取り上げたように、今回やるテーマは
「管理者情報の変更・更新」「レコードの追加」「レコードの編集・削除」です。
ちなみに、ログインにはIDを使いたいと思っているので
こちらを参考に変えてみたりしました。参考にどうぞ。
そのついでに
メールによるID通知というのも取り入れてみました。
メールじゃなくてIDを忘れてしまった場合のリマインダがないと困りますからね。
とりあえずメール送信機能をつけてみます。
パスワード変更のコントローラーに組み込もうと思ったんですがなんかうまくアクセスしてくれなかったので自前のコントローラーから実装します。
方針としては、メールアドレスを入力させて、そのメールアドレスをもとに、データベースから値を取り出し、メールにてそれを通知する、という形でよさそうですね。
use DB;
use Mail;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class AdminController extends Controller {
public function name_rem(){
self::$inputs = \Request::all();
try{
$db=DB::table('users')//DB読み込み
->where('email', '=', self::$inputs["email"])
->get();
$db=get_object_vars($db[0]);
Mail::send('emails.id', ['db' => $db,], function($message) use($db){
$message
->to($db["email"],$db["name"])
->subject('Musel 管理ID');
});
}
catch(Exception $e){
}
return view('name_remainder');
}
public function name(){
return view('name_remainder');
}
}
簡単に実装するとこんな感じです。
テーブルとメールアドレスが一致すればそれに送ればいいだけです。バリデーションなどは後から入れることにして、とりあえず最初は動くものを、という。
まぁこの例だとバリデーションとか要らないと思いますけどね。
一応、エラーとかを渡せたらいいなということでtry~catch文を使ってますが、上の例では全く有効に活用できていません。後でいろいろいじってみようかとは思ってますけど、とりあえず後回しで。(笑)
メール送信に関しては、16行目のMailファサードにて実装できます。これを使うには2行目でMailファサードをインポートすれば良いことになります。
|
Mail::send('ビュー', ['ビューに渡す変数名' => $ビューにわたすデータ], コールバック関数);
|
基本的な使い方はこんな感じです。
まず、ビューですが、例の書き方だと
views/emails/id.blade.phpが読み込まれることになります。
第二引数は、ビューにwithで渡した時を思い返してもらうと良いかなと思います。あれと恐らく同じです。配列の形で書けばいくらでも渡せるかと思われます。
ただ注意点としては、
オブジェクトを渡そうとするとエラーになるということです。よく分からないので14行目の
get_object_vars()で配列に戻してます。
第三引数は無名関数で指定していますが、変数を使ったほうがエレガントですね。ということで、無名関数に引数を渡す方法として
useというものがあるようなのでそれを使います。
あとはビューをいい感じに書けばこんなんが送れます。この例ではHTMLとか書かずに送信してるんですが、ビューの書き方次第でHTMLメールにもできるようです。まぁ僕はHTMLメールは嫌いですが・・・・・・
表示されないので・・・・・
あ、関係ないですけど前回に
「契約とかの実装がどこでされているのか分からない・・・・・・」って言ってましたが、おそらくその実装は
vendor/laravel/framework/src/Illuminate内のものだと思います。
ちょっと多すぎて解読はできないのですが、必要に応じて少しずつ見ていけば動きはつかめるんじゃないかなと思います。
さて、前置きが長くなりましたが管理画面の実装に入りたいと思います。
1.管理者情報の変更・更新
管理者情報の変更、更新でやるべきことは
「name」「email」「password」カラムの内容変更ですね。
この中でemail変更のみは確認をとらないと割とマズいことになりますので、URLでトークンを送って、それにアクセスすることで変更が完了する、というプログラムにしてみたいところですね。
他はまぁ、変なもの入れても後から調べることができるのでバリデーションだけかけとけばいいでしょう。
とりあえずビューを作ってみましょう。基本的にはパスワードのやつをコピペで項目増やせばいいでしょう。
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}
@endforeach
</ul>
</div>
@endif
変更がある項目のみ入力してください。
<form class="form-horizontal" role="form" method="POST" action="{{ url('/administrator_alter_post') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}"><br>
name<input type="email" class="form-control" name="name" value="{{ old('name') }}"><br>
email<input type="email" class="form-control" name="email" value="{{ old('email') }}"><br>
Password<input type="password" class="form-control" name="password"><br>
Confirm Password<input type="password" class="form-control" name="password_confirmation"><br>
確認のため、現在のパスワードを入力してください。<input type="password" class="form-control" name="old_password"><br>
<button type="submit" class="btn btn-primary">送信</button></form>
ビューはとりあえずこんな感じで。
バリデーションの実装などは
以前やったものを拡張すれば良いとして、新たに知識として取り入れなければならないのはビューにあるヘルパー関数のoldと、あとはエラーの吐き出しですね。
まずヘルパー関数の方から片付けると、Laravelのマニュアルに
HTTPリクエストという項目がありまして、ここに
フラッシュデータをセッションに保存ということをするために
Request::flash()というものを紹介されています。魔法の呪文みたいに使って良い感じですね。たぶん。(笑)
ただ、withInputを用いて実装した方がラクっぽいですね・・・・・・。とりあえずバリデーションとこいつを実装してみます。
全体的なフローチャートとしてはこんな感じです。
1.バリデーションする
2.確認のためのパスワードが合っているかどうか確かめる
3.空入力かどうかを調べる
4.メールアドレスが変更されていたら、確認のトークンを送信し、そうでなければ設定を反映する。
メールアドレスに関しては、ミスがあると復帰できなくなるのでガードを固めます。
で、そのコントローラーの実装としては、こんな感じです。
public function admin(Request $request){
$validator = Validator::make($request->all(),[
'name' => 'max:60',
'email' => 'email|max:200',
'password' => 'min:8|max:100|confirmed'
]);
if (!Hash::check($request["old_password"], Auth::user()->password)){
//パスワードが正しくない
$validator->errors()->add('old_password', 'パスワードが正しくありません');
return redirect()->back()->withInput()->withErrors($validator);
}
if(
empty(self::$inputs["name"])
&&empty(self::$inputs["email"])
&&empty(self::$inputs["password"])
){
//空入力
$validator->errors()->add('name', '入力されていません');
return redirect()->back()->withErrors($validator);
}else{
if ($validator->fails())
{
// 与えられたデーターはバリデーションをパスしなかった
return redirect()->back()->withInput()->withErrors($validator->messages());
}else{
//バリデーション通過
DB::table('users')
->where('id', Auth::user()->id)
->update(['updated_at' => date_format(date_create("now"), 'Y-m-d H:i:s')]);
if(empty($request["email"])){
//メール変更がないのでそのままデータを入れ替える
if(!empty($request["name"])){
DB::table('users')
->where('id', Auth::user()->id)
->update(['name' => $request["name"]]);
}
if(!empty($request["password"])){
DB::table('users')
->where('id', Auth::user()->id)
->update(['password' => Hash::make($request["password"])]);//ハッシュして更新
}
}
else{
//メールアドレスが変更されたので、確認のトークンを送信
$ipad = getenv('REMOTE_ADDR');
$time = time();
$rand = mt_rand();
// 値をハッシュ化
$ipad = hash( 'sha256', $ipad );
$time = hash( 'md5', $time );
$rand = hash( 'md5', $rand );
$no_token = $ipad.$time.$rand;
// トークン生成
$token = hash( 'sha256', $no_token );
DB::table('users')
->where('id', Auth::user()->id)
->update(['confirmation_token' =>$token]);//トークン埋め込み
try{
$inputs=$request;
Mail::send('emails.confirmation', ['token'=>$token], function($message) use($inputs){
$message
->to($inputs["email"],$inputs["name"])
->subject('Musel メールアドレス変更');
});
}
catch(Exception $e){
}
//変更予定箇所をカラムに埋め込む
//確認がめんどくさいので、変更されていない部分もそのまま入れてしまいます。
if(empty($request["name"])){
$request["name"]=Auth::user()->name;
}
if(empty($request["password"])){
$request["password"]=Auth::user()->password;
}else{
$request["password"]=Hash::make($request["password"]);//ハッシュが必要なのでハッシュする
}
$db=DB::table('users')
->where('id', Auth::user()->id);
$db->update(['confirmation_name' =>$request["name"]]);
$db->update(['confirmation_email' => $request["email"]]);
$db->update(['confirmation_password' => $request["password"]]);
$db->update(['confirmation_created_at' => date_format(date_create("now"), 'Y-m-d H:i:s')]);
}
return redirect('/');
}
}
}
こんな感じですかね。もともとコンストラクタでクラス変数にとってたものを使ってたんですが、たぶん$requestでもいけると思うんで変えてます。
動かなかったらごめんなさい
あと65行目の意味がちょっと僕自身よくわからないんですが、たぶんなくても大丈夫です。たぶん。←
58行目以降で生成しているトークンはテキトーにググって拾ったものです。このトークンをデータベースに格納し、このトークンをURLにくっつけて送信し、一致したら処理をする、みたいなことを想定しています。
メールアドレス変更が要請されました。<br>
以下のURLをクリックして変更を確定してください。<br>
有効期限は24時間です。期限が切れていた場合はお手数ですがもう一度変更してください。<br>
<br>
{{ url('email_alter/'.$token) }}<br>
<br>
<br>
※心当たりがない場合はクリックしないでください。その場合、パスワードが漏れている可能性が高いので、変更をお勧めします。
メールのビューはこんな感じです。プレーンメールとして送りたかったんですが、プレーンメールだと改行が認識されないので諦めてbrタグで改行してもらいます。
ま、こんな感じで管理者の情報更新ができました。
詰んだポイントとしては
updateですかね。
DBファサードで生成される、今回の例だと$dbはオブジェクトでして、updateはそのなかのメソッドなわけですね。
これは基本的なことなんですが、検索の時の癖で代入しまくってたらエラーが出たんですよね。
$db=$db->update(['confirmation_name' =>$request["name"]]);ってしちゃってたんですよね。これ単体だと動いてしまうってのが気づくのに遅れた原因なんですが、おそらくこれだと$dbにTRUEが代入されることになるんですかね。実装を知らないので何がreturnされるか分からないんですが、まぁコアを読めば分かるでしょうし、読まなくてもvar_dump()でもしておけば何が出てくるかぐらいは見えるでしょう。(笑)
あと、若干苦し紛れ感が強いんですが、
オリジナルの警告文を表示するために19行目でerrors()にaddを与えています。
これでwithErrorsでビューに文章を渡すことができるようです。第一引数はエラーを与えているフォーム部品の名前なんですが、この場合何にしてもいいと思うんで、とりあえずnameに与えてますが別に他でも一緒だと思います。
メールのトークンを踏ませてからの処理は、3.で解説するルートパラメータの話が分かれば後はデータベースを書き換えるだけの簡単な話なので解説は割愛します。(笑)
2.レコードの追加
基本は一緒です。updateがinsertになるだけです。
$this->validate($request, [
'selector' => 'required|integer',
'countries' => 'required|string|max:50|unique:countries,country',
]);
$db=DB::table('countries');//DB取得
if($request["selector"]==0){
//新規
$insert=[
"country"=>$request["countries"]
];//挿入する配列
$db->insertGetId($insert);//データ挿入
}else{
//更新
・・・・・・
}
全然違う場所のコードなんですが、こんな風に$insertを作って、挿入してやればいいです。上の例だとカラムが1つだけなので配列じゃなくてもいいと思いますけど見やすいので。
っていうか別の場所からコピペした結果です
大体の場合はオートインクリメントIDがついてると思うので、その場合は
insertGetIdというものを使えば勝手にやってくれます。
3.レコードの更新・削除
ここでの特異な点は、ルートパラメータを指定することですね。
どういうことかというと
URLにIDをつけることで扱うデータを区分できるっていうやつですね。
例えば
http://.../public/data/5なんていうURLを渡すと、ルーティングはdataになりますが、同時に"5"というパラメータを渡すことができる、ってことですね。
Route::group(['middleware' => 'auth'], function(){
Route::post('record_delete/{id}', 'YourController@record_delete');
});
こんな感じです。URLにアクセスするだけで消されたら困るので、認証されていない状態では認証ページに飛ぶようにしています。
あと、変なデータ送られても困るのでpostにしてます。HTTP動詞に関してはちょっと良くわからないんですが、PUTとDELETEというのもあるみたいです。何ができるんだろう・・・・・・?
とりあえず、上の例だとrecode_delete/???にアクセスすると、データベース上のIDが???のカラムが削除される、っていうメソッドを実装できます。
ちなみに、このパラメータは変数名の後に?をつけることで可変にすることもできるので、idが渡されなかった場合の処理も別途、作ろうと思えば作れますね。
public function record_delete($id)
{
DB::table('yourdb')->where('composition_id', '=', $id)->delete();
return redirect('index');
}
削除自体は2行でできます(笑)
心配な方はここで認証を挟んでもいいかも知れませんけど、たぶん大丈夫だと僕は思います。
最後は説明がめんどくさくなって駆け足になりましたが、これでLaravel5を用いたデータベースを運用できる程度の知識は手に入ります。
実際にオケのページにアップロードして動かしてます。アップロードした結果、PHPの互換性によるバグがでてきたんですが、まぁそれはまた別の話なのでおいときます。修正もそんなに難しくないし。(笑)
このPHPお勉強シリーズは個人的にもっと続けたいと思ってるので、次回からはwebサイトの設計をしてて
javascriptって意外と重要だってことに気づいたということで、
jQueryにちょっと手を出してみようかと思います。