20210122のlaravelに関する記事は9件です。

Laravelで画像を表示する方法

PHP、Laravelの初学者です。

Laravelで画像を表示させる方法がわかったので、メモしておきたいと思います。

※ 使用しているLaravelのバージョンは6.xです。違うバージョンをご使用の方はご注意ください。
また、私はMacを使用しているので、Windowsをご使用の方もご注意ください。

Laravelで画像を表示させる方法

①「自分のプロジェクト/storage/app/public」の中に利用したい画像を置く。

 私は「/storage/app/public」にtop-page.jpgを入れました(以下の画像をご覧ください)。

image.png

② シンボリックリンクを張る

Laravel6.xの公式ドキュメントを見たところ、
「/storage/app/public」においた画像を使えるようにする(Webからのアクセスを許す)には、
「『public/storage』から『storage/app/public』へシンボリックリンクを張る」という作業が必要になるようです。

Laravel6.xの公式ドキュメント

シンボリックリンクって何だろう??

シンボリックリンクとは、オペレーティングシステム(OS)のファイルシステムの機能の一つで、特定のファイルやディレクトリを指し示す別のファイルを作成し、それを通じて本体を参照できるようにする仕組み。

こちらから引用

んー。まだ難しい。
もう少し噛み砕いて説明された記事がありました。

シンボリックリンクとは

要は「代理人」のような役割を持つのが、このシンボリックリンクだそうです。
例を引用させていただくと

例えば、ファイルAと、シンボリックリンクA’があるとしましょう。
シンボリックリンクA’は、ファイルAのシンボリックリンクです。このシンボリックリンクA’を開くと、ファイルAが開きます。
ファイルAを直接開いたのと同じ結果です。

要は、代理人みたいなものですね。
シンボリックリンクは、自分がそのファイルやフォルダであるかのように振る舞います。

とのことです。

さて、前置きが長くなりましたが、このシンボリックリンクを張るには、以下のコマンドを実行しましょう。

ターミナル
php artisan storage:link

③シンボリックリンクを張ることができたら、ビューファイルに以下のように記述すれば画像を表示できます。

index.blade.php
<img src="/storage/top-page.jpg">

image.png

こんな感じで成功しました!

以上です。
何かありましたらコメントで教えていただけますと幸いです。

ここまで読んでいただきありがとうございました。

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

【Laravel】オレ得 Eloquent/QueryBuilderメモ集

概要

よく忘れるので自分用メモ✏️
※日々追加...

データ取得

get

$users->get();
// 返り値は collection<StdClass>

first

$users->first();
// 返り値は StdClass

WHERE句

whereIn(含む)

$users->whereIn('id', [1, 2, 3]);

whereDate(日付)

$query->whereDate('created_at', '=', '2020-01-22');

JOIN句

LeftJoin

DB::table('users as U')
  ->select('U.name, U.age, J.role')
  ->leftJoin('jobs as J', 'j.user_id', '=', 'U.id');

AND/OR

AND検索

$query
  ->where('name', '=', $value1)
  ->where('age', '=', $value2);

OR検索

$query
  ->where('name', '=', $value1)
  ->orWhere('age', '=', $value2);

OR+AND検索

$query->where(function ($query) use ($keyword) {
     $query->where('name', 'LIKE', '%' . $keyword . '%')
           ->orWhere('age', 'LIKE', '%' . $keyword . '%');
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSとLaravelでAPI通信する際に日付データを合わせる(unixtime <-> Date)

概要

サーバーさん => Sさん
フロントさん => Fさん

あるときこんなやりとりを目撃しました。

Sさん < 日付のフォーマットは20200222で送ってください
Fさん < ここは2020/02/22で送ってください
Sさん < この場合は2020-02-22で送ってください
続く....

そのとき第三者の私はこう思いました。

私 < Unixtimeでやりとりすれば、同じフォーマットでやりとりできるんじゃね?と...

やってみた。

javascript
// Date -> UnixTime
new Date().getTime() / 1000;
// 1611320223.828

// UnixTime -> Date
new Date(unixTime * 1000);
// Fri Jan 22 2021 21:57:44 GMT+0900 (日本標準時)

※バックエンドはLaravelを使用しています

php
// Date -> UnixTime
$date = new DateTime('now');
$date->format('U');
// "1611320663"

// UnixTime -> Date
$dateTime = new DateTime('@' . (int)$unixTime);
$dateTime->setTimeZone(new DateTimeZone('Asia/Tokyo'));
// 2021-01-22 22:04:23.0 Asia/Tokyo (+09:00)

感想

一旦これでUnixtimeでやりとりできそう!
もっと良いやり方などがありましたら教えてください。

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

Laravel6でmorphToのリレーション先テーブルの参照するカラムを変更したい時

そもそもそんなテーブル設計は間違っているということはいったんさておき、表題の対応しなければいけないことがあったのでメモとして残す

default_templates
 - template_id
 - name

user_created_templates
 - id
 - name

reports
 - id
 - templatable_id
 - templatable_type

運用者が頻繁に更新する自動採番IDを持たないテーブルと、ユーザーが自分でCURDするテーブルがあってそれらをポリモーフィックで呼び出したいというケース。

いずれのテーブルもプライマリキーがidという場合にはモデル内部でmorphToで参照すればいい。

今回のケースで言えばtemplatable_typeに応じて参照するキーを動的に変更したいので下記のようにすると参照可能。

Models/report.php
class report exteds Model
{
    public function templatable()
    {
        $ownerKey = $this->getAttribute('templatable_type') === 'App\Models\DefaultTemplate'
            ? 'template_id'
            : 'id';
        return $this->morphTo('templatable', null, null, $ownerKey);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel checkboxで複数取得する方法 

はじめに

inputのcheckboxを使って

1. 複数チェックの取得
2. チェックしたデータを保持する方法

をまとめてみました。

テーブル設計とリレーション

今回はusersテーブルcategoriesテーブル使っていきます。

userは複数のcategoryを持ち、categoryも複数のuserに紐づけられているので

多対多のリレーションになります。

テーブル設計

◎usersテーブル

migration
public function up()
   {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
        });
   }



◎categoriesテーブル

migration
public function up()
   {
        Schema::create('categories', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
   }


◎category_userテーブル

migration
public function up()
   {
        Schema::create('category_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->integer('category_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
            $table->foreign('category_id')->references('id')->on('categories')->OnDelete('cascade');
            $table->timestamps();
        });
   }



category_userテーブルで

migration
$table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
$table->foreign('category_id')->references('id')->on('categories')->OnDelete('cascade');

でOnDelete('cascade')をつけておきましょう。 後々でdetachのときに一緒に削除するためです

リレーション設計

User.php
public function categories()
{
    return $this->belongsToMany(Category::class);
}

ここまで出来たら次はコントローラーの作成に入ります

コントローラー設計

Contorller
public function update(Request $request)
    {
        $user = Auth::user();

        $user->categories()->detach();
        $user->categories()->attach($request->category);


$user->categories()でリレーションをして、attach($request->category)でリクエストするカテゴリーを紐付けます。

テーブルでOnDelete('cascade')をつけたのは、detachで削除するときに、user_id, category_idを削除するためです。

次はBladeの設計です

Bladeの設計

ここが結構悩みました。 データの取得は出来たのですが、編集するときにデータの保持するのがなかなか詰まりました。

最初はoldを使ってデータ保持をやろうとしたのですがなかなかうまく行かず、oldを使わないでcheckedが含まれてるときでif文を作りました。

blade
@foreach ($categories as $category)
   @if($user->categories->contains('id', $category->id))
     <input type="checkbox" name="category[]" value="{{ $category->id}}" checked>
   @else
     <input type="checkbox" name="category[]" value="{{ $category->id}}">
   @endif 
    <label for="">
     {{ $category->name }}
    </label>
@endforeach

指定したidを判定するためにコレクションのcontains() メソッドを使って、指定したidが含まれているか確認しました。

ただ、oldを使ってデータ保持もやりたかったので、三項演算子を使って下記のようにも書いてみました。

blade
@foreach($categories as $category)
  <input type="checkbox" name="category[]" id=""  value="{{ $category->id }}" 
       {{ $category->id == old('category', $user->categories->contains('id', $category->id) ?? '') ? 'checked' : ''}}  >
    <label for="category">
       {{ $category->name  }}
     </label>  
@endforeach

保存して、また編集画面に遷移してもチェックボックスにcheckがついています!
色々なやり方がありますがご参考までに!!

参考

Laravel7.x コレクション contains

Laravel7.x リレーション

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

【PHP初心者】Laravelを2週間触ってみて理解したこと②【ルーティング】

1.はじめに

こんにちは。nakaです。
昨晩ホームベーカリーをセットし、今朝は小麦のいい匂いで朝を迎えました( ˘ω˘ )
パンが大好きなのでとても癒されました・・・

さて本題に入ります!前回に引き続きLaravelについてまとめたいと思います。
前回の記事:【PHP初心者】Laravelを2週間触ってみて理解したこと

今回はルーティングについてです。

2.ルーティングとは

初めてLaravelのソースを見たとき、「えーとControllerやろー・・・・ん?」
controllerにリクエストのURLがなくてびっくりしました。
Laravelは代わりにルーティングと呼ばれるものがその役割を担っているようです。
ルーティングのルートが「経路」と訳されるように、ユーザからのリクエストを受け取り、ある処理を実行するまでの案内人のようなイメージですね。

ルーティングは「routes/web.php」で設定できます。
Larabelをインストール後は、デフォルトのページの設定がされています。

web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

3.ルート定義メソッド

それでは上記ファイル(web.php)に書くHTTPリクエストに定義できるメソッドを見てみましょう。
メソッドでよく見るものは「GET」「POST」ですかね。

// GETリクエスト
Route::get($uri, $callback);

// POSTリクエスト
Route::post($uri, $callback);

// putリクエスト
Route::put($uri, $callback);

// patchリクエスト
Route::patch($uri, $callback);

// deleteリクエスト
Route::delete($uri, $callback);

ちなみにルーティングにはルートファザードを使います。
ファザードとはクラスをインスタン化せずに実行できる機能のことです。

4.コントローラーのルーティング

基本の形

例えばコントローラーへルーティングする際は、以下のように書きます。

web.php
Route::get('sample', 'SampleController@index');

http://xxx/sampleへアクセスすると、SampleControllerのindex()が呼ばれます。

パラメータ付き

またURLからパラメーターを取得したい場合は、以下のように書きます。

web.php
Route::get('sample/edit/{id}', 'SampleController@edit');
SampleContoroller.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleController extends Controller
{
    public function edit($id)
    {
        print_r($id);
    }
}

http://xxx/sample/edit/1へアクセスすると、「1」というパラメータを取得し、SampleContoroller.phpのedit()の$idという変数で値を取得できます。

ルーティングに名前を付ける

またルーティングに名前をつけることもできます。
URLをわざわざ書かなくも名前で呼び出せます。

web.php
Route::get('sample', 'SampleController@index')->name('sample.index');

6.リダイレクトのルーティング

上記の名前をつけると便利

web.php
Route::get('sample', 'SampleController@index')->name('sample.index');
SampleContoroller.php
return redirect()->route('sample.index');

5.ルートのグループ化

ルーティングをグループ化できます。

URLをまとめるときに使う「prefix」

管理者と会員など権限が複数の時に使うと便利

// ごちゃごちゃ
Route::get('admin/top', 'AdminContoroller@top');
Route::get('admin/setting', 'AdminContoroller@setting');
// 見やすい
Route::prefix('admin')->group(function () {
    Route::get('top','AdminContoroller@top');
    Route::get('setting','AdminContoroller@setting');
});

名前空間をまとめる「namespace」

配置先をまとめることができます。
以下の場合は、"App\Http\Controllers\Admin"のディレクトリ内を示しています。

App\Http\Controllers\Admin\TopColler@top
App\Http\Controllers\Admin\SettingContoroller@setting
// ごちゃごちゃ
Route::get('top','Admin\TopColler@top');
Route::get('setting','Admin\SettingContoroller@setting');
// 見やすい
Route::namespace('Admin')->group(function () {
    Route::get('top','TopColler@top');
    Route::get('setting','SettingContoroller@setting');
});

他ミドルウェアをまとめる「middleware」、ドメインをまとめる「domain」、ルート名をまとめることもできます。複数を一気に設定もできます(今回は割愛)

6.さいごに

忘れていましたがルーティングはphp artisan route:listで一覧が確認できます。ルーティングを理解するとソースの流れがわかりますね。
次回はBladeについて書く予定です(゜レ゜)
マークダウン少しづつ慣れてきた。

参考記事

Laravelルーティングの基本とよく使われるルーティングパターン
routesを使いこなす(3)prefixでgroupを分割

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

【Laravel】繰り返し処理は@includeを使う。PHPはビューではなくコントローラに記述する(リファクタリング)

リファクタリングの個人メモです。

  • ビューの中に繰り返し処理を記述している場合は、処理を別ファイルに切り出し、@includeで呼び出す

  • PHPの処理はビューの中に書かず、コントローラで渡す(他でも使う処理の場合は自作のヘルパー関数を作って渡す)

リファクタリング事例

リファクタリング前のコード

以下のように同じコードが繰り返されている処理をリファクタリングする。

phones.balde.php
  <div class="p-service__list">
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">xperia</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
  </div>
</div>

コードを見るとsectionが繰り返されているのがわかる。

繰り返しのコード
<section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>

@foreach(__('service') as $service)
言語ファイル( resources > lang > ja )のservice.phpに記述された配列を、$serviceとして一つづつ抜き出している。

@if( $service['category'] === 'iphone' )
抜き出した配列のキー名categoryの値がiphoneなら以下の処理を実行する。

このように、categoryの値によって表示するタイトルとデータを変えている処理。


リファクタリング

1. 繰り返しのコードを別ファイルに移動する。

繰り返されているコードを別ファイルに記述する。ここでは、section.blade.phpを作成。

呼び出しは@includeを使う。

section.blade.php
<section>
    <h2 class="c-page-headline">{{ $categoryTitle }}</h2> 
    @foreach($services as $service)
        <section class="p-service-section">
            <div>
            <span class="p-service-section__img-border">
                <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
            </span>
            </div>
            <div>
                <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                <p class="p-service-section__lead">{{ $service['lead'] }}</p>
            </div>
        </section>
    @endforeach
</section>

[1] <h2 class="c-page-headline">{{ $categoryTitle }}</h2>

h2タグのデータは変数$categoryTitleとし、@includeした時に渡すようにする。



[2] @foreach($services as $service)

以前の処理
@foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )

foreachでデータを一つづつ取得し、指定のカテゴリ名に一致する場合のみ実行(フィルタリング)していた処理を変更し、フィルタリング後のデータを$servicesとして渡すようにする。


2. PHPの処理をコントローラに移動する

コントローラ(リファクタリング前)
public function index(Request $request)
    {
        return view('contents.service.index');
    }

元の状態はビューファイルを返しているだけ。


コントローラ(リファクタリング後)
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

リファクタリング後はビューの中のforeach処理をコントローラ内で実行し、ビューに渡している。

▼foreachの処理

        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;

if( isset($service['category']))
変数$serviceのキーcategoryに値があるか確認。

$services[$service['category']][] = $service;
変数$servicesを新たに用意。

キー名がカテゴリー名となる連想配列にforeachで抜き出した値($service)を追加していく。



▼注意点
配列に要素を追加する場合はarray[] = '追加する値'とする。

array[キー名]のようにキー名を指定して代入すると上書きになってしまう。


3. ビューの修正(@includeで読み込む)

切り出したビューを読み込む。

@include('ビュー', ['変数名'=>'渡すデータ'])
┗ ビューはresources > views 配下のディレクトリパスで指定。
 ┗ ディレクトリはドットでつなぐ。
 ┗ 第2変数でビューにデータを渡す。

        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>

あんなに長かったビューが驚くほどシンプルになった。。
繰り返し処理があったら@includeが便利。


(補足)PHP処理のリファクタリングの考え方

リファクタリングLevel 0

リファクタリングなし

.blade.php
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
                //処理
              @endif
          @endforeach

 ↓ 処理を外部ファイルに移動し@importで読み込む

PHP処理をビューファイルの情報に

リファクタリングLevel 1

.blade.php
        @php
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }
        @endphp

        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>

 ↓ PHP処理をコントローラに移動

リファクタリングLevel 2

コントローラ
public function index(Request $request)
    {
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }

        return view('contents.service.index', compact('iphone', 'android', 'xperia'));
    }

ビューのPHP処理をそのままコントローラに移動。(★基本的に、PHP処理はビューではなくコントローラで行う)

compact('変数名', '変数名', '変数名',,,,,)
compactメソッドは指定した変数を連想配列にしてくれる。
キー名は変数名、値は変数に入っている値が自動的に適用される。

※compactの注意点
変数は$変数名ではなく、'変数名'で渡す。

 ↓ ビューに連想配列を渡す(compactメソッドを使わない)

リファクタリングLevel 3

コントローラ
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category']) && $service['category'] === 'iphone' ){
                $services["iphone"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'android' ){
                $services["android"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'xperia' ){
                $services["xperia"][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

$services["キー名"][] = 値
変数$servicesを新たに定義し、指定したキーに値を追加していく。

 ↓ カテゴリ名の分岐にifを使わない

リファクタリングLevel 4

コントローラ
    public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }

if( isset($service['category']))
ifを使うのは対象の値の存在確認のみ。

$services[$service['category']][]
キー名はテキストでベタ打ちせず、$service['category'で動的に指定する。

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

Laravel 8.x 時点での認証の実装

Laravel は多彩な認証ライブラリをリリースしています。

  • Tailwind CSS && Blade Templates で出来た Laravel Breeze
  • Tailwind CSS && (Livewire || Inertia) で出来た Laravel Jetstream
  • OAuth 2 プロバイダ実装の Passport
  • OAuth 2 ログイン実装の Socialite
  • (レガシーパッケージとなっていますが) Bootstrap && (React || Vue) で出来た laravel UI

どれが要件に合うのかという所でもかなり悩みどころですが、とにかく選択肢は豊富です。

今回はこれらのライブラリについて深掘りしていくより、 Laravel 自体の認証の実装方法について調べてみます。

イントロダクション

Laravel Authenticationページでは、最初にこのように書かれています。

多くのウェブアプリケーションは、ユーザがアプリケーションで認証して「ログイン」するための方法を提供しています。この機能をウェブアプリケーションに実装することは、複雑でリスクを伴う可能性があります。このため、Laravelは、認証を素早く、安全に、簡単に実装するために必要なツールを提供するように努めています。
Laravelの認証機能は「ガード」と「プロバイダ」で構成されています。ガードは、各リクエストに対してユーザーがどのように認証されるかを定義します。例えば、Laravelにはセッションガードが搭載されており、セッションストレージとクッキーを使用して状態を維持します。
プロバイダーは、永続的なストレージからユーザーを取得する方法を定義します。LaravelはEloquentとデータベースクエリビルダを使ったユーザーの取得をサポートしています。しかし、アプリケーションに必要に応じて追加のプロバイダを自由に定義することができます。
アプリケーションの認証設定ファイルはconfig/auth.phpにあります。このファイルには、Laravelの認証サービスの動作を微調整するためのいくつかのオプションが含まれています。
www.DeepL.com/Translator (無料版)で翻訳しました。

Config

認証情報の設定は config/auth.php にまとまっています。

<?php

return [
    // デフォルト(Guard未指定)での利用 Guard(`Auth::user()` などした時に使われるもの)
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    // Guard の実装リスト。 Middleware の `auth:api` や `Auth::guard('api')` のように明示指定出来る
    'guards' => [
        'web' => [
            // SessionGuard を利用し、プロバイダは `users` (後述のキー値)
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            // TokenGuard を利用し、プロバイダは `users`, ハッシュ化はしない
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    'providers' => [
        // 'users' プロバイダは Eloquent を利用する
        // 'users' というキー名は上記 Guard の 'provider' に指定するもの
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        // 代わりに DatabaseUserProvider を指定することも可能
        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
// ... 以下省略
];

Guard

どのリクエスト情報(session だとか Authorization ヘッダーだとか)からユーザ情報を取得出来るかの実装です。

<?php

namespace Illuminate\Contracts\Auth;

interface Guard
{
    /**
     * 現在のユーザが認証されているかどうか
     *
     * @return bool
     */
    public function check();

    /**
     * 現在のユーザがゲスト(未ログイン)かどうか
     *
     * @return bool
     */
    public function guest();

    /**
     * 現在の認証されたユーザーを取得する
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user();

    /**
     * 現在の認証されたユーザーの ID を取得する
     *
     * @return int|string|null
     */
    public function id();

    /**
     * ユーザーの秘匿情報をバリデートする
     *
     * @param  array  $credentials
     * @return bool
     */
    public function validate(array $credentials = []);

    /**
     * 現在のユーザーを設定する
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @return void
     */
    public function setUser(Authenticatable $user);
}

このインターフェースの実装となります。

これに対する Laravel 上での実装は RequestGuard, TokenGuard, SessionGuard の三つになります。

RequestGuard

これは HTTP Request 内の任意の要素を使ってログイン判定をする最も簡単な Guard です。

Auth::viaRequest('custom-token', function (Request $request) {
  return User::where('token', $request->token)->first();
});

このようにして新しいドライバーをクロージャで作ることが出来ます。

return [
  'guards' => [
    'api' => [
      'driver' => 'custom-token',
    ],
  ],
];

config/auth.php でこのようにドライバーを指定することで機能します。簡単。

これはステートレスなのでセッションを張らずに認証が可能です(代わりに毎回同じトークンを渡す必要があります)。

TokenGuard

これはリクエストからトークンを取得し、それをもって認証する Guard です。

  • クエリストリング /foo?api_token=aaa
  • リクエストデータ curl --data '{"api_token":"aaa"}'
  • Bearer トークン Authorization: Bearer aaa
  • パスワード https://foo:aaa@example.com

これらを探索し、トークンが見つかればそれを利用してログインを試みます。 api_token というキー名は config で上書きが可能です。

これもステートレスです。

SessionGuard

これはクッキーとストレージにセッション情報を残し、ステートフルに認証する Guard です。

普通にブラウザでアクセスする要件ではまずこれを使うのが基本になると思います。


UserProvider

Guard は「クライアントから送られてきた認証情報をどう取得するか」を定義したものに対し、 UserProvider は「取得した認証情報からデータベース内のユーザー情報をどう取得するか」を定義したものです。

<?php

namespace Illuminate\Contracts\Auth;

interface UserProvider
{
    /**
     * ユニークIDでユーザーを取得する
     *
     * @param  mixed  $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier);

    /**
     * ユニークIDと "remember me" トークンでユーザーを取得する
     *
     * @param  mixed  $identifier
     * @param  string  $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token);

    /**
     * 渡されたユーザーの "remember me" トークンを更新する
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  string  $token
     * @return void
     */
    public function updateRememberToken(Authenticatable $user, $token);

    /**
     * 渡された秘匿情報からユーザーを取得する
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials);

    /**
     * 渡された秘匿情報を使ってユーザーをバリデートする
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(Authenticatable $user, array $credentials);
}

インターフェースはこのようになっています。

remember token が必須になっているのは若干モヤっとする設計ですが、基本的に渡されたパラメータでデータベースからユーザーを取得するメソッドを実装することになります。

Laravel では EloquentUserProviderDatabaseUserProvider が実装されています。

EloquentUserProvider

最初からある App\Models\User クラスのような ORM を使って取得する手法です。最も一般的です。

DatabaseUserProvider

コンフィグからテーブル名を指定して password カラムを使って取得する手法です。カラム名が password 固定なのがモヤっとするというか、使いづらい所ですね。


ちなみに RequestGuard を利用する場合はこの UserProvider を通さずにログインすることが出来ます(指定したクロージャで DB にアクセスしているので)。


この UserProvider で利用されるユーザー情報は Illuminate\Contracts\Auth\Authenticatable インターフェースを実装したクラスである必要があります(Eloquent である必要はありません)。

<?php

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    /**
     * ユーザーのユニークIDのカラム名を取得する
     *
     * @return string
     */
    public function getAuthIdentifierName();

    /**
     * ユーザーのユニークIDを取得する
     *
     * @return mixed
     */
    public function getAuthIdentifier();

    /**
     * ユーザーのパスワードを取得する
     *
     * @return string
     */
    public function getAuthPassword();

    /**
     * "remember me" セッション用のトークンを取得する
     *
     * @return string
     */
    public function getRememberToken();

    /**
     * "remember me" セッションのトークンを設定する
     *
     * @param  string  $value
     * @return void
     */
    public function setRememberToken($value);

    /**
     * "remember me" トークンのカラム名を取得する
     *
     * @return string
     */
    public function getRememberTokenName();
}

またこの Authenticatable のデフォルト実装 trait として Illuminate\Auth\Authenticatable を利用することが出来ます。

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

LaravelでDBに保存した画像を表示する方法

はじめに

前回Laravelで画像のパスをDBに保存する方法をご紹介したので、今回は画像を表示する方法関してご紹介していきたいと思います。

保存する方法に関しては以下の記事をご参考ください。
Laravelで画像をアップロードし、パスをDBに保存する方法

こちらでは、画像のパスがすでにDBに保存されている前提で進めており、ファイル名等は全て前回作成したものを利用していきます。

-各バージョン
-laravel 6.x
-PHP 7.4.9
-mySQL 5.7.30

コントローラーを記述する

今回はindexアクションを使っていきたいと思います。

DBファザード使用するため、useで宣言し、そのあとに書いていきます。

BookController
use Illuminate\Support\Facades\DB;

public function index()
{
  $books = DB::table('books')->orderBy('created_at', 'desc')->get();

  return view('index', compact('books'));
}

まず、前回作成したbooksテーブルのデータを取得し、orderBy()created_at、つまり作成した日時をdesc順に並べるように指定しています。descとはdescendの略で、下に降りるという意味になるので、指定することによって作成した日時が新しいものが先頭に来るようになります。
そして取得したものを$booksという変数に格納しています。

続いて格納したものをreturnでビューに渡すようにします。
index.blade.phpというビューをこのあと作成するので、indexを指定し、compact()メソッドで変数をビューに渡すことができるようになっています。compact()を使用するときは$マークはつけずに記述して問題ありません。

また、今回はデータが少ないため全てのデータを取得していますが、もしデータが多く一覧画面では一部のデータのみ表示させたい場合は、以下のように書くこともできます。

BookController
use Illuminate\Support\Facades\DB;

public function index()
{
  $books = DB::table('books')
  ->select('id','title', 'author', 'created_at') //ここを追加
  ->orderBy('created_at', 'desc')
  ->get();

  return view('index', compact('books'));
}

select()でデータを選択することができるので、表示したいカラム名を指定することができます。
この場合は別途、全てのデータを表示する画面を作成するといいでしょう。

ビューを作成する

新しくデータを表示するindex.blade.phpを作成し、一覧を表示させるコードを書いていきます。
なお、今回はデータが少ないので全てのデータが表示されるようにしていきます。

index.blade.php
@foreach('books' as 'book')
  <table>
    <tr>
      <th>タイトル</th><td>{{ $book->title }}</td>
    </tr>
    <tr>
      <th>作者</th><td>{{ $book->author }}</td>
    </tr>
    <tr>
      <th>投稿日</th><td>{{ $book->created_at }}</td>
    </tr>
    <tr>
      <th>画像</th><td><img src="{{ asset('uploads/books/' . $book->image) }}" alt="book-image"></td>
    </tr>
  </table>
endforeach

BookControllerでbooksテーブルのデータを$booksに格納しているので、データをforeachで回して表示するようにしています。
画像に関しては、DBには画像のパスしか保存されていないため、画像の保存先を指定する必要があります。画像は前回Publicフォルダ内のuploads/booksに保存したので、今回はそのように指定しています。
また、見えやすくするようにtableを使用しましたが、便宜に合わせてulpタグで表示することも可能です。

ルーティングを指定する

index.blade.phpに接続したら、BookController内のindexアクションが起動するようにします。

web.php
Route::get('index', 'BookController@index')->name('index');

以上で完成です!

さいごに

今回はDBに保存している画像のパスを表示させる方法に関して解説しました。

もっとこういうやり方あるよーという場合は、やさしくコメントで教えていただけるとありがたいです!
ここまで読んでくださりありがとうございました!

参考

Laravel Image CRUD : How to fetch the image in Laravel

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