コードロード

エラー討伐

githubの秘密鍵を指定してgit pullする

こんな感じで .ssh/config に2つ登録したとする

// .ssh/config

Host github.com.first
        HostName github.com
        IdentityFile ~/.ssh/id_rsa_first
        User git

Host github.com.second
        HostName github.com
        IdentityFile ~/.ssh/id_rsa_second
        User git

秘密鍵を指定して git pull したいときはこうする

$ git pull github.com.second:組織名/リポジトリ名

公式ドキュメントにも、ここにオプションを指定しなさいと書いてある

git pull [<options>] [<repository> [<refspec>…]]

参考

qiita.com

git-scm.com

【Laravel×Postman】画像を取得できない(送信できない)時は

LaravelでREST API作成中にハマった。Postmanで動作確認中。

結論

リクエストヘッダーの Content-Type のチェックが外れていないか確認!

値は multipart/form-data; boundary=<calculated when request is sent> となっているはず。

ハマった

こんな感じで、POSTリクエストで、Bodyを form-data にして、ファイルを設定しているのに、Laravel側で取得できなかった。

<?php

dd($request->file('image'));

// 結果:null

Laravel Sanctumを使っていたので、Content-Typeapplication/jsonに指定していた。

色々ググってみて、Content-Typemultipart/form-dataにしてみても効果なし。

リクエストヘッダーを全部確認してみたら、デフォルトのContent-Typeのチェックが外れていて無効になっていた。

値はmultipart/form-dataではなく、デフォルトのmultipart/form-data; boundary=<calculated when request is sent>でないと送信できないよう。

こんな感じだと効果なし。

参考

stackoverflow.com

【Laravel】定数をクラスファイルで管理する

定数をクラスファイルで管理する

app/Consts/CommonConst.php を作成した。

<?php

namespace App\Consts;

class CommonConst {

  const MESSAGE = 'message';

  const RESULT = 'success';

}

config/app.phpエイリアスを追加

<?php

return = [
    // 省略

  'aliases' => [
    // 省略
    'CommonConst' => \App\Consts\CommonConst::class,
  ]
]

app/Http/Controllers/Api/UserController.php で使用する

<?php
    public function index()
    {
        $users = User::where('id', Auth::id())->get();
        return response()->json([CommonConst::MESSAGE => CommonConst::RESULT, 'user_info' => $users]);
    }

参考

qiita.com

【Laravel Sanctum】ログインは通ってセッション生成されたのにSanctumミドルウェアのルートにアクセスするとunauthenticatedになる

メモ程度

課題

表題の通り

対策

キー名RefererOrigin、値はURLでリクエストヘッダーに設定してあげる。

参考

refererヘッダーまたはoriginヘッダーのチェック
SPAからこのヘッダーが送られないと認証エラーになる
Postmanで動作確認するときに注意する

qiita.com

【Laravel】419 CSRF token mismachエラーの対処記録

Laravel Sanctumを使ってログイン機能を実装中にエラーに遭遇。 思考の整理と次に時間を溶かさないようにするためのメモ

結論

.env での SESSION_DOMAIN='localhost' ここが間違えていた。

.env にてセッションを設定するドメインを設定しているので、ドメインを設定する。

開発環境なので localhost で良い。

本番環境のドメインを指定してしまっていたので、セッションクッキーがセットされていなかったよう。考えてみれば当たり前。デプロイ時は本番のドメインを指定する。

ちなみに localhost:8000 だとエラーになるのでポート番号はいらない!

環境

  • PHP 7.4.16
  • Laravel 8.83.17
  • MAMP
  • フロントはReactで、LaravelはAPIサーバとして開発

Sanctumによるログインの流れ

  1. Laravel Sanctumをインストール
  2. 自動で sanctum/csrf-cookie というルーティングが追加されるから、ここにGETリクエストすることで、CSRF保護を初期化する。( /login へPOSTする前に必ず実行する必要あり。これを行わないと、CSRFが照合できずエラーになる )
  3. レスポンスヘッダーに XSRF-TOKEN というクッキーが返ってくる。
  4. これを X-XSRF-TOKEN というキーでリクエストヘッダーにセットして、 /login へリクエストする。
  5. $request->session()->regenerate(); でフロントにセッションクッキーをセット。

流れは間違っていなかったが、ドメインが間違えていたのでセッションクッキーをセットできていなかった。

調べたあれこれ

guard

// config/auth.php

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            // 'driver' => 'session',
            'provider' => 'users',
            'hash' => false,
        ],
    ],
  • Laravel Sanctumによる認証は、webのguardを使用する。
  • regiter、login、logoutあたりはセッションクッキーを使用するので、sessionドライバーを設定しているweb guardを使用する。
  • api guardはデフォルトではtokenドライバーが設定されている。これをsessionに書き換えてもいいようだけど、それだと本来のAPIの仕様が薄れちゃうから微妙という記事もあった。
  • 書き換えてみたら CSRF token mismachエラーはでなくなったものの、 Auth::attempt() が使用できなくなり断念。 undefindエラーが出た。やっぱりセッションクッキーだからapiではなくてwebの方を使えということなのかな?

CSRF

  • \App\Http\Middleware\VerifyCsrfToken::class の箇所でtokenの検証をしている。
  • フロントから送られてくる X-XSRF-TOKEN とLaravel側のトークンを比べている。
// app/Http/Kernel.php  

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class, ←ここ
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

VerifyCsrfToken クラスの詳しい動きは下記の記事が細かく載っていた。

LaravelのCSRF対策の処理を実際のコードから見てみる - Qiita

その他

  • config/cors.phpに下記を追記。
    • paths : ここの部分はクロスドメイン周りのエラーを回避するよってこと?
    • supports_credentials :デフォルトでは false だったので true にする。GET以外のHTTPメソッドのときにトークン認証を毎回するよってこと。
// config/cors.php

    'paths' => [
        'api/*',
        'login',    // 追記
        'logout',   // 追記
        'register', // 追記
        'sanctum/csrf-cookie'
    ],

    'supports_credentials' => true,

参考

Laravel7 + Sanctum でSPAログイン認証機能を実装 - Qiita

Laravel API SanctumでSPA認証する - Qiita

【attemptメソッドが使えない?】Call to undefined method TokenGuard::attempt() の解決法

Laravelの標準Authentication(Auth)の動きを調べてみる - Qiita

Laravel5.4でAuth::attemptを使わずに認証処理をカスタマイズ

LaravelでSPA開発が可能なLaravel Sanctumの概要と導入手順を紹介する

Next.js(axios)とlaravelを使ってhttp通信がしたい(POST、PUT、DELETE)

【Laravel】多対多でリレーション先のデータを取得する

多対多のER図

リレーション先を取得する

  • モデルで追加したメソッド名を with('メソッド名') の引数に追加する
    • ※リレーション先のテーブル名やクラス名をwith()の引数にするわけではない!
// App/Models/Food.php

    /**
     * foodを持っているフードアイコン
     */
    public function foodIcons()
    {
      return $this->belongsToMany(FoodIcon::class, 'food_food_icon', 'food_id', 'food_icon_id')
                  ->withTimestamps();
    }
// app/Http/Controllers/FoodController.php

    public function index()
    {
        $foods = Food::->with('foodIcons')->get();
        return response()->json('foods' => $foods);
    }

【PHP】多次元配列を並び替えて、指定番目から指定件数のみ切り取った配列を取得

やりたいこと

PHPで、多次元配列があって、指定したキーの値で、昇順・降順の並び替えができるようにしたかった。 並び替えの後、指定した番目から指定した件数分の要素だけを取得したかった。 つまり、ページネーションのイメージ。

SQLでやればこんな感じですぐにできるが、色々あってPHPで処理することとなった。

SELECT * FROM users ORDER BY age ASC LIMIT 2, 2
$array = [
  [
    'name'=> 'kenta',
    'age' => 20
  ],
  [
    'name'=> 'taro',
    'age' => 10
  ],
  [
    'name'=> 'yuki',
    'age' => 15
  ],
  [
    'name'=> 'hanako',
    'age' => 25
  ],
  [
    'name'=> 'yuji',
    'age' => 30
  ],
];

結論

こんな感じの関数を作った。

/**
 * 配列の指定番目から指定数を切り取って返す
 *
 * @param  int   $start
 * @param  int   $max
 * @param  array $array
 * @return array
 */
function cutStartToMaxArray($start, $max, $array) {

  $tmp_array = [];
  for ($i = $start; $i <= count($array) - 1; $i++) {
    if (count($tmp_array) > $max - 1) {
      break;
    }
    $tmp_array[] = $array[$i];
  }
  return $tmp_array;
}

/**
 * キーを指定してその値をもとに、多次元配列をソートして返す
 *
 * @param  string $target_key
 * @param  string $order SORT_ASC or SORT_DESC
 * @param  string $type  SORT_REGULAR or SORT_NUMERIC or SORT_STRING etc...
 * @param  array  $array
 * @return array
 */
function orderMultiArray($target_key, $order, $type, $array) {

  foreach ($array as $k => $v) {
    $order_target_array[] = $v[$target_key];
  }
  array_multisort($order_target_array, $order, $type, $array);

  return $array;
}

$start_limit = 2; // 配列の何番目からスタート
$max_count   = 2; // データ数

$ordered_array = orderMultiArray('age', SORT_DESC, SORT_NUMERIC, $array)

$new_array = cutStartToMaxArray($start_limit, $max_count, $ordered_array);

var_dump($new_array);
// 結果
// ageで並び替えた2ページ目の2人分($new_array[2]と$new_array[3])のデータを取得

array (size=2)
  0 => 
    array (size=2)
      'name' => string 'kenta' (length=5)
      'age' => int 20
  1 => 
    array (size=2)
      'name' => string 'hanako' (length=6)
      'age' => int 25

参考

[PHP]自在に多次元配列をソートできるarray_multisort

【PHP】多次元配列を並び替える方法 - Qiita