tohokuaikiのチラシの裏

技術的ネタとか。

LaravelのRequestを使ったForm ValidateにRequestクラス内共通の処理を使いたかった

つまり、Requestのメソッドを使いたかったんだけど、それはできないみたいなのでクロージャを使った。

<?php
class UserRequest extends FormRequest
{

    public function rules()
    {
        // 共通で処理するValidator
        $my_validate = function($attribute, $value, $fail){
            return $fail('no good');
        };

        $rules = [
            'name' => [
                'required',
                $my_validate,

こんな感じ。

requiredを満たした時、この$my_validateが走る。

LaravelでPolicyを追加する時に、ModelがInjectionしてくれなくて困った件

この辺りを読んでて… readouble.com

route/api.php

<?php
    Route::post('user/confirm', 'UserController@confirm')->name('user.create.confirm')->middleware('can:confirm,App\User');
    Route::post('user/{user}/confirm', 'UserController@confirm')->name('user.update.confirm')->middleware('can:confirm,App\User');

って書いて、UserPolicyのcnofirmメソッドに

<?php
    public function confirm(User $user, User $model = null)
    {

って書いたけど、$modelにObjectあきよらん…って思ってたけど、

createのようなアクションではモデルインスタンスを必要としません。このようなケースでは、ミドルウェアへクラス名を渡してください。クラス名はアクションを認可するときに、どのポリシーを使用するかの判断に使われます。

とあって、->middleware()の第二引数(正確には第一引数のカンマ区切り2個目)にクラスを書くのはmodelが不要なときだった…

$modelが必要な場合は

canミドルウェアへ2つの引数を渡しています。最初の引数は認可したいアクションの名前です。2つ目はポリシーメソッドに渡したいルートパラメータです。

とあるように、Routeで{user}としたものが入るので↑のRouteの記述は

<?php
    Route::post('user/confirm', 'UserController@confirm')->name('user.create.confirm')->middleware('can:confirm,App\User');
    Route::post('user/{user}/confirm', 'UserController@confirm')->name('user.update.confirm')->middleware('can:confirm,user');

とやるのが正解。

LaravelでSQLログを出力するのをProviderで実装する

以前書いたコレ。

tohokuaiki.hateblo.jp

Providerで実装する。

Providerを作る

こちらを参考に。 qiita.com

app/Providers/DataBaseQueryServiceProvider.php

<?php
declare(strict_types=1);

namespace App\Providers;

use Carbon\Carbon;
use DateTime;
use DB;
use Event;
use Illuminate\Database\Events\TransactionBeginning;
use Illuminate\Database\Events\TransactionCommitted;
use Illuminate\Database\Events\TransactionRolledBack;
use Illuminate\Support\ServiceProvider;
use Log;

class DataBaseQueryServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register(): void
    {
        if (env('APP_SQL_DEBUG') !== true) {
            return;
        }

        DB::listen(function ($query) {
            $sql = $query->sql;
            foreach ($query->bindings as $binding) {
                if (is_string($binding)) {
                    $binding = "'{$binding}'";
                } elseif (is_bool($binding)) {
                    $binding = $binding ? '1' : '0';
                } elseif ($binding === null) {
                    $binding = 'NULL';
                } elseif ($binding instanceof Carbon) {
                    $binding = "'{$binding->toDateTimeString()}'";
                } elseif ($binding instanceof DateTime) {
                    $binding = "'{$binding->format('Y-m-d H:i:s')}'";
                }

                $sql = preg_replace("/\?/", $binding, $sql, 1);
            }

            Log::channel('sqllog')->debug('SQL', ['sql' => $sql, 'time' => "$query->time ms"]);
        });

        Event::listen(TransactionBeginning::class, function (TransactionBeginning $event) {
            Log::channel('sqllog')->debug('START TRANSACTION');
        });

        Event::listen(TransactionCommitted::class, function (TransactionCommitted $event) {
            Log::channel('sqllog')->debug('COMMIT');
        });

        Event::listen(TransactionRolledBack::class, function (TransactionRolledBack $event) {
            Log::channel('sqllog')->debug('ROLLBACK');
        });
    }
}

Providerを登録する

config/app.php

<?php
    'providers' => [
.../*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\DataBaseQueryServiceProvider::class,

loggerを登録する

config/logging.php

<?php
        'emergency' => [
            'path' => storage_path('logs/laravel.log'),
        ],

        'sqllog' => [
            'driver' => 'daily',
            'path' => storage_path('logs/sql.log'),
            'level' => 'debug',
        ],

LaravelのAuthをAPIで効かす時にいつも忘れるのでデジタルタトゥーに刻みたい

make authしてから

$ ./artisan make:auth

は、Laravelの古い奴だっけ…

じゃなくて

$./artisan ui vue --auth

してから、

APIでもSessionを効かす

app/Http/Kernel に

<?php
    protected $middlewareGroups = [
        'web' => [
              // 省略
        ],

        'api' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,

この4行を追加する。

Policyを効かす

$ ./artisan make:policy UserPolicy --model=User

とかでModelのPolicyを作って、Controllerで

<?php
class UserController extends Controller
{
    /**
     * CardInfoController constructor.
     */
    public function __construct()
    {
        $this->authorizeResource(User::class, 'user');
    }

とかするとリソースポリシーが効く