新情報まとめました。
新カバー楽曲

ドレミファロンド。
ボカロ曲です。
カバーバンドはハロハピ
予想レベル24。
フル楽曲情報

予想通りFIRE BIRDでした。
予想レベル28。
公開と同時にMVも追加されます。

公開日は23日です。
ガールズバンド総選挙情報
イラストと楽曲が公開されました。



曲の一部が後悔されていましたが、かなり難しそうです。
予想レベル27。
ログインボーナス


ログインボーナス忘れずに貰いましょう。
本当にね、もう年だからね、長時間PC作業していると目が疲れるのよ。
なので、思い切って買ってみました。

アイマッサージャー。

USBで充電するタイプで、3~4時間の充電で、30~45分使用可能になります。
スイッチをONにすると動き出します。
目に当てる部分が暖かくなり、空気圧と振動でマッサージしてくれます。
やはり、目元を暖めてくれるのは本当に嬉しくて、
今までは蒸しタオルを使っていたのですが、その効果は長くても1分。
でもこれは1回で15分間暖めてくれます。
この機能だけで良い。
もう、マッサージ機能とかは無くても良いです。
振動機能なんて意味わからん。
目を温めて、繰り返し利用できるだけでも買えて満足です。
じゃらんを調べてみると、札幌でワカサギ釣り体験ができるというものがありましたので、行ってみました。
車で行く場合は、駐車料金が必要になります。
車が無い場合は、札幌バスターミナル、麻生駅、栄町駅からあいの里4条1行きのバスに乗れば最寄りのバス停まで行けます。


自分でテントを立ててワカサギを釣ることもできるみたいです。
もちろん、釣り具をレンタルすることもできます。
テントに番号が付けられていて、受付を済ませると、指定されたテントの中で釣ることになります。

テントの中はこんな感じ。

穴が空いているので、ここに糸を垂らして釣ります。
エサもレンタルに含まれます。(※観覧注意)


13時から15時までやってみましたが、釣れませんでした。
15時近くになると日が落ちて寒くなってきて、帰りのバスの時間も近くなってきたので、断念しました。
いろいろ調べてみると、エサは針に取り付けた後、はさみで二つに切る、という手間が必要らしいです。
というのは、エサを切断した断面からエサの体液が水の中に広がり、ワカサギをおびき寄せる、らしいです。
レンタルにはさみは付いてきません。
余ったエサは同じテントでワカサギを釣りまくってた方にあげてきました。
その人の話を聞くと、釣り具は1500円で購入できる、とのことです。
まぁ、いろいろ足りないところもわかってきたので、また次回(たぶん来年)挑戦したいと思います。

冷えた体に冷たいビール。
前回までの状況はこちら。
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/hotel-mng
Vue.jsを使用する前はRequestにバリデーション処理を行っていましたが、Vue.jsを使用してからは、一切使用されていませんでした。
当然、このままではよろしくないので、パラメータをチェックするバリデーション処理を追加します。
やることは単純で、パラメータに値が入っているかどうかだけを確認します。
validate: function(){
var ret = true;
this.errors = [];
if(this.contents.id == 0) {
this.errors.push("ユーザーが選択されていません");
ret = false;
}
if(this.contents.num == 0) {
this.errors.push("人数が選択されていません");
ret = false;
}
if(this.contents.roomid == 0) {
this.errors.push("部屋が選択されていません");
ret = false;
}
if(this.contents.days == 0) {
this.errors.push("宿泊日数が入力されていません");
ret = false;
}
if(this.contents.start_day == "") {
this.errors.push("宿泊日が入力されていません");
ret = false;
}
if(this.contents.checkout == "") {
this.errors.push("チェックアウト時刻が入力されていません");
ret = false;
}
return ret;
},
regist: function() {
if(this.validate() == false){
this.error_flg = true;
return;
}
var self = this;
this.param.contents = this.contents;
axios.post('/api/add', this.param).then(function(response){
document.location = "/management";
}).catch(function(error){
self.error_flg = true;
self.error_message = error.response.data.errors;
console.log("失敗しました");
});
},
<p v-if="error_flg == true" class="error">
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
チェックに引っかかった項目をすべてerrorsにpushして、error_flg=trueとすることで、その内容をリストで表示します。

おなじ処理を編集画面にも実装します。

うまく動きました。
Amazonを見ていると、他にもそれなりの性能で安いスマートバンドあるじゃん!って思いますが。
オイラも一時期、安いスマートバンド使ってました。
これを使用していたときは、とにかく、装着部分がめちゃくちゃかぶれてました。
ところがですよ。
これをfitbitに代えてからかぶれなくなったのです。
これには、調べてみると理由がありまして。
fitbitって、実は過去に皮膚のかぶれでクレームが入り、リコールを行っているのです。
https://www.gizmodo.jp/2015/02/post_16614.html
原因は、バンドの素材にアレルギーを引き起こす可能性があるものが含まれていたらしく、それ以降、アレルギー物質を含まない素材にした、ということらしいです。
実際、オイラが安物スマートバンドを使っていたときは、一日中装着していると、肌がかぶれていましたが、
fitbitに代えてからは一切かぶれていないのです。
お風呂以外、ほぼ同じ場所につけっぱなしにも関わらず!
確かにfitbitは他のスマートバンドより割高ですが、高いものには高い理由があった。
今はfitbitに代えて大満足です。
安物スマートバンドで肌荒れに困っている人は、今すぐfitbitに切り替えよう!
前回までの状況はこちら
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/sudoku
さて、最後に解析終了処理を実装していきます。
解析終了条件は、9×9=81マス全ての数字が確定した場合、になります。
逆に言えば、81マスの中で、一つでも確定していないマスがあれば、終了しない、と言うことになります。
と言うわけで、コードはこうなりました。
private bool checkEnd()
{
for (int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
if(_square[i, j].isConfirmed() == false)
{
return false;
}
}
}
return true;
}
解析終了ならばtrueを、そうでないならばfalseを返します。
これを周回処理に組み込みます。
public void run()
{
bool roop = true;
while (roop)
{
for(int row = 0; row < 9; row++)
{
for(int col = 0; col < 9; col++)
{
if(_square[row,col].isConfirmed() == false)
{
Candidate candidate = new Candidate();
searchRowLine(row, candidate);
searchColLine(col, candidate);
search9Area(row, col, candidate);
_square[row, col].checkCandidate(candidate);
}
}
}
roop = !checkEnd();
FileAccess.Output(_square);
}
}
ループ条件と判定結果が反転しているので、!演算子で反転させています。
実行結果はこちら。


ちゃんと全部埋まった時点で終了しました。
前回までの状況はこちら。
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/hotel-mng
さて、ここである重大な欠陥を見つけてしまいました。
それは、Vue.jsで実装を行った際、管理者が予約をチェックする処理の実装を忘れていました。
いかんいかん、完全にミスったわ。
というわけで、ここを実装します。
修正するのは予約一覧から予約をクリックして表示される予約詳細画面です。
管理者がこの画面を表示させた場合、チェックボタンを追加し、チェック処理を行います。
Vue.jsのテンプレートと処理を追加します。
<p>
<button @click="closeModal">close</button>
<button v-if="edit_flg == false" @click="onClickEdit">編集</button>
<button v-else @click="onClickSave">保存</button>
<button v-if="edit_flg == false" @click="onClickDelete">削除</button>
<button v-if="role == true && edit_flg == false" @click="onClickCheck">チェック</button>
</p>
data() {
return {
role: false,
error_message: "",
error_flg:false,
errors: {},
created: function() {
this.getRole();
this.getRooms();
this.getTimeList();
},
methods: {
getRole: function() {
var self = this;
axios.post('/api/role').then(function(response){
self.role = response.data.role;
}).catch(function(error){
console.log("失敗しました");
});
},
データにroleフラグを追加し、画面表示時にroleの取得を行います。
trueが返れば管理者であるということなので(ここは前回実装の動きに合わせる)、role=trueならば、チェックボタンを表示させます。
また、編集中はチェックボタンを表示させないようにします。

では、API部分の実装。
public function check(Request $request)
{
$this->registerManagement->lodging($request->id);
return response()->json(['registerLists' => $this->registerManagement->getListByMonth(
$request->year,
$request->month,
$request->room,
Auth::user()
)]);
}
チェックの実行を行った直後に、予約一覧を返すことで、チェック後の予約一覧を素早く表示させることができます。
最後Routeを追加。
Route::post('/api/check', 'ApiController@check');
これで機能するようになりました。
ふぅ。(汗
前回までの状況はこちら。
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/sudoku
多分、今回が一番のキモ。
実際に数独を解析するロジックを作成していきます。
そもそも、数独のルールは、

オレンジのところに入る数字は、黄色の縦、横、9マスの部分に同じ数字が存在してはならない、ということです。
ということは、黄色の部分を検索して、そのなかに含まれない数字が、このマスに入る数字の候補となります。
そのとき、もし、候補が一つしか存在しない場合は、その値がそのマスに入る数字で確定します。

この調査を、確定していないマス全てに対して、全てのマスが確定するまで実施します。
では、まずは候補を調査した結果を格納するクラスの定義。
class Candidate
{
public bool[] value;
public Candidate()
{
this.value = new bool[9] { false, false, false, false, false, false, false, false, false };
}
public int Count()
{
int ret = 0;
foreach(var val in this.value)
{
if(val == true)
{
ret++;
}
}
return ret;
}
}
boolean値の配列9個を持ち、コンストラクタでfalseに初期化します。
メソッドとしては、この中でいくつヒットしたか、配列のtrueの数を数えて結果を返すメソッドを持ちます。
これを使用して、実際に調査を行う処理はこちら。
class Sudoku
{
private Square[,] _square;
/**
* コンストラクタ
*/
public Sudoku(Square[,] square)
{
_square = square;
}
/**
* 実行
*/
public void run()
{
int roop = 0;
while (true)
{
for(int row = 0; row < 9; row++)
{
for(int col = 0; col < 9; col++)
{
if(_square[row,col].isConfirmed() == false)
{
Candidate candidate = new Candidate();
searchRowLine(row, candidate);
searchColLine(col, candidate);
search9Area(row, col, candidate);
_square[row, col].checkCandidate(candidate);
}
}
}
//debug
roop++;
FileAccess.Output(_square);
if(roop == 10)
{
break;
}
}
}
private void searchRowLine(int row, Candidate candidate)
{
for(int i = 0; i < 9; i++)
{
int val = _square[row, i].GetValue();
if(val != 0)
{
candidate.value[val - 1] = true;
}
}
}
private void searchColLine(int col, Candidate candidate)
{
for (int i = 0; i < 9; i++)
{
int val = _square[i, col].GetValue();
if (val != 0)
{
candidate.value[val - 1] = true;
}
}
}
private void search9Area(int row, int col, Candidate candidate)
{
int rowStart;
int colStart;
if (row >= 0 && row <= 2)
{
rowStart = 0;
}
else if(row >= 3 && row <= 5)
{
rowStart = 3;
}
else
{
rowStart = 6;
}
if (col >= 0 && col <= 2)
{
colStart = 0;
}
else if (col >= 3 && col <= 5)
{
colStart = 3;
}
else
{
colStart = 6;
}
for(int r = rowStart; r < rowStart+3; r++)
{
for (int c = colStart; c < colStart + 3; c++)
{
int val = _square[r, c].GetValue();
if (val != 0)
{
candidate.value[val - 1] = true;
}
}
}
}
}
searchRowLine()では、横のラインを、
searchColLine()ででゃ、縦のラインを、
search9Area()では9マスの範囲を検索します。
ヒットした数字に対して、candidateの持つ配列にtrueを設定します。
なお、-1しているのは、数独に設定する数字が1~9なのに対し、プログラム上の配列の範囲は0~8なので、その補正です。
そして、調査結果を反映する処理はこちら。
class Square
{
:
中略
:
public void checkCandidate(Candidate candidate)
{
if(candidate.Count() == 8)
{
for(int i = 0; i < 9; i++)
{
if(candidate.value[i] == false)
{
SetValue(i + 1);
}
}
}
}
}
調査結果、見つかった数字の数が8個だった場合、見つかっていない数字を探し、その値でマスの値を確定します。
これをrun()メソッドで、確定していない全マスに対して行います。
本来は、全てのマスが埋まったことを確認して終了しなければならないのですが、そこはまだ作っていないので、とりあえず、10回繰り返す、と言うことにしています。
では、実際に動かしてみます。
インプットデータはこちら。

実行結果はこちら

1回ループする毎に、途中結果も全てファイルに吐き出すようにしています。
見事に全部埋まりました。
これを、ナンプレアプリに入力してみましょう。

目論見通りに動いてくれましたね。
多分、簡単な数独問題ならこれで解けるはず。
前回までの状況はこちら
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/hotel-mng
さて、前回はユーザーログインでのあれこれを修正したのですが、それに伴って、管理者ログインでのあれもれも直す必要があります。
まずは、予約一覧画面。
ユーザーログインでは、ログインしたユーザーのもののみを表示させましたが、管理者ログインでは、全ユーザーの情報を表示する必要があります。
なので、ロール「管理者(manager)」でなければwhere句でユーザーを指定するというふうに変えなければなりません。
public function getListByMonth($year, $month, $room, $userId)
{
$select = ['reserve_managements.id as id', 'users.name as name', 'users.address as address', 'users.phone as phone', 'num', 'rooms.id as roomid', 'rooms.name as room', 'days', 'checkout', 'start_day'];
$query = ReserveManagement::select($select)
->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
->leftJoin('reserve_management_user', 'reserve_managements.id', '=', 'reserve_management_user.reserve_management_id')
->leftJoin('users', 'reserve_management_user.user_id', '=', 'users.id')
->where('start_day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
->where('start_day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
->where('reserve_management_room.room_id', $room)
->where('lodging', false);
if(Gate::denies('manager')){
$query = $query->where('users.id', $userId);
}
$query = $query->orderBy('start_day');
return $query->get();
}
こんな感じで、where()を、Gate::denies()で「管理者以外」とすれば、このwhereはユーザーログインのみに適用されます。

次は、予約追加画面。
ユーザーログインの場合は、ログイン中の名前などの表示させましたが、管理者ログインの場合は、登録されているユーザーを選択して登録しなければなりません。
ここでVue.jsの出番です。(まじか・・・)
まずはテンプレート。
基本的には、編集時のVue.jsを参考にしています。
<template>
<div>
<p v-if="error_flg == true" class="error">{{error_message}}</p>
<table class="edit">
<tbody>
<tr>
<th>名前</th>
<td v-if=role>{{ contents.name }}<button @click="openModal">検索</button></td>
<td v-else>{{ contents.name }}</td>
</tr>
<tr>
<th>住所</th>
<td>{{ contents.address }}</td>
</tr>
<tr>
<th>電話番号</th>
<td>{{ contents.phone }}</td>
</tr>
<tr>
<th>人数</th>
<td>
<select v-model=contents.num>
<option v-for="num in nums" v-bind:value="num.value">{{ num.text }}</option>
</select>
</td>
</tr>
<tr>
<th>宿泊部屋</th>
<td>
<select v-model="contents.roomid">
<option v-for="room in rooms" v-bind:value="room.id">{{ room.name }}</option>
</select>
</td>
</tr>
<tr>
<th>宿泊日数</th>
<td><input type="number" v-model=contents.days /></td>
</tr>
<tr>
<th>宿泊日</th>
<td><input type="date" v-model=contents.start_day /></td>
</tr>
<tr>
<th>チェックアウト</th>
<td>
<select v-model="contents.checkout">
<option v-for="time in timeList" v-bind:value="time.key">{{ time.value }}</option>
</select>
</td>
</tr>
</tbody>
</table>
<p><button @click="regist">登録</button></p>
<div id="overlay" v-show="showContent">
<div id="content">
<table class="edit">
<p>名前<input type="text" v-model=param.search /></p>
<tbody>
<tr>
<th>名前</th>
<th>住所</th>
<th>電話番号</th>
</tr>
<tr v-for="user in users" @click="selectUser(user)">
<td class="name">{{ user.name }}</td>
<td class="address">{{ user.address }}</td>
<td class="phone">{{ user.phone }}</td>
</tr>
</tbody>
</table>
<p>
<button @click="closeModal">close</button>
<button @click="searchUsers">検索</button>
</p>
</div>
</div>
</div>
</template>
まず、テーブルの部分ですが、名前、住所、電話番号以外は、編集画面をそのまま使用しました。
問題の、名前、住所、電話番号の部分ですが、管理者ログインならば、検索ボタンを設置し、それをクリックするとモーダルダイアログを表示する、という仕組みにしたいと思います。
モーダルダイアログでユーザーを検索し、その名前をクリックすると、登録画面に反映される、という動きにします。
実際の処理。
<script>
export default {
data() {
return {
role: false,
error_message: "",
error_flg:false,
errors: {},
nums: [
{text:'1', value:1},
{text:'2', value:2}
],
timeList:[],
rooms: [],
users: [],
showContent: false,
param: {
search: "",
},
contents: {
id: 0,
name: "",
address: "",
phone: "",
num: 0,
roomid: 0,
room: "",
days: 0,
start_day: "",
checkout: "",
},
}
},
created: function() {
this.getRole();
this.getRooms();
this.getTimeList();
},
methods: {
getRole: function() {
var self = this;
axios.post('/api/role').then(function(response){
self.role = response.data.role;
if(self.role == false){
self.contents.id = response.data.user.id;
self.contents.name = response.data.user.name;
self.contents.address = response.data.user.address;
self.contents.phone = response.data.user.phone;
}
}).catch(function(error){
console.log("失敗しました");
});
},
searchUsers: function() {
this.users = [];
var self = this;
axios.post('/api/users', this.param).then(function(response){
response.data.users.forEach(element => {
self.users.push({id:element.id, name:element.name, address:element.address, phone:element.phone});
});
}).catch(function(error){
console.log("失敗しました");
});
},
selectUser: function(user) {
this.contents.id = user.id;
this.contents.name = user.name;
this.contents.address = user.address;
this.contents.phone = user.phone;
this.closeModal();
},
regist: function() {
var self = this;
this.param.contents = this.contents;
axios.post('/api/add', this.param).then(function(response){
document.location = "/management";
}).catch(function(error){
self.error_flg = true;
self.error_message = error.response.data.errors;
console.log("失敗しました");
});
},
getRooms: function() {
var self = this;
axios.post('/api/rooms').then(function(response){
response.data.roomLists.forEach(element => {
self.rooms.push({id:element.id, name:element.name});
});
}).catch(function(error){
console.log("失敗しました");
});
},
getTimeList: function(){
var self = this;
axios.post('/api/timelist').then(function(response){
for (let [key, value] of Object.entries(response.data.timelist)){
self.timeList.push({key: key, value: value});
}
}).catch(function(error){
console.log("失敗しました");
});
},
openModal: function(){
this.users = [];
this.showContent = true;
},
closeModal: function(){
this.showContent = false;
this.edit_flg = false;
},
}
}
</script>
ポイントは、ユーザーログインも管理者ログインも使用するということ。
なので、現在ログインしているアカウントのロールをgetRole()で最初に取得している、ということです。
このgetRole()の結果で表示内容を切り替えたりしてます。
そして、必要事項を入力したら、登録処理を行います。
このとき必要になるのは、ユーザーID。これも送信するパラメータに含めます。
では、API側の処理。
public function __construct()
{
$this->middleware('auth');
$this->registerManagement = new RegisterManagementRepository();
$this->room = new RoomRepository();
$this->user = new UserRepository();
}
public function role(Request $request)
{
return response()->json(['role' => Gate::Allows('manager'),
'user' => Auth::user()]);
}
public function users(Request $request)
{
return response()->json(['users' => $this->user->search($request->search)]);
}
public function add(Request $request)
{
\Log::debug(print_r($request->contents, true));
if($this->registerManagement->checkSchedule($request->contents["start_day"],
$request->contents["days"],
$request->contents["roomid"]) == false)
{
return response()->json([
'errors' => "スケジュールが重複しています"
], 400);
}
$param = $this->registerManagement->getParam();
$this->registerManagement->add([
$param[0] => $request->contents["num"],
$param[1] => $request->contents["days"],
$param[2] => $request->contents["start_day"],
$param[3] => false,
$param[4] => date('Y-m-d H:i', strtotime($request->contents["start_day"].' + '.$request->contents["days"].' day') + $request->contents["checkout"])
], $request->contents["roomid"], $this->user->getUserById($request->contents["id"]));
return response()->json();
}
class UserRepository
{
public function __construct()
{
}
public function getUserById($id)
{
return User::where("id", $id)->first();
}
public function search($word)
{
$select = ['id', 'name', 'address', 'phone'];
return User::select($select)
->where('name', 'like', "%{$word}%")
->get();
}
}
ユーザー検索で使用する「あいまい検索」はこんな感じで「like」を使用します。
管理者ログイン時の動作


ユーザーログイン時の動作

これでうまく行ったみたいです。(疲れた。。。)
富士山と書いて、「ふじやま」と読みます。
https://taishu-izakaya-fujiyama.owst.jp
大通りの近くに、お昼から飲める居酒屋がオープンしているんですね。

飲み物は1280円で2時間、ビール付きは+300円です。
お酒の値段は高いですが、何よりもお食事の値段が安い。


このメニューの他にも黒板メニューがあります。

オニオンサラダ。鰹節の下に玉ねぎ+ドレッシングがあります。

サバ文化干し。

サバの味噌煮。
この日はとにかく魚が食べたかった。
これだけ飲み食いしてて、お会計は3000円程度でした。
かなりお手頃な価格設定ですよ。
ガーデンズキャビンからも近いので、ガーデンズキャビンのお風呂に行った後、ここでお風呂上がりの一杯、という使い方もできます。
なってったって、お昼から開いてますからね!