メール送信があまりにも重い処理なので、ハンドシェイク関連は全部キューにすることにしました。(笑)
まぁこんな手戻りばっかやってるからいつまで経っても完成しないんですけどね・・・・・・。一回作った処理に関して作り直しを何回も何回も繰り返している気がするんですよね・・・・・・。
まぁでも、こーいうのは作ってみないとわかんないこともあるということで諦めることにいたしましょう(笑)
ハマったところをいくつか。
まず、.envをちゃんと設定しとかないとrouteが正しくとれません。じゃあなんで今まで動いてたのかってのは理由は分からないんですけど、cronはURLとかの情報なしで動いてるわけで、相対パスとか使えないのが原因なのかなぁとかは思います。
ただ、.envのAPP_URLでしたっけ。まぁそこをちゃんとドキュメントルートにすればちゃんとしたURLがrouteで出力できると思います。
デバッグの方法としては、そのままだと出力が捨てられてしまってやりにくいので、外部ファイルに出力するようにいたします。
protected $commands = [
'App\Console\Commands\Handshake_start',
'App\Console\Commands\Handshake_accept',
'App\Console\Commands\Handshake_deny',
'App\Console\Commands\Handshake_deadline',
];
protected function schedule(Schedule $schedule)
{
//終了系(これを最初にしないと、開始メールが送信されることがあると思う)
$schedule->command('handshake_accept')->everyMinute()->withoutOverlapping()
->sendOutputTo("/Users/a/Documents/TheRong'sCaravan/php/Livvon/storage/logs/laravel-cron-accept.log");
$schedule->command('handshake_deny')->everyMinute()->withoutOverlapping()
->sendOutputTo("/Users/a/Documents/TheRong'sCaravan/php/Livvon/storage/logs/laravel-cron-deny.log");
//開始系
$schedule->command('handshake_start')->everyMinute()->withoutOverlapping()
->sendOutputTo("/Users/a/Documents/TheRong'sCaravan/php/Livvon/storage/logs/laravel-cron-start.log");
//変更系
//デッドラインによる自動拒否は最後にする(受諾されている可能性もあるため)
//※この処理の時点ではメール送信はしない(メール自体はhandshake_denyで行う)
$schedule->command('handshake_deadline')->everyMinute()->withoutOverlapping()
->sendOutputTo("/Users/a/Documents/TheRong'sCaravan/php/Livvon/storage/logs/laravel-cron-deadline.log");
}
いまのとこハンドシェイクの開始と終了のみ取り扱っています。変更に関してはまだですが、メール送信自体のロジックはほぼコピペになるように作ってるはずなんで大丈夫かなって感じです。
順番に関しては多少気を使っています。まず承認を最優先し、次に拒否ですね。
というのも、メール送信を全部キュー処理にしたせいで、ハンドシェイク開始メールが送られる前に画面上ではハンドシェイクの可否が選択できるようになってまして、1分以内に運良く気づけばWeb上から選択することも可能なんですね。
そーいうわけで、先に終了するような処理を書いとけば、ハンドシェイク開始の通知キューを抹消することができる、って感じですね。
で、ちょっと考えたのが回答期限超過によるデッドラインですね。現状は24時間で固定にしていますけど。
実はここではメールを送信せず、ハンドシェイク拒否キューにぶっ込んで、その拒否理由を「システム上の理由」ということにする・・・・・・という処理がここに書いてあります。
で、こっからどういう処理になっていくのか、ハンドシェイク拒否の例でみてみましょーか。
class Handshake_deny extends Command {
//DBのキューにあるハンドシェイク開始のメールを一括送信。
protected $name = 'handshake_deny';
/**
* The console command description.
*
* @var string
*/
//protected $description = 'Command description.';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$queue = (new T_reservationsDAO())
->where("mail_queue_deny_handshake","=",1)//ハンドシェイク拒否メールが未送信のもの
->select_email()
->select_data()
->select_reason()
->get();//未処理で期限が切れているハンドシェイクデータを取得
if(empty($queue)){return;}//データがなければおわり
(new HandshakeController())->deny_queue($queue);
return true;
}
}
まぁこんな感じに書いてます。わりと見やすいプログラムになったかと思います。
DBから該当するものを取ってきて、それが空っぽじゃなければコントローラーに処理を投げる、ということにしてます。
ね、チェーンメソッドって見やすいでしょ?(笑)
public function deny_queue($columns){
//与えられたレコード群に対するハンドシェイク非承認を開始する
$processed_ids=array();//処理されたIDを入れる
foreach($columns as $column){
try{
$detail = (new Handshake_detail())->set_from_record($column);
$reason = new Reason($detail->__get("reason"), $detail->__get("reason_note"));
(new MailController())->handshake_deny($detail,$reason);//ハンドシェイク拒否メール送信
$processed_ids[]=$column->id;//処理済としてIDを保存
}catch (\Exception $e){
//なんらかのエラーが発生したら飛ばす
d($e);
}
}
T_reservationsDAOfunction::update_handshake_deny($processed_ids);//処理が終わったもののDBを書き換える
}
該当するメソッドはこれですね。不安なのでtry-catchでエラーを吐き出すようにしています。
ここでエラーが出ればその内容がログファイルに書き出されるはずです。一応これでデバッグしていたんですが、実務でどうなるかは知りません。(初心者丸出し)
やっていることは単純で、予約情報を扱うクラスに、レコードをぶっ込んで情報をごにょごにょしてもらい、そっから必要な情報を抜き出してうんたら、っていうのをやっています。
ちなむとReasonとかいう変数使ってますけど、最終的にこれdetailの中に入ってるんでぶっちゃけ要らなくなりましたが、書き直すのめんどいので残してます←
で、エラーが特に出なかったら配列に保存して、最終的にupdateするって感じの処理になってます。
public function handshake_deny(Handshake_detail $detail,Reason $reason){
//ユーザー側
Mail::to($detail->user_email)
->queue(new HandshakeDeny_user($detail,$reason));
//ホスト側
/*Mail::to($detail->host_email)
->queue(new HandshakeDeny_host($detail,$reason));*/
}
メールコントローラーの処理はこうなっています。ただ、アドレスを引っこ抜いてメール用のクラスに投げているだけですね。
これはもう完成形だと思います。まぁReasonは要らないのでRC版では消すつもりですけど。やっぱ、情報が集約されていることが保証されているクラスってのを作るだけでだいぶ簡素化されて見やすいですね。
こう書き直すのに何日もかかってるからほんと二度手間だったんだよなあ
まぁこっから先はそんなに解説はいらないと思うので省略しますけど、最後にβ版での目玉というか、一番頑張ったHandshake_detailクラスの内容を紹介して終わります(笑)
class Handshake_detail {
protected $empty=true;
protected $is_accept;
protected $is_canceled;
protected $is_status=false;//falseならば「秘匿情報公開」
protected $is_user=false;//視点調整用。
protected $reason=null;
protected $accept_note=null;
protected $id;
protected $host_token;
protected $user_token;
protected $price;
protected $day;
protected $day_finish;
protected $nights;
protected $checkin;
protected $checkin_Time;
protected $checkout;
protected $checkout_Time;
protected $occurance_Datetime;
protected $deadline_Datetime;
protected $option_breakfast;
protected $option_dinner;
protected $option_pickup;
protected $note;
protected $user;//将来的には以下のデータはこいつに全部集約されるべき。
protected $user_id;
protected $user_email=null;
protected $user_name;
protected $user_name_first;
protected $user_name_first_kana;
protected $user_name_last;
protected $user_name_last_kana;
protected $user_nickname;
protected $user_is_public_name;
protected $user_sex;
protected $user_tel;
protected $user_yyyy;
protected $user_m;
protected $user_d;
protected $user_Age;
protected $user_count_stay;
protected $user_count_plus;
protected $user_count_minus;
protected $user_address_country;
protected $user_address_formatted;
protected $user_address_postalCode;
protected $user_address_prefecture;
protected $user_address_municipality;
protected $user_address_localName;
protected $user_address_buildingName;
protected $user_profile;
protected $user_is_Verified;
protected $host;
protected $host_id;
protected $host_email=null;
protected $host_name;
protected $host_name_first;
protected $host_name_first_kana;
protected $host_name_last;
protected $host_name_last_kana;
protected $host_nickname;
protected $host_is_public_name;
protected $host_sex;
protected $host_tel;
protected $host_yyyy;
protected $host_m;
protected $host_d;
protected $host_Age;
protected $host_count_stay;
protected $host_count_plus;
protected $host_count_minus;
protected $host_address_country;
protected $host_address_formatted;
protected $host_address_postalCode;
protected $host_address_prefecture;
protected $host_address_municipality;
protected $host_address_localName;
protected $host_address_buildingName;
protected $host_profile;
protected $host_is_Verified;
function __get($name){
return $this->$name;
}
function get_partner(){
//相手のプロフィールを取得する
if($this->is_user===true){
return $this->host;
}else{
return $this->user;
}
}
function deny(Reason $reason){
$this->is_accept=0;
$this->is_status=false;
$this->reason=$reason;
}
function accept($accept_note=null){
$this->is_accept=1;
$this->is_status=true;
$this->accept_note=$accept_note;
return $this;
}
function set_vision(string $name){
if($name=="user"){$this->is_user=true;}
if($name=="host"){$this->is_user=false;}
return $this;
}
public function display_user_age(){
return ($this->is_status===true)?$this->user_Age->display_age():$this->user_Age->display_generation();
}
public function display_day_nights(){
//〜(曜)から〜泊、という文面を出す
return $this->day->format_m_d()."(".$this->day->convert_Japanese_Weekday().")から".$this->nights."泊 ";
}
public function display_status():string{
if($this->is_accept === null){
return "ハンドシェイク承認待ち";
}
if($this->is_canceled === 1){
//キャンセル
return $this->reason->display_reason();
}
if($this->is_accept === 1){
return "ハンドシェイク完了";
}
if($this->is_accept === 0){
return "ハンドシェイク非承認";
}
}
public function generate_Profile(String $hundle):Array{
//userもしくはhostのProfileインスタンスを作るための準備
$result =[];
$header = $hundle.'_';
foreach ($this as $key=>$value){
if(strpos($key,$header) !== false){
//"$user_"もしくは"$host_"
$result[str_replace($header, "", $key)]=$value;
}
}
return $result;
}
public function set_from_host_detail($id,$host_token){
$this->set_vision("host")->set_from_record(T_reservationsDAOfunction::get_detail_byhost_token($id, $host_token));
return $this;
}
public function set_from_user_detail($id,$user_token){
$this->set_vision("user")->set_from_record(T_reservationsDAOfunction::get_detail_byuser_token($id, $user_token));
return $this;
}
public function set_from_host($id,$host_token){
$this->set_vision("host")->set_from_record(T_reservationsDAOfunction::get_handshake_info_byhost_token($id, $host_token));
return $this;
}
public function set_from_user($id,$user_token){
$this->set_vision("user")->set_from_record(T_reservationsDAOfunction::get_handshake_info_byuser_token($id, $user_token));
return $this;
}
public function set_from_id($id){
$this->set_from_record(T_reservationsDAOfunction::get_handshake_info_byid($id));
return $this;
}
public function set_from_record($record){
if(empty($record)){
//存在しないハンドシェイクを取得しようとした。
//IDとトークンが一致していない場合ここにくる。
error_redirect("s00");
}
$this->empty=false;
//とりあえず思考停止でメンバ変数登録
foreach((array)$record as $key=>$value){
$this->$key = $value;
}
//オブジェクト生成
$this->checkin_Time = new Time($this->checkin);
$this->checkout_Time = new Time($this->checkout);
$this->user_name = new Name($this->user_name_first,$this->user_name_first_kana,
$this->user_name_last,$this->user_name_last_kana,$this->user_nickname,$this->user_is_public_name);
$this->host_name = new Name($this->host_name_first,$this->host_name_first_kana,
$this->host_name_last,$this->host_name_last_kana,$this->host_nickname,$this->host_is_public_name);
$this->deadline_Datetime=new LivvonDateTime($this->deadline_Datetime);
$this->occurance_Datetime=new LivvonDateTime($this->occurance_Datetime);
$this->day=new LivvonDateTime($this->day);
$this->day_finish=new LivvonDateTime($this->day_finish);
$this->user_Age=new Age($this->user_yyyy,$this->user_m,$this->user_d);
$this->host_Age=new Age($this->host_yyyy,$this->host_m,$this->host_d);
if($this->is_accept === 1){
$this->is_status=true;//受け入れ済状態の時のみ情報を公開する
}else{
if($this->reason !=null){
$this->reason = new Reason($record->reason, $record->reason_note);
}
}
$this->user = new Profile($this->generate_Profile("user"));
$this->host = new Profile($this->generate_Profile("host"));
return $this;
}
}
実はすっげー汚いプログラムになりました(笑)まぁ突貫工事で作ってるししょうがないね・・・・・・(?)
もうちょっとなんとかならないのか考える必要がありますが、とりあえずレコードそのものからでも、IDとトークンの組み合わせからでもデータが入るようになっています。
で、データが入ったらそれを元にメンバオブジェクトを生成して、利便性の向上を図っているわけですね。実際使いやすいです。
もちろん、ハンドシェイク段階では氏名などの情報はそもそも取得していないので入らないのでセキュリティも万全です!悪さされることはないと思うんですけど(笑)
ここ数日はこんなのを書いていたわけですね〜。
まぁそんな感じの機能紹介でした。たぶんここまで読んでる人いないとおもうけど(笑)