20200814のlaravelに関する記事は14件です。

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.php
use 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はどんなことができるか調べておくと良いかもしれません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで掲示板制作 #3 『投稿を画面に表示』

今回はデータベースの投稿データを画面に表示します。
わからない部分は学習ノート記事を見てもらうか公式ドキュメント見てください。

シーディング

そもそも表示するものがないとなのでデータを入れる。
どうやらモデルを介してインサートするとTimestampが自動で入るらしい。

php artisan make:seeder PostsSeeder
PostsSeeder.php
public function run()
{
  //
  Post::create([
    'nickname' => 'テストくん',
    'content_text' => 'おはようございます、テストくんです'
  ]);
  Post::create([
    'nickname' => 'テストちゃん',
    'content_text' => 'こんにちは、テストくんです'
  ]);
}
DatabaseSeeder.php
public function run()
{
  $this->call(PostsSeeder::class);
}
php artisan db:seed

画面に表示

データが入ったので画面に表示します。

コントローラ作成

コマンドで作成。

php artisan make:controller HomeController

関数作成

作成したHomeController.phpにルーティングで入ってくる関数を作る。
Postモデルから日付順で取ってきてhomeビューを返します。

HomeController.php
use App\Post;
// 省略
  public function get_index(){
    $posts = Post::orderBy('created_at')->get();
    return view('home', ['posts' => $posts]);
  }

ビュー作成

foreachディレクティブでそれぞれ表示させます。
cardm-1p-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.php
Route::get('/', 'HomeController@get_index');

表示確認

いつも通り

php artisan serve

手順抜けてたらごめんなさい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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への変換 = いわゆるシリアライズした上でのデータの受け渡しはときにリスクを伴う、ということは覚えておいても良いかもしれません。

お読み頂きありがとうございました。
ではまたどこかで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 に「山田」が入った状態。

前方と後方に付加した '%' 文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orz

EmployeeController.php(抜粋)
    // 「名前」による絞り込み
    $name = $request->query('name');
    if (!is_null($name)) {
        $employee = $employee->where('name', 'like', '%' . $name . '%');
    }

調べて分かったこと

結論から言うと,Illuminate\Database\Eloquent\Collection での where 利用は,like が使えないようです。

API 仕様書では,第二パラメータ $operator で指定できるような思わせぶりなので,すっかり,ハマってしまいました。

api.png

どう対応したか

結局,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 指定ができないという記述は発見できずじまいでした。

https://laravel.com/docs/7.x/eloquent

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 に「山田」が入った状態。

前方と後方に付加した '%' 文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orz

EmployeeController.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 さん,貴重なアドバイス,感謝いたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 に「山田」が入った状態。

前方と後方に付加した '%' 文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orz

EmployeeController.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 さん,貴重なアドバイス,感謝いたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 に「山田」が入った状態。

前方と後方に付加した '%' 文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orz

EmployeeController.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::wherelike 演算子が使えない(動かない。そういう仕様?
  • Illuminate\Database\Eloquent\Builder::wherelike 演算子が使える

これからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。

@nunulk さん,貴重なアドバイス,感謝いたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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")にする事も検討ください。わかりやすいさいと

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/guzzle
use 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',
    ],
]);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.js
function 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レポジトリを完成させることも夢ではありません。みなさんの参考になれば幸いです。

もっとこうした方がいい!!、ここは間違ってる!! 等ございましたら遠慮なくお申し付けください!
もっとスマートなコードの書き方あると思うので、思いつく方いらっしゃいましたらご教授お願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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);で解決した時点でバリデーションを含む一連の処理が完了してます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.php
 public function boot()
        {
            $this->registerPolicies();

            // 管理者権限設定
            Gate::define('isAdmin',function($user){
                return $user->role === 1;
            });
        }

3. ルーティングを作成して完了

管理者にだけアクセス許可したいページとコントローラーを作成します。
ルーティングをグループにまとめ、ミドルウェアを設定したら完了です。

web.php
    Route::middleware(['auth','can:isAdmin'])->group(function(){
        Route::get('/admin','AdminController@index');
    });

Laravel 7.x ルーティング

その他参考:
Laravel Gate(ゲート)、Policy(ポリシー)を完全理解 | アールエフェクト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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(''); // false

empty()

変数が空であるかどうかを検査する。「変数が空」のイメージがしっくり来ないので、以下でテスト。

$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 空文字にかかわらず直接与えるとエラー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで掲示板制作 #2 『DB作成・画面表示』

学習ノートのシリーズに書いてあることは細かい説明なしで進めます。

一応コメント来たら返すつもりでいます。

DB

DBはMySQLです。
データベース作成と確認だけphpAdminでやってます。

DB作成

laravel_anonymous_boardという名前でデータベース作成して.envを書き換える

.env
DB_DATABASE=laravel_anonymous_board

モデルとマイグレーション

最初にあるやつ削除

デフォルトでユーザー登録用のマイグレーションファイルが作成されているので消す。

モデルとマイグレーションファイル作成

コマンドでモデルとマイグレーションファイル作成。
-mオプションでマイグレーションファイルを一緒に作成できる。

php artisan -m make:model Post

マイグレーションファイル編集

Postテーブルは以下のように作ると決めたのでマイグレーションファイルもそれに合わせて編集。
create_attimestamps()を使います。

column type
id PK
nickneme varchar(24)
content_text varchar(255)
create_at timestamp
migrations/create_posts_table.php
public 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 dev

View

Viewファイル作成

resources/viewshome.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.php
Route::get('/', function () {
    return view('home');
});

起動して表示

とりあえず起動して表示確認。
コマンドで起動してlocalhost:8000でアクセス。
アクセスできたらOK。

php artisan serve
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む