- 投稿日:2020-08-14T23:06:33+09:00
Carbonを用いて期間検索機能を作る
はじめに
日付検索する時にDBには年月日時分秒まで登録されているけど期間(年月日)で検索したい時ありませんか。
そんな時はCarbonを使って検索機能を作りましょう。Carbonを用いて期間検索機能を作る
ある事柄が始まった日時
$period[start_at]の検索で、検索期間(年月日)のはじめを$request['start_at_from']、検索期間(年月日)の終わりを$request['start_at_to']として$requestの配列で受け取ったとします。
$period['start_at_from']と$period['start_at_to']は(2020-08-13のように)年月日で受け取っているとしてCarbonを用いて00:00:00と日時分を仮置きします。//2020-08-13 $period['start_at_from'] //2020-08-13 00:00:00 Carbon::parse($period['start_at_from'])
startOfDay()メソッドで1日の中の一番初めの時刻(00:00:00)、endOfDay()メソッドで1日の中の一番終わりの時刻(23:59:59)を取得できます。//2020-08-13 00:00:00 Carbon::parse($period['start_at_from'])->startOfDay()); //2020-08-13 23:59:59 Carbon::parse($request['start_at_to'])->endOfDay());
whereは第一引数にカラム名、第二引数に比較演算子、またはSQLで使うオペレータ、第三引数に比較する値を指定します。
上記までのものも合わせて下記に実装をまとめます。Controller.phpuse Carbon\Carbon; public function periodSearch(Request $request) { $inputs = $request->validatedValues(); $this->where('start_at', '>=', Carbon::parse($request['start_at_from'])->startOfDay()); $this->where('start_at', '<=', Carbon::parse($request['start_at_to'])->endOfDay()); } return view('periodSearch', ['period'=> $period]);おわりに
Carbonは日時を操作するのに大変便利なので使いこなせるようにCarbonはどんなことができるか調べておくと良いかもしれません。
- 投稿日:2020-08-14T22:41:14+09:00
Laravelで掲示板制作 #3 『投稿を画面に表示』
今回はデータベースの投稿データを画面に表示します。
わからない部分は学習ノート記事を見てもらうか公式ドキュメント見てください。シーディング
そもそも表示するものがないとなのでデータを入れる。
どうやらモデルを介してインサートするとTimestampが自動で入るらしい。php artisan make:seeder PostsSeederPostsSeeder.phppublic function run() { // Post::create([ 'nickname' => 'テストくん', 'content_text' => 'おはようございます、テストくんです' ]); Post::create([ 'nickname' => 'テストちゃん', 'content_text' => 'こんにちは、テストくんです' ]); }DatabaseSeeder.phppublic function run() { $this->call(PostsSeeder::class); }php artisan db:seed画面に表示
データが入ったので画面に表示します。
コントローラ作成
コマンドで作成。
php artisan make:controller HomeController関数作成
作成した
HomeController.phpにルーティングで入ってくる関数を作る。
Postモデルから日付順で取ってきてhomeビューを返します。HomeController.phpuse App\Post; // 省略 public function get_index(){ $posts = Post::orderBy('created_at')->get(); return view('home', ['posts' => $posts]); }ビュー作成
foreachディレクティブでそれぞれ表示させます。
card、m-1、p-1はbootstrapのクラスです。home.blade.php<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <!-- 省略 --> </head> <body> <div> <div class="header"> <span>あのにボード</span> </div> <div class="post-list"> @foreach($posts as $post) <div class="card m-1 p-1"> <div> <span>{{ $post->nickname }}</span> </div> <div> <span>{{ $post->created_at }}</span> </div> <div> <span>{{ $post->content_text }}</span> </div> </div> @endforeach </div> </div> </body> </html>ルーティング
コントローラの関数を呼び出すよう書き換える。
routes/web.phpRoute::get('/', 'HomeController@get_index');表示確認
いつも通り
php artisan serve手順抜けてたらごめんなさい。
- 投稿日:2020-08-14T21:39:29+09:00
IPアドレス流出はなぜ起こったのか?(推理)
TL;DR
- 某サービスに「ソースコードからIPアドレスが流出してしまう」という脆弱性が発見され、世間を騒がせていますね。
- そもそもなぜソースコードからIPアドレス流出したの?という問題について語ってみます。
- 筆者は某サービスの開発に関わっていたわけではないので、当記事で語るパターンでの流出だったとは断言できません。
- そのため、起こりうる一つの「怖いパターン」の事例共有として、ご参考までによろしくお願いいたします。
- 初学者にもわかるように少し噛み砕いてご説明します。
前提
まず大前提ですが、統計のためやバグの追跡のためにサービス利用者のIPアドレスを収集すること自体は不思議なことではありません。
問題は、なぜそれがフロントから見える状態になっていたのかです。統計を取るにせよ、サービスに嫌がらせをしていた投稿者を特定してそのIPアドレスをブロックするにせよ、DBに値が残っていれば十分で、それをフロントに持ってくる意味ってありませんよね?
少なくとも私はパッと思いつきません。
それにフロントに持ってくるということは、今回のような「流出のリスク」があるということでもあります。要は、「DBにさえあれば良いはずのIPアドレスがなぜフロントから見えてしまったのか?」が今回理解すべきことになります。
サーバサイドの動きを考えてみる
その前提の上でDBに思いを馳せてみましょう。
例えば「ブログ投稿サービスで、記事の情報を保存する『Article』テーブルが存在していた」と考えてみます。
正規化がどうのこうのという難しいことは考えなくて良いです。
以下の要件で考えてみましょう。
- Articleテーブルは1記事の情報を1レコードで保存している
- Articleテーブルには「記事タイトル」「本文」「投稿日付」などのカラムがある
- そのカラムの中に「IPアドレス」もあった
不思議な仮定ではないはずです。
さて、お話が変わって現在三大フルスタックWebフレームワークといえば
- Rails
- Laravel
- Django
です。本題ではないのでなぜこれが三大なのかという議論は避けますが、少なくともよく使われているフレームワークであることには間違いないでしょう。
参考:どれ使うべき?3大WebフレームワークRails・Django・Laravelを徹底比較してみたその中で、RailsとLaravelのORM(DBへのアクセスを簡単にしてくれる仕組みのことですね) には共通の特徴があります。
Active Recordという特徴です。
ざっくりといえば、「DBの1レコードをひとつのオブジェクトとして扱おう!」という考え方です。
賛否両論、メリットデメリットがある仕組みではあるのですが、個人的には大好きな仕組みです。
詳しくはこちらもどうぞ (宣伝)
開発への異常な愛情 または私は如何にして嫉妬を止めてActive Recordを愛するようになったか閑話休題。
さて、例えばLaravelの場合、その"Active Record"を利用すると、このようなことができます。
TestController.php// ログインしているユーザーの最新の記事のIDを特定 $currentArticleId = $this->getCurrentArticleIdByLoginUser(); // 最新の記事(1レコード ≒ 1オブジェクト)を取得 $currentArticle = Article::find($currentArticleId); // オブジェクトをビューに渡す return view('current_article.index',compact('currentArticle'));分かる人は「コントローラーでそんな処理するな」とか「なんでコントローラーで明らかに特定のModel専用の独自処理を実装してるの?怖……」とか言いたくなったかもしれませんが、とりあえず今は無視してください。
要するに、「Articleテーブルの、ログインユーザーによる最新の1レコード ≒ 1オブジェクト」を取得して、フロントにパスしているということだけ伝われば十分です。
compact('currentArticle')はそのためのおまじないです。さて、このオブジェクトが格納されている変数
$currentArticleは、当たり前ですが1レコードの情報全てが格納されています。
フロント側……Laravelは.blade.phpというファイルにHTMLとPHPが融合したようなものを記載していくのですが……このbladeファイルの中では、$currentArticleが持っている情報全てにアクセスすることができます。index.blade.php<h1>{{ $currentArticle->title }}</h1>こう書くだけでWebページにタイトルが表示されます。とても便利です。
話を戻しましょう。なぜフロントでIPアドレスが見えてしまったのでしょうか?
仮説1。
bladeファイル(あるいは他のフレームワークにおける同様の機能)の中でindex.blade.php{{ $currentArticle->ipaddress }}と書いてしまったのでしょうか?
その可能性は低いと思われます。意味がありませんし、レビュー文化があればすぐ気付くでしょう。仮説2。
index.blade.php{{ $currentArticle }}と書いてしまった?これもあまり意味がなく、すぐ気付くため考えにくいです。
ちなみに実際にこれをやってみると確かに流出はするのですが、そもそもフロントに$currentArticleの中身を展開した文字列がズラーッと表示されますw ソースコード上の流出どころの騒ぎではありませんね。仮説3。
index.blade.php<h1>{{ $currentArticle->title }}</h1>と書くだけでもフロントに
$currentArticleの情報は引き渡っているから、$currentArticleのメンバ変数は全て可視化されてしまっていた。
こう考えた人、ほぼ正解!
ただ少なくともLaravelくんはとても賢いので、そのようなことにはなりません。仮説4。
フロント専用のフレームワークを使っていたことが原因。
このパターンについてご説明いたします。JSフレームワークを使っていたとしたら?
フロントの三大フレームワーク、これは議論を待たずに
- React
- Vue
- Angular
ですね。これらはいわゆる「三大フロントフレームワーク」と呼ばれるものですが、言い換えると「三大JavaScriptフレームワークです」
JavaScriptのフレームワークなんです。さて、JavaScriptがPHPのオブジェクトを使う方法はあるでしょうか?
PHPでなくても、RubyでもPythonでも同じです。
使えるように変換する必要がありますよね?そう、JSON(JavaScript Object Notation) です。
……厳密に言えば、JavaScriptのオブジェクトに変換するために、一度JSONに変換する必要があるというお話です。他にも色々方法はありますが、一番良く使われるのがJSONでしょう。そういうわけで、
TestController.php$currentArticle = Article::find($currentArticleId); $currentArticleJson = $currentArticle->toJson();のようにしてあげるとJSON(文字列)に変換できます。
あとはこのJSON文字列
$currentArticleJsonをフロントにそのまま引き渡せば、JavaScriptがその文字列を処理する際に流出してしまう……というオチです。ちなみにLaravelではJSONに変換する際に「この項目はJSONに変換した際に変換の対象にしない!」と指定するためのフィールドがあります。
https://readouble.com/laravel/5.5/ja/eloquent-serialization.html
モデルから変換する配列やJSONに、パスワードのような属性を含めたくない場合があります。それにはモデルの$hiddenプロパティに定義を追加してください
本題ではないので詳細は省きますが、この記載から属性:"ipaddress"の指定が漏れてしまうということは十分にあり得ることです。
結論
繰り返しますが、この記事で書いたのはあくまでもあり得るパターンの一つであり、今回世間を騒がせた件は別の理由である可能性もあります。例えばそれこそ5chではIPアドレスをフロントで表示する機能もあるので、そのような機能がどこかにあったのかもしれません。
ただ、ここで書いた事例は他のIT企業でも十分に「起こりうる」ことです。
ここで紹介したパターンでは、そのリスクはフレームワークを他のフレームワークに適合させるために = 汎化させるために、「JSONへの明示的な変換」をしたことで発生するものでした。
「こうするとリスクは防げる」という銀の弾丸ははなかなかないものなのですが、少なくともJSONへの変換 = いわゆるシリアライズした上でのデータの受け渡しはときにリスクを伴う、ということは覚えておいても良いかもしれません。お読み頂きありがとうございました。
ではまたどこかで。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えないという話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employeeテーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
nameカラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
LIKEであいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employeeテーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$nameに「山田」が入った状態。前方と後方に付加した
'%'文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,
Illuminate\Database\Eloquent\Collectionでのwhere利用は,likeが使えないようです。API 仕様書では,第二パラメータ
$operatorで指定できるような思わせぶりなので,すっかり,ハマってしまいました。どう対応したか
結局,
like利用をあきらめて,完全一致検索に仕様変更しました。そもそも……練習用だからと横着して,「苗字 + 半角スペース1文字 + 名前」をひとまとめにして,
nameカラムにするというテーブルの作り方がマズイですね。きちんと「苗字」と「名前」は別カラムに分けたうえで,完全一致検索という仕様にするのが,いちばんスッキリするかと。
EmployeeController.php(変更前)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }EmployeeController.php(変更後)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', $name); }他のアプローチ
どうしても,あいまい検索が必要ならば,Eloquent モデルで組むのをあきらめて,クエリビルダを使う方式にすると,
like指定での検索ができそうです(未検証)。公式ドキュメントを見ても,
like指定ができないという記述は発見できずじまいでした。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えないとハマったときの話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employeeテーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
nameカラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
likeであいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employeeテーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$nameに「山田」が入った状態。前方と後方に付加した
'%'文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collectionでのwhere利用は,likeが使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()を呼ぶとIlluminate\Database\Eloquent\Collectionインスタンスが返って来るのに対し,query()を呼ぶと,Illuminate\Database\Eloquent\Builderインスタンスが返るという差異があり,それぞれのインスタンスでは,挙動が異なるということへの気づきを得られました。これからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えない!?とハマったときの話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employeeテーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
nameカラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
likeであいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employeeテーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$nameに「山田」が入った状態。前方と後方に付加した
'%'文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collectionでのwhere利用は,likeが使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()を呼ぶとIlluminate\Database\Eloquent\Collectionインスタンスが返って来るのに対し,query()を呼ぶと,Illuminate\Database\Eloquent\Builderインスタンスが返るという差異があり,それぞれのインスタンスでは,挙動が異なるということへの気づきを得られました。これからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えない!?とハマった話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employeeテーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
nameカラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
like演算子であいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employeeテーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$nameに「山田」が入った状態。前方と後方に付加した
'%'文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collectionでのwhere利用は,like演算子が使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()を呼ぶとIlluminate\Database\Eloquent\Collectionインスタンスが返って来るのに対し,query()を呼ぶと,Illuminate\Database\Eloquent\Builderインスタンスが返るという差異があり,それぞれのインスタンスでは,where関数の挙動が異なるということへの気づきを得られました。
Illuminate\Database\Eloquent\Collection::where⇒like演算子が使えない(動かない。そういう仕様?)Illuminate\Database\Eloquent\Builder::where⇒like演算子が使えるこれからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T15:20:27+09:00
【2020年7月-8月】 chromeでpage expiredになって困ったちゃんの解決策
突然、ログイン画面などがPage Expiredになって困っちゃった。
原因
Sessionのsame_siteをnoneに指定しているのに、secureの属性をつけていない。(chromeのupdateにより、same_site=noneの場合はsecure属性にしないとクッキー拒否されます)
config/session.phpの設定
'same_site' => "none"になっているのに.envに
SESSION_SECURE_COOKIE=trueがない場合。
注意
当然ですが、 trueにした場合はhttps経由のアクセスじゃないと、そもそもcookieは有効になりません。
対策としては 自己証明書で https にしてもかまわないのですが、そもそものデフォルト値(same_site="lax")にする事も検討ください。わかりやすいさいと
- 投稿日:2020-08-14T10:13:05+09:00
LaravelでQiita APIを使用し投稿した記事を取得してみた
目的
Qiitaから投稿記事のタイトルとURL、投稿日の取得。(下記サイトのように使用しました。)
http://kudohayatoblog.com/blog_topアクセストークンの取得
下記ページで「新しくトークンを発行する」からトークンを発行。
https://qiita.com/settings/applications
※今回は書き込みをしないので、「read_qiita」を選択。API通信
今回はcURLを使用した方法と、GuzzleHttpを使用した方法を紹介します。
cURLでの通信
$token = '取得したアクセストークン'; $curl = curl_init('https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=10'); $option = [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer '.$token, 'Content-Type: application/json', ], ]; curl_setopt_array($curl, $option); $result = curl_exec($curl); $decode_res = json_decode($response_body);↑で取得できたので、後はforeachなどで取得できます。
foreach ($decode_res as $res_data) { $url = $res_data->url; //urlの取得例 }GuzzleHttpでの通信
下記コマンドでGuzzleのインストール。
composer require guzzlehttp/guzzleuse GuzzleHttp\Client; $client = new Client; $token = '取得したアクセストークン'; $result = $client->request('GET', 'https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=10', [ 'headers' => [ 'Authorization' => 'Bearer '.$token, 'Accept' => 'application/json', ], ]); $response_body = (string) $result->getBody(); $decode_res = json_decode($response_body);先ほどと同様foreachなどで取得できます。
foreach ($decode_res as $res_data) { $url = $res_data->url; //urlの取得例 }最後に
API認証について、Laravelの公式ドキュメントでは以下のように記載されていますが、ここの$clientが何なのかよくわからなかったのでGuzzleを使用しました。
わかる人がいれば教えてください!$response = $client->request('POST', '/api/user', [ 'headers' => [ 'Authorization' => 'Bearer '.$token, 'Accept' => 'application/json', ], ]);
- 投稿日:2020-08-14T07:42:32+09:00
Laravel x GAS API で Spreadsheet 自動作成してみた
はじめに
実務で、GAS(Google Apps Script)を使って Spreadsheet 作成APIを作ったので、そのやり方を書く。
今回は、Laravel の Guzzle を使って APIを叩いたので、そのテンプレも書くことにした。ここでは、clasp などのコマンドラインでの作業の説明は割愛する。
Guzzle テンプレート
$client = new \GuzzleHttp\Client(); url = https://script.google.com/a/XXXX.co.jp/macros/s/XXXXXXXXXXXXXXXXXXXXXXXX/exec $sheets = ['[シート名]' => [['名前', 'Soma', 'Sekimoto'],['住所', 'Tokyo', 'Suginami'], ['年齢と性別', ' 23歳', '男']]] $res = $client->post( $url, [\GuzzleHttp\RequestOptions::JSON => [ 'title' => 'スプレッドシートタイトル', 'sheets' => $sheets, 'spread_sheet_id' => '[スプレッドシート ID]', 'startPositions' => ['startRow' => 3, 'startColumn' => 2], ]]);GAS(Google Apps Script) API
CreateSheet.jsfunction doPost(e) { // 引数で送られてきたパラメータを取得する var c = e.postData.contents var contents = JSON.parse(c) // パラメーターをそれぞれの変数に格納 var title = contents.title var sheets = contents.sheets var startPositions = contents.startPositions var startRow = startPositions.startRow var startColumn = startPositions.startColumn var spreadsheetId = contents.spread_sheet_id var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId).copy(title) : SpreadsheetApp.create(title); // Parameter に spread_sheet_id がない場合は、新しいスプレッドシートを作るようにする。 i = 0 for (var sheet_name in sheets) { if (i >= spreadsheet.getSheets().length) { spreadsheet.insertSheet() } // シートが足りなくなった時は新しいシートを追加させる。 var sheet = spreadsheet.getSheets()[i] var rows = sheets[sheet_name] sheet.getRange(startRow, startColumn, rows.length, rows[0].length).setValues(rows) sheet.getRange(startRow, startColumn, rows.length, rows[0].length).createFilter() sheet.setName(sheet_name) i += 1; } spreadsheet.addEditor("/xx/domain/XXXX.co.jp") // 編集者を付与できる。 return ContentService.createTextOutput(JSON.stringify({url: spreadsheet.getUrl()})).setMimeType(ContentService.MimeType.JSON); // 作成したシートの URL を返す。 }これでスプレッドシート自動作成API完成!!!!
おわりに
既存アプリ x GAS APIの無限大の可能性を利用すれば、最強のAPIレポジトリを完成させることも夢ではありません。みなさんの参考になれば幸いです。
もっとこうした方がいい!!、ここは間違ってる!! 等ございましたら遠慮なくお申し付けください!
もっとスマートなコードの書き方あると思うので、思いつく方いらっしゃいましたらご教授お願いします!
- 投稿日:2020-08-14T04:25:26+09:00
LaravelのFormRequestのテストをちゃんと書く
はじめに
以前にLaravelのFormRequestの単体テストのやり方という記事を書きましたが、この方法だと
prepareForValidation()やpassedValidation()のメソッドは使われないので、これらのメソッドに処理を記述している場合はテストができないということになります。なので、これらのメソッドも実行されるようなFormRequestのテストの書き方を書いていきます。
テストする
そもそも
prepareForValidation()やpassedValidation()はprotectedなのでメソッド単位でテストすることができないので、FormRequest全体の流れをテストします。public function test_() { $parameters = ['key' => 'value']; $request = Request::create('', 'GET', $parameters); app()->instance('request', $request); $formRequest = app(HogeFormRequest::class); $this->assertSame('value2', $formRequest->get('key')); }
$parametersに実際送信するパラメーターを連想配列で記述してください。注意点は
app(HogeFormRequest::class);で解決した時点でバリデーションを含む一連の処理が完了してます。
- 投稿日:2020-08-14T02:06:07+09:00
Laravelで『シンプルに』管理者権限を追加する
Laravelで管理者権限の作り方をシンプルにまとめました
色々やり方があるようだったので、シンプルにまとめました。
Laravelプロジェクトが作成されている前提です。環境
- mac OS Catalina 10.15.6
- Laravel 7.22.4
Gateを使う
AuthServiceProviderの中にあるGateという機能を使います。
ちなみに同じファイル内にPolicyという似た機能を持つものもあります。【GateとPolicyの使い分け】
- Policyは特定のモデルへのアクションに対して制限を加える場合
- Gateはモデルに関連しないアクションに対してユーザーの制限を加える場合
詳しくはこちら Laravel 7.x 認可
手順
1. XXXXX_create-users-table.phpにroleカラムを追加
今回はroleが1を管理者、5を一般ユーザーに設定します。
XXXXX_create_users_table.php// Role 1=admin, 5=normal user $table->tinyInteger('role')->default(5);2. AuthServiceProviderに追加
isAdminという名前で定義して、管理者かどうか判断します。
AuthServiceProvider.phppublic function boot() { $this->registerPolicies(); // 管理者権限設定 Gate::define('isAdmin',function($user){ return $user->role === 1; }); }3. ルーティングを作成して完了
管理者にだけアクセス許可したいページとコントローラーを作成します。
ルーティングをグループにまとめ、ミドルウェアを設定したら完了です。web.phpRoute::middleware(['auth','can:isAdmin'])->group(function(){ Route::get('/admin','AdminController@index'); });
- 投稿日:2020-08-14T01:50:21+09:00
PHPで「ここはis_null?empty?isset?」ってなったので比較してまとめた。
is_null?empty?isset?ってなることが多々あったので、比較実験した。
実験内容
それぞれに以下を与えて結果を取得する。
- $string; //変数を宣言するだけ
- $string = 'test'; //文字列
- $string = null; //null
- 未定義の変数
- 空文字
最終結果はページの最後に表にしています。
is_null()
変数がNULLかどうか調べる。つまり、nullならばtrueが、それ以外であればfalseが返ってくる。
$string; is_null($string); // true$string = 'test'; is_null($string); // false$string = null; is_null($string); // true//定義していない $a is_null($a); // true//空文字 is_null(''); // falseempty()
変数が空であるかどうかを検査する。「変数が空」のイメージがしっくり来ないので、以下でテスト。
$string; empty($string); // true$string = 'test'; empty($string); // false$string = null; empty($string); // true//定義していない $b empty($b); // trueここまでは、is_nullと挙動が同じだなーって思ったが、以下の挙動が違った。
//空文字 empty(''); // true空文字は「変数が空である」と捉えられるらしい。
isset()
変数が宣言されていること、そして NULL とは異なることを検査する。
つまり、
- 変数が宣言されている
- その変数がnullではない
という2つの条件が整うとtrueが返ってくる。is_nullとemptyとは異なり2つ条件があるっぽい。
is_nullとemptyとは「あったらtrue」という感じでニュアンスが違う。
$string; isset($string); //false (変数は宣言されているが、nullなのでアウト)$string = 'test'; isset($string); // true (変数は宣言され、文字列が与えられているのでセーフ)$string = null; isset($string); // false (変数は宣言されているが、nullなのでアウト)// 定義していない $c isset($c); // false (変数が宣言されていないのでアウト)なるほど。じゃあ変数としてではなく、直接文字列とか与えたらどうなる??(以下のように)
isset(''); isset('string');結果は、
PHP Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)ダメらしい。
まとめ
上の結果を以下の表にまとめました。
$string $string = 'test' $string = null 定義していない変数 空文字を直接 is_null true false true true false empty true false true true true isset false true false false 空文字にかかわらず直接与えるとエラー
- 投稿日:2020-08-14T01:03:17+09:00
Laravelで掲示板制作 #2 『DB作成・画面表示』
学習ノートのシリーズに書いてあることは細かい説明なしで進めます。
一応コメント来たら返すつもりでいます。
DB
DBはMySQLです。
データベース作成と確認だけphpAdminでやってます。DB作成
laravel_anonymous_boardという名前でデータベース作成して.envを書き換える.envDB_DATABASE=laravel_anonymous_boardモデルとマイグレーション
最初にあるやつ削除
デフォルトでユーザー登録用のマイグレーションファイルが作成されているので消す。
モデルとマイグレーションファイル作成
コマンドでモデルとマイグレーションファイル作成。
-mオプションでマイグレーションファイルを一緒に作成できる。php artisan -m make:model Postマイグレーションファイル編集
Postテーブルは以下のように作ると決めたのでマイグレーションファイルもそれに合わせて編集。
create_atはtimestamps()を使います。
column type id PK nickneme varchar(24) content_text varchar(255) create_at timestamp migrations/create_posts_table.phppublic function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('nickname', 24); $table->string('content_text', 255); $table->timestamps(); }); }マイグレーション
これもコマンドで。
php artisan migrate画面
Vueインストール
UI変えたいのでコマンドでインストール。
入れるけどSPAじゃないです。
これやるとBootstrapが入るっぽいのでとりあえず入れます。composer require laravel/ui php artisan ui vue npm install npm run devView
Viewファイル作成
resources/viewsにhome.blade.phpを作成します。resources/views/home.blade.php<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="/css/app.css"> <link rel="stylesheet" href="/css/main.css"> <title>あのにボード</title> </head> <body> <div> <div class="header"> <span>あのにボード</span> </div> </div> </body> </html>route設定
home.blade.phpを表示するようにroutes.phpを編集。routes.phpRoute::get('/', function () { return view('home'); });起動して表示
とりあえず起動して表示確認。
コマンドで起動してlocalhost:8000でアクセス。
アクセスできたらOK。php artisan serve
