20200207のlaravelに関する記事は8件です。

【Laravel】デフォルトの管理画面に左メニューをサクッと設置

最近Laravelはじめました。

Laravelにはデフォでユーザ管理機能が存在しますが、そのログイン後の画面はコンテンツエリアのみのレイアウトとなってます。これをよくある固定左メニュー+コンテンツエリアのレイアウトにしてみます。

(完成形)
スクリーンショット_2020-02-07_17_26_06.png

0. 管理機能の有効化

ver5系ではphp artisan make:authコマンドでユーザ管理機能がセットアップできたのですが、6系の場合は以下のリンク等を参考にセットアップしてください。

Laravel 6系でmake:authを使う方法
Laravel6 備忘録 −ユーザー認証(Auth)−

うまくセットアップできれば、/resources/views下に以下のファイルが生成されているはずです。
home.blade.php
今回は認証後の最初のビューであるこれを元に話をすすめます。

1. home.blade.phpから左メニューの元ネタをコピー

<div class="col-md-12">から閉じの</div>までをコピーします。

/resources/views/home.blade.php
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">

        // 略

2. menu.blade.phpを新規作成

1でコピッたソースを貼り付けて、col-md-12col-md-2に変更してお好みに応じてカスタマイズします。

/resources/views/menu.blade.php
  <div class="col-md-2">
      <div class="card">
          <div class="card-header"><i class="fas fa-th-list"></i></i> MENU</div>
          <div class="card-body">
              <div class="panel panel-default">
                  <ul class="nav nav-pills nav-stacked" style="display:block;">
                      <li><i class="fas fa-user-alt"></i> <a href="#">XXXXXXXX</a></li>
                      <li><i class="fas fa-user-alt"></i> <a href="#">XXXXXXXX</a></li>
                      <li><i class="fas fa-user-alt"></i> <a href="#">XXXXXXXX</a></li>
                </ul>
              </div>
          </div>
      </div>
  </div>

3. home.blade.phpを修正

col-md-12col-md-10に変更して、その直前の行に@include('menu')を。

menu.blade.php
<div class="container">
    <div class="row justify-content-center">

        <!-- left menu -->
        @include('menu')

        <div class="col-md-10">
            <div class="card">

        // 略

ちなみに今回はこんなCSSをあててみました。

.col-md-2 .card-body {
    padding:0;
}
.col-md-2 ul {
  padding: 0;
  position: relative;
}

.col-md-2 ul li {
  line-height: 1.8;
  padding: 0.5em 0.5em 0.5em 0.7em;
  list-style-type: none!important;
  background: -webkit-linear-gradient(top, #whitesmoke 0%, whitesmoke 100%);
  background: linear-gradient(to bottom, whitesmoke 0%, #dadada 100%);
  text-shadow: 1px 1px 1px whitesmoke;
  color: black;
}

以上、LaravelというようりBootstrapのグリッドシステムを利用した小ネタでした。今回は2:10でメニューとコンテンツエリアを分割しましたが、12(グリッドの個数は最大12)の範囲内で3分割なども可能です。

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

laravel6.0でのauthの実装

完全に自分用のメモです。

composerでlaravel/uiをインストール

6系ではvue.jsやreactを使うにはuiコマンドによってインストールする必要がある。

$ composer require laravel/ui --dev

authコマンドを実行

$ php artisan ui vue --auth

または

$ php artisan ui react --auth

npmのインストール、ビルド

$ npm install

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

Laravel で .env の値が取得できない

おさらい

  • Laravel では環境変数の設定に .env を使う
  • .env の値にアクセスするには env('<config>.<key>') とする
    • 例: env('app.url')
  • 設定 5.7 Laravel

問題

  • ある状況で env('app.url')null が返ってくるようになった
    • とくになにもアプリケーションコードさわってない

原因

  • 設定ファイルをキャッシュすると、env() 関数が null を返すようになる
    • php artisan config:cache

Note: 開発過程の一環としてconfig:cacheコマンド実行を採用する場合は、必ずenv関数を設定ファイルの中だけで使用してください。設定ファイルがキャッシュされると、.envファイルはロードされなくなり、env関数の呼び出しは全てnullを返します。

設定 5.7 Laravel https://readouble.com/laravel/5.7/ja/configuration.html#configuration-caching

対策

  • config ファイルで env 関数を使う
config/app.php
<?php

return [
    'name' => env('APP_NAME', 'Laravel'),
    'env' => env('APP_ENV', 'production'),
    'debug' => env('APP_DEBUG', false),
    // ...
  • そして config() 関数でアクセスする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel用コマンドチートシート

Laravelプロジェクトの作成

composer create-project --prefer-dist laravel/laravel プロジェクト名

起動

$ cd SampleProject
$ php artisan serve

モデルの作成

$ php artisan make:model CreateModel

コントローラーの作成

$ php artisan make:controller CreateController

マイグレーションコマンド系

$ php artisan make:migration make_migration    マイグレーションファイル作成
$ php artisan migrate                          マイグレーションの実行
$ php artisan migrate:refresh                  全てのマイグレーションをリセットし、再度実行
$ php artisan migrate:reset        全てのマイグレーションをロールバック

シーダーコマンド系

$ php artisan make:seeder UsersSeeder          シーダーファイル作成
$ php artisan db:seed                          シーダー実行
$ php artisan db:seed --class=UsersSeeder

Seederを逆生成する

サーバーを移行する際に、データベースを引っ越すときに使えるかもしれない。
あとは新規でプロジェクトに入った人とかにローカル環境のデータを皆で共有するときとかに

$ composer require --dev "orangehill/iseed"
$ vim config/app.php
'providers' => [
    Orangehill\Iseed\IseedServiceProvider::class,       #追加
],
$ php artisan iseed {table_name}

仮に、categoriesテーブルだとすると、以下のようになります。

CategoriesTableSeeder.php
// database/seeds/CategoriesTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class CategoriesTableSeeder extends Seeder
{

    /**
     * Auto generated seed file
     *
     * @return void
     */
    public function run()
    {


        \DB::table('categories')->delete();

        \DB::table('categories')->insert(array (
            0 => 
            array (
                'id' => 1,
                /* ...(中略)... */
            ),
            1 => 
            array (
                'id' => 2,
                /* ...(中略)... */
            ),
            2 => 

キャッシュクリア系

Laravel、Apacheやnginxを再起動したとしても、内部のキャッシュが残っててしまって、更新したプログラムをうまく反映してくれない時がある。

$ php artisan cache:clear
$ php artisan config:clear
$ php artisan route:clear
$ php artisan view:clear

随時追加予定。

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

laravel: パスワードリセット by Auth

参考

Laravelパスワードリセットメールの文面・デザインを変更する
Laravel5.6でバリデーションなどのエラーメッセージを日本語化する方法
laravel-resources-lang-ja
Laravel 5.6 パスワードリセット
php - Laravel 58でカスタムPasswordBroker sendResetLink()メソッドを拡張または作成する方法は?

.env
//メール送信設定
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USERNAME={address_id}@gmail.com
MAIL_PASSWORD={mail_pass}
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS={email_address}
MAIL_FROM_NAME={name}

ここまでで下記画面からのパスワードリセットメールが送信できるようになります。
WS000007.JPG

パスワードリセットメールのテンプレートビュー

resource/view/mail/passwordreset.blade.php
//参考サイト様より
<!DOCTYPE html>
<html lang="ja">
<style>
  body { 
    background-color: #fffacd;
  }
  h1 {
    font-size: 16px;
    color: #ff6666;
  }
  #button {
    width: 200px;
    text-align: center;
  }
  #button a {
    padding: 10px 20px;
    display: block;
    border: 1px solid #2a88bd;
    background-color: #FFFFFF;
    color: #2a88bd;
    text-decoration: none;
    box-shadow: 2px 2px 3px #f5deb3;
  }
  #button a:hover {
    background-color: #2a88bd;
    color: #FFFFFF;
  }
</style>
<body>
<h1>
  パスワードリセット
</h1>
<p>
  以下のボタンを押下しパスワードリセットの手続きを行ってください
</p>
<p id="button">
  <a href="{{$reset_url}}">パスワードリセット</a>
</p>
</body>
</html>

通知クラス(Notification)を継承するパスワードリセットクラスの作成

$ php artisan make:notification PasswordResetNotification
app/Notification/PasswordResetNotification.php
//追加
public $token;
protected $title = 'パスワードリセット 通知';

//変更
public function __construct($token)
{
    $this->token = $token;
}

public function toMail($notifiable)
{
    return (new MailMessage)
        ->subject($this->title)
        ->view('mail.passwordreset',
            [
                'reset_url' => url('password/reset', $this->token),
            ]);
}

作成したPasswordResetNotificationクラスを利用するために。

app/User.php
//追加
use App\Notifications\PasswordResetNotification;

//メソッドのオーバーライド
public function sendPasswordResetNotification($token)
{
    $this->notify(new PasswordResetNotification($token));
}

これでパスワードリセットできるようになります。

カスタマイズ

期限の変更設定

config/auth.php
//expireオプションで設定
'passwords' => [                     
    'users' => [                     
        'provider' => 'users',       
        'table' => 'password_resets',
        'expire' => 30, // <- ココ
    ],                               
],                                   

パスワードリセット後のリダイレクト先

app/Http/Controller/Auth/ResetPasswordController.php
//プロパティを変更
protected $redirectTo = '/home';

フラッシュメッセージの日本語化

resource/lang/ja/password.php
<?php
    return [
        'password' => 'パスワードは6文字以上にして、確認用入力欄と一致させてください。',
        'reset' => 'パスワードは再設定されました!',
        'sent' => 'パスワード再設定用のURLをメールで送りました。',
        'token' => 'パスワード再設定用のトークンが不正です。',
        'user' => "メールアドレスに一致するユーザーが存在しません。",
    ];

メールアドレスが見つからなくても同じメッセージを出力

app/Http/Controller/Auth/ForgetPasswordController.php
//追加
use Illuminate\Http\Request;
//オーバーライド
public function sendResetLinkEmail(Request $request)
{
    $this->validateEmail($request);
    $response = $this->broker()->sendResetLink(
        $request->only('email')
    );
    return back()->with('status', 'パスワード再設定用のURLをメールで送りました。');
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelの認可処理はGateとPolicyどっちを使えばいいのかわからない

概要

Laravelには認可処理として、GateとPolicyというものが用意されています。

最初はよくわからなくてどっちがどっちなんだ?と思うでしょう。

結論

わからなかったらGateをつかえばよい。

そもそも認可処理を担当するクラスはIlluminate/Auth/Access/Gate.phpで、Policyも結局はGateを使っています。

Policyはあくまでもモデルに対しての認可処理を簡単にかけるようにしたものです。

Policyのmiddlewareでの指定方法

Policyでの認可処理の書き方の一つにルートでミドルウェアとして書く方法があります。

Route::post('/book', 'HogeController@update')->middleware('can:update,book');

canは固定で、:のあとの1つめはポリシーのメソッド名になります。そのあとのは主に書き方が2種類あります。

モデルをつかわないメソッドの場合

ポリシーで以下のようなメソッドを定義したとします。

public function create(User $user)
{
}

このメソッドは引数にユーザーしか受け取っていません。

引数にモデルを渡さない場合の書き方は、

Route::get('/create', 'HogeController@create')->middleware('can:create,App\\Book');

とこのように完全修飾名を渡してあげます。この文字列はサービスプロバイダーに登録されたポリシーを探し出すためのkeyとしてのみ使用されます。

モデルをつかうメソッドの場合

public function update(User $user, Book $book)
{
}

このメソッドは引数にユーザーしか受け取っていません。

引数にモデルを渡さない場合の書き方は、

Route::put('/{book}', 'HogeController@store')->middleware('can:update,book');

としてあげます。第一引数の{}の中身と同じ名前を指定しなければいけないことに注意してください。

ルートモデルバインディングを利用しているので合わせる必要があります。

Policyを使うときにはAuthServiceProviderに登録するのを忘れないようにしましょう。(よく忘れて怒られる...)

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

認可処理であるPolicyのソースコードリーディング(middleware編)

ポリシーのソースコードリーディング

がんばりましょう。

Gateファサード

解決されるクラス Illuminate/Auth/Access/Gate.php

ポリシーの登録

ポリシーのマッピングはapp/Providers/AuthServiceProviderに記述します。

keyに関連するモデル、valueにポリシーを記述します。

app/Providers/AuthServiceProvider
protected $policies = [
    // 'App\Model' => 'App\Policies\ModelPolicy',
]

boot()の中で呼ばれているregisterPolicies()で登録していきます。

Illuminate/Foundation/Support/Providers/AuthServiceProvider.php
public function registerPolicies()
    {
        foreach ($this->policies() as $key => $value) {
            Gate::policy($key, $value);
        }
    }

$this->policies()は先程のpoliciesプロパティを返します。

Illuminate/Auth/Access/Gate::policies()が呼ばれます。

Illuminate/Auth/Access/Gate.php
public function policy($class, $policy)
{
    $this->policies[$class] = $policy;

    return $this;
}

Illuminate/Auth/Access/Gatepoliciesプロパティに同じ形で値を格納していきます。

これにてポリシーの登録は終了。

ポリシーをつかう

主なポリシーの使用方法は3つで、

  • User.phpから使う
  • Controllerから使う
  • ミドルウェアで使う

中身は大体同じなので、今回はミドルウェアで使うを例に見ていきます。

想定ルート、ミドルウェア

Route::get('/{book}/{comment}', 'BookController@index')->middleware('can:view,book,comment');

実際にアクセスされたURL

/1/2

bookはルートモデルバインディングで解決され、commentは解決されないものとします。

ここでの注意点は、middleware()内に書いた文字列のbookcommentに当たる部分は必ず同じ名前でURIに記載してください。

たとえば、

Route::get('/{book}/hoge', 'BookController@index')->middleware('can:view,book,comment');

のように、middleware()内にcommentと書いてあるのに、getの第一引数の中に{comment}が含まれていないと怒られます。

ポリシーはリソース(モデル)に対しての認可を提供するので、ルートモデルバインディングで解決されたインスタンスを利用します。(なのでミドルウェアの順番も重要です)

想定ポリシー

protected $polices = [
    Book::class => BookPolicy::class
]

B
BookPolicy::view()が定義されている。

みていこう

まずは'can:view,book,comment'がどのようにミドルウェアに変換されるか見ていきましょう。

ミドルウェアをごにょごにょしているのはここです。

Illuminate/Pipeline/Pipeline.php
protected function carry()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            [$name, $parameters] = $this->parsePipeString($pipe);

            $pipe = $this->getContainer()->make($name);

            $parameters = array_merge([$passable, $stack], $parameters);

            $carry = method_exists($pipe, $this->method)
                            ? $pipe->{$this->method}(...$parameters)
                            : $pipe(...$parameters);

            return $this->handleCarry($carry);
        };
    };
}

$pipeには'can:view,book'という文字列が入り、$passableにはRequestクラス、$stackは次のミドルウェアが入っています。

parsePipeString()でミドルウェア名とその他に分けます。

Illuminate/Pipeline/Pipeline.php
protected function parsePipeString($pipe)
{
    [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);

    if (is_string($parameters)) {
        $parameters = explode(',', $parameters);
    }

    return [$name, $parameters];
}

$name = 'can'
$parameters = ['view', 'book', 'comment']

$this->getContainer()->make($name)Illuminate/Auth/Middleware/Authorize.phpインスタンスを生成します。
(文字列とミドルウェアの対応はapp/Http/Kernel.phpに書いてあります。)

$this->methodにはhandleが入っているので、Illuminate/Auth/Middleware/Authorize::handle()が実行されます.

Illuminate/Auth/Middleware/Authorize.php
public function handle($request, Closure $next, $ability, ...$models)
{
    $this->gate->authorize($ability, $this->getGateArguments($request, $models));

    return $next($request);
}

$abilityviewが入り、...$modelsに残りもの(book comment)が渡されます。

まずはgetGateArguments()を見ていきましょう。

Illuminate/Auth/Middleware/Authorize.php
protected function getGateArguments($request, $models)
{
    if (is_null($models)) {
        return [];
    }

    return collect($models)->map(function ($model) use ($request) {
        return $model instanceof Model ? $model : $this->getModel($request, $model);
    })->all();
}

コレクションクラスのmap()で一つずつ処理をしていきます。

今回$modelsは文字列なのでgetModel()が呼ばれます。

Illuminate/Auth/Middleware/Authorize.php
protected function getModel($request, $model)
{
    if ($this->isClassName($model)) {
        return trim($model);
    } else {
        return $request->route($model, null) ?:
            ((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null);
    }
}

isClassNameで完全修飾名かどうかを判定しています。完全修飾名だとそのまま返すようです。

完全修飾名出ない場合を見ていきましょう。

Illuminate/Http/Request.php
public function route($param = null, $default = null)
{
    $route = call_user_func($this->getRouteResolver());

    if (is_null($route) || is_null($param)) {
        return $route;
    }

    return $route->parameter($param, $default);
}

$routeには現在のルートの情報を保持するRouteクラスが入ってきます。

Routeクラスのparametersプロパティにはルートモデルバインディングで解決されたモデルなどが入っています。

protected $parameters = [
    'book' => 解決されたBookモデル,
    'comment' => 2,

$route->parameter()は第一引数をkeyに$parametersから値を取得します。

今回のgetGateArguments()の返り値は配列になります。中身は[解決されたBookモデル, 2]です。

$this->gate->authorize('view', [解決されたBookモデル, 2])を見てきます。

Illuminate/Auth/Access/Gate.php
public function authorize($ability, $arguments = [])
{
    return $this->inspect($ability, $arguments)->authorize();
}

inspcetが呼ばれて、

Illuminate/Auth/Access/Gate.php
public function inspect($ability, $arguments = [])
{
    try {
        $result = $this->raw($ability, $arguments);

        if ($result instanceof Response) {
            return $result;
        }

        return $result ? Response::allow() : Response::deny();
    } catch (AuthorizationException $e) {
        return $e->toResponse();
    }
}

rawが呼ばれ、

Illuminate/Auth/Access/Gate.php
public function raw($ability, $arguments = [])
{
    $arguments = Arr::wrap($arguments);

    $user = $this->resolveUser();

    $result = $this->callBeforeCallbacks(
        $user, $ability, $arguments
    );

    if (is_null($result)) {
        $result = $this->callAuthCallback($user, $ability, $arguments);
    }

    return $this->callAfterCallbacks(
        $user, $ability, $arguments, $result
    );
}

Arr:wrapは配列でないときは配列でラップしてくれるだけです。

$userにはログイン済みのユーザーが入ってきます。

callBeforeCallbacks()callAfterCallbacks()は、それぞれGate::before()Gate::after()で定義したコールバックが呼ばれます。

今回は関係ないのでcallAuthCallback()を見ていきます。

Illuminate/Auth/Access/Gate.php
protected function callAuthCallback($user, $ability, array $arguments)
{
    $callback = $this->resolveAuthCallback($user, $ability, $arguments);

    return $callback($user, ...$arguments);
}

resolveAuthCallbackは...

Illuminate/Auth/Access/Gate.php
protected function resolveAuthCallback($user, $ability, array $arguments)
{
    if (isset($arguments[0]) &&
        ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
        $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
        return $callback;
    }

    //...
}

$argumentsは[解決されたBookモデル, 2]なので、issetはtrueになり、getPolicyFor()は、

Illuminate/Auth/Access/Gate.php
public function getPolicyFor($class)
{
    if (is_object($class)) {
        $class = get_class($class);
    }

   if (isset($this->policies[$class])) {
       return $this->resolvePolicy($this->policies[$class]);
   }

    //...

    foreach ($this->policies as $expected => $policy) {
        if (is_subclass_of($class, $expected)) {
            return $this->resolvePolicy($policy);
        }
    }
}

ポリシーは

protected $polices = [
    Book::class => BookPolicy::class
]

と登録したので、$this->policies[$class]の返り値はBookPolicy::classになります。

resolvePolicyでクラス名からインスタンスを生成します。resolveAuthCallback()に戻ります。

Illuminate/Auth/Access/Gate.php
protected function resolveAuthCallback($user, $ability, array $arguments)
{
    if (isset($arguments[0]) &&
        ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
        $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
        return $callback;
    }

    //...
}

resolvePolicyCallback()はコールバックを返すみたいなのでコールバックが呼ばれるときに見てみることにします。

呼び出し元に戻ります。

Illuminate/Auth/Access/Gate.php
protected function callAuthCallback($user, $ability, array $arguments)
{
    $callback = $this->resolveAuthCallback($user, $ability, $arguments);

    return $callback($user, ...$arguments);
}

コールバックを呼び出してその返り値を返しているようです。案外登場が早かったのでコールバックの中身を見てみましょう。

Illuminate/Auth/Access/Gate.php
protected function resolvePolicyCallback($user, $ability, array $arguments, $policy)
{

    return function () use ($user, $ability, $arguments, $policy) {

        $result = $this->callPolicyBefore(
            $policy, $user, $ability, $arguments
        );

        if (! is_null($result)) {
            return $result;
        }

        $method = $this->formatAbilityToMethod($ability);

        return $this->callPolicyMethod($policy, $method, $user, $arguments);
    };
}

$userはログインユーザー、$abilityview$argumentは[解決されたBookモデル, 2]、$policyはさっき解決したBookPolicyインスタンス。

ポリシーにもbefore()がありますが今回は関係ないのでスルー。

Illuminate/Auth/Access/Gate.php
protected function callPolicyMethod($policy, $method, $user, array $arguments)
{
    if (isset($arguments[0]) && is_string($arguments[0])) {
        array_shift($arguments);
    }

    if (! is_callable([$policy, $method])) {
        return;
    }

    if ($this->canBeCalledWithUser($user, $policy, $method)) {
        return $policy->{$method}($user, ...$arguments);
    }
}

$arguments[0]が文字列ならばここで抹消されます。

最終的に$policy->{$method}($user, ...$arguments)でポリシー内のメソッドが呼ばれます。ポリシーのメソッドはboolを返します。

なので、callAuthCallback()の返り値は、ポリシーの指定されたメソッドの返り値ということになります。

Illuminate/Auth/Access/Gate.php
public function raw($ability, $arguments = [])
{
    $arguments = Arr::wrap($arguments);

    $user = $this->resolveUser();

    if (is_null($result)) {
        $result = $this->callAuthCallback($user, $ability, $arguments);
    }

    return $this->callAfterCallbacks(
        $user, $ability, $arguments, $result
    );
}

afterを指定していないので、callAuthCallback()の返り値がそのまま返ります。

Illuminate/Auth/Access/Gate.php
public function inspect($ability, $arguments = [])
{
    try {
        $result = $this->raw($ability, $arguments);

        if ($result instanceof Response) {
            return $result;
        }

        return $result ? Response::allow() : Response::deny();
    } catch (AuthorizationException $e) {
        return $e->toResponse();
    }
}

$resultにポリシーのメソッドの返り値が格納されます。

Illuminate/Auth/Access/Response.php
public static function deny($message = null, $code = null)
{
    return new static(false, $message, $code);
}

public static function allow($message = null, $code = null)
{
    return new static(true, $message, $code);
}

Illuminate/Auth/Access/Response.phpを生成して返していますね。

Illuminate/Auth/Access/Gate.php
public function authorize($ability, $arguments = [])
{
    return $this->inspect($ability, $arguments)->authorize();
}

inspct()Illuminate/Auth/Access/Responseを返すので、Illuminate/Auth/Access/Response::authorize()が呼ばれます。

Illuminate/Auth/Access/Response.php
public function authorize()
{
    if ($this->denied()) {
        throw (new AuthorizationException($this->message(), $this->code()))
                    ->setResponse($this);
    }

    return $this;
}

ここでエラーレスポンスを返すか、次のミドルウェアへ処理を移すかの分岐になっていますね。

以上でポリシーの認可は終わりです!

まとめ

ポリシーはリソース(モデル)を利用するGateの糖衣のようなもの。
ルートモデルバインディングを前提としている。
before, afterなどのhookが用意されている。

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

Laravel バリデーションで変数を利用

Laravelのバリデーションで変数の使い方

なぜ気になったのか

ECサイトを制作中に、商品をカートに追加する際にデータベース上の商品の在庫数以上の数を買い物かごに入れることができてしまう。
バリデーションを利用するときに商品の在庫数はそれぞれ異なるので固定の値は設定できない。
商品ごとにデータベースの商品の在庫数をMAXとしてそれ以上買い物かごに入れる時にはバリデーションを発動してエラーメッセージを出したかったから。

まず試したこと

$data['test'] = 5;
$max = 10;

$validator = Validator::make($data, [
            'test' => 'max:$max',
        ]);

これでは全くバリデーションは効かず

ではどうするべきか。

PHPで変数を文字列内で展開したい場合は変数を{ }(波括弧)で囲って書くことで、文字列の中に変数があること明示している。
ただし、文字列の中に変数を入れる場合は、文字列を必ず" "(ダブルクォーテーション)で囲む必要がある。
詳しくはPHPマニュアル

これを踏まえて下記で実行してみました。

$data['test'] = 5;
$max = 10;

$validator = Validator::make($data, [
            'test' => "max:{$max}",
        ]);

しかしこれでもうまくいきませんでした。

何が必要なのか。

Laravelバリデーションのmin max sizeなどを利用するときは指定されたを判断する。
そのため、上記の書き方では文字列と判断されは文字長を判断する。
詳しくはLaravel5.5 バリデーション
在庫数などの数値を判断する場合にはmaxの前に整数値であるバリデーションが必要なためintegerを記述する必要がある。

これを踏まえると下記の書き方になる。

$data['test'] = 5;
$max = 10;

$validator = Validator::make($data, [
            'test' => "integer|max:{$max}",
        ]);

細かい部分ですが、つまずいたポイントなので復習がてらにまとめてみました。

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