20210621のlaravelに関する記事は7件です。

Laravelで違う文字(濁点/半濁点/平仮名/片仮名)が同じ文字として使われてしまう問題の対処法

はじめに Laravelで日本語の比較処理をしようとしたところ、違う文字列であるにも関わらず同じものとして扱われてしまうことがありました。 その時の対処法を忘れないように記事にしたいと思います。 動作確認環境 PHP 8.0 Laravel 8.0 MySQL 8.0 ※下位環境でも動作する場合がございます テーブル構造 問題点 $nana = Sample::where('name', 'バッグ')->get(); この処理。一見だと違う文字列であるため、一致する値は取得できないように思えますが、実はID:1の情報が返ってきてしまいます。 解決法1 $nana = Sample::where('name', 'LIKE BINARY', 'バッグ')->get(); あいまい一致を回避するためにはLaravelでは'LIKE BINARY'を付けることで完全一致に切り替えることができます。 解決法2 ただ、解決法1ではSelect文の時は回避できるのですが、該当カラムにユニークキーが貼られていた場合、Insertは依然として通りません。 該当カラムにユニークキーを使っている場合は、該当カラムの参照順序(文字コード)を変えてしまいましょう。 MySQLのバージョンにもよりますが8.0.2以降であれば、utf8mb4_ja_0900_as_cs_ksを使っておけば無難です。 ※MySQL8系が使えない場合はutf8mb4_binが無難です(適時読み替えて下さい) /** * Run the migrations. * * @return void */ public function up() { // マイグレーションの書き方 Schema::create('samples', function (Blueprint $table) { $table->id(); $table->string('name')->unique()->collation('utf8mb4_ja_0900_as_cs_ks'); $table->timestamps(); }); } 解決法3 文字コードで混乱しなくないという場合は、Laravelのconfig/database.phpの設定を書き換えてしまうのが簡単です。 MySQL 8.0.2以降を使用する場合は下記の設定しておけば問題ないかと思います。 マイグレーションした際の初期値がutf8mb4_ja_0900_as_cs_ksになります(個別設定は不要)。 ※なお、MySQL8系が使えない場合はutf8mb4_binが無難です(適時読み替えて下さい) 'mysql' => [ ... 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_ja_0900_as_cs_ks', ... ], 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザーエージェントを判別するAgentをLaravelにインストールする

「Agent」のロゴ。ダンディですね。 UserAgentを識別するライブラリ「Agent」 PHPでユーザーのOSやブラウザの種類を判別したいときには「Agent」が便利です。 使い方 <?php use Jenssegers\Agent\Agent; $agent = new Agent(); // デバイスのチェック $agent->isAndroidOS(); // ブラウザのチェック $agent->isIE(); $agent->isSafari(); Laravelへのインストール composerでインストールします。 composer require jenssegers/agent Laravelから呼び出せるように設定をconfig/app.phpに足します。 config/app.php Jenssegers\Agent\AgentServiceProvider::class, config/app.php 'Agent' => Jenssegers\Agent\Facades\Agent::class, LaravelのBladeに便利に渡す ServiceProviderを追加し、Bladeテンプレートに常に$agentが渡されるようにします。 これでいつでもBlade内から$agentを参照することによってUserAgentを取得できます。 app/Providers/AgentServiceProvider.php <?php namespace App\Providers; use View; use Jenssegers\Agent\Agent; use Illuminate\Support\ServiceProvider; class AgentServiceProvider extends ServiceProvider { public function boot() { $agent = new Agent(); View::share('agent', $agent); } public function register() { // } } config/app.php <?php ... 略 ... /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, ... 略 ... App\Providers\AgentServiceProvider::class, ], これで以下のようにBladeテンプレートから呼び出せます。 index.blade.php @if($agent->isIE()) {{ 'Internet Explorerを使っています。' }} @endif
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel API認証 vs Passport vs Sanctum 違い

とあるSPA開発をしていて、APIとしてlaravelを使うことにしておりユーザー認証はOAuth2つまりLaravel Passportを使っていて実は2年前にLaravel6.xで実装済。 認証の部分は済ませていて後は各画面だったり機能の開発に専念できる状態にまでしていたのだが、知り合いから頼まれてたような開発なのでプライベート時間を使ってちょこちょこやってはいたんだけど、ちょっと時間が空き過ぎて見返しても思い出せん部分がちらほら。 そもそもSPAの認証の仕組みってどうなってるんだっけって状態にまで...。 オイラも2年前から多少は成長しているわけでAPI側見てみてもデザパタがなってなかったりいづれにしてもそこそこのリファクタが必要だったりする。フロント側もatmic手法でコンポーネント切り分けできてないな...って今ではそういう目線で見たりしてる。 とは言え一人で作ったやつだからリファクタするにしてもそこまで大掛かりではない。 なのでもう最初から作り直すことにした。 ちなみにフロントはnuxt.js/vuetifyでAPIはLaravel6.xである。 あと2ヶ月もしたらlaravelLTS9.xがでるから、今最初から作り直すのはもどかしい気持ち。 routes/api.phpのauth:apiってなに?使い道は? もうこんなレベルに落ちるまでリア充しておりました、はい。 <?php use Illuminate\Http\Request; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); 答え userテーブルにapi_tokenカラムを持たせる $table->string('api_token', 80)->after('password') ->unique() ->nullable() ->default(null)->comment('API トークン'); 以下でtokenを作り、適当なユーザーデータを作る dd(\Str::random(60)) route定義はgetなのでとりあえずブラウザで以下を実行してみる まぁこんなエラーがでるが Symfony\Component\Routing\Exception\RouteNotFoundException Route [login] not defined. app/Http/Middleware/Authenticate.phpの処理の結果である class Authenticate extends Middleware { /** * Get the path the user should be redirected to when they are not authenticated. * * @param \Illuminate\Http\Request $request * @return string|null */ protected function redirectTo($request) { if (! $request->expectsJson()) { return route('login'); } } } で、今度はクエパラを付与してアクセスしてみる 合致するapi_tokenのユーザーでログインしたことになる {"id":1,"name":"test","email":"test@t1e2s3t4.co.jp","email_verified_at":null,"api_token":"jD5SB9NpzyQjhHGgxWXcYgUkxkB74rrfXL6Z67b4aQH9cdMVcFPx3QDvKh46","created_at":null,"updated_at":null} 詳細知りたいなら以下を。 じゃあlaravel passportとの違いは?ちなみにSanctumってどうなのよ? SanctumはLaravel8.xから導入された比較的軽量な認証のエコシステムらしい。 システムがOAuth2に準拠するのならpassport使えとある。 Passportは、Laravelアプリケーションに完全なOAuth2サーバー実装を数分で提供します。したがって、OAuth2についての簡単な知識が必要です。 Sanctumは、OAuthを複雑にすることなく、ユーザーにAPIトークンを発行するためのシンプルなパッケージです。Sanctumは、Laravelの組み込みのCookieベースのセッション認証サービスを使用します。 小さなアプリケーションでは、Sanctumを使用します。シンプルで簡単です。 小さなアプリケーションと言われてもなぁ...もっと具体例がないと判断材料にできん。 だから正直どういうケースのときにpassportとsanctumを使うべきなのかの説明はできん。 ただ説明を見る限りは、SanctumにできることはPassportもできるけど、PassportにできてSanctumにはできないこともあるみたいだから個人的にはpassport使っとけばいいと思う。 エンジニアらしい回答でなくてすまんな、エンジニア上がりの経営者なので後々リプレイス案件にでもすればいいと思っている。まずは稼ぐことが大事なのだ。 そしたら次は、API認証 vs Passport だ。 API認証 vs Passport auth:apiの方は認証できなければログインページのリダイレクト処理が入ってる。 Passportは認証できなければjsonでresしてる。 まぁどっちを使っても、例えばauth:api使ってもredirectToでjsonをreturnすればPassportと同じようなことはできると思ってるが、流石にそこは気持ち悪さがある。redirectToじゃないし的な。 どちらかと言えばフロントがjsonを受け取ってその結果を、vue-rooterの対象にリダイレクトさせるようにすべきだろう。 で、話を戻してとある記事ではこんな風に書かれている。 Laravelの外側のアプリケーションからアクセス制限のかかっているLaravel内のリソースにアクセスするためにはどうしたらいいのでしょう。そんな疑問を解決してくれるのがLaravel Passportです。 そして公式にはこんな風にも書かれている。 Note: Laravelでは、シンプルなトークンベースの認証ガードを提供していますが、API認証を提供する堅牢なプロダクションアプリケーションでは、Laravel Passportの使用を考慮することを強く推奨します。 API認証を提供する堅牢なプロダクションアプリケーションとは。具体例はよ。 別にauth:apiでも外部からapi-token付与してアクセスしたらユーザー情報は引っ張って来られる。 だからpassportとの違いがわからない。 とりあえず堅牢にすべきかそうでないかと問われたら堅牢にしたくないなんてプロダクトオーナーはいないだろう。 よってpassportを使っとけばいいんでねーかというのが結論。 明確に使い分けを説明できる勇者様が現れましたら記事は更新します。 追記 ↓堅牢です、はい。 {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMjVjZjllNDk5Njc2ZmQ1ODdiYTg2MGMwMGNlNTE3NmIyYTY2NmE3ZDhmZmI3NTdhMWM2MDE0MmUzZjZmMDQxOTRmMTdiMWM0MDczNTliOTciLCJpYXQiOjE2MjQyOTAwNjksIm5iZiI6MTYyNDI5MDA2OSwiZXhwIjoxNjU1ODI2MDY5LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.H8tIfRCDkQJR6QQktcxU0Gap34LtKdHSstToAMnsNEtAgteEe-3I9NteBmysmJgxshWkagerDdIwQBtgZvQRa06O8a-_daB-oDIbRgob270eee7j2YW6yQqklSuZSSP-0zqfkOBlRSCuzoVHBRYE1kGs_ytIhxVeJF0tdWHkCsp6tqOE8-nzCycgwhA0yob6xMHHLio_AhKlXvsplHKPQO6o1v-MinF4C_fiqxQd-6ksFCVoifQkoiUzVF3GGtEem5mWYTNsZ0yAovEXk7ca2qJNaTpNFY1tBupfqLCesWhE_x2PYJnAhU37_8yZIsQn45tgUGK7Oi_iSZhUlXn2_eVK3wm_R04EJQI-3moRPiFaKbk3vaaz7sQVM74t6Zy_SX4Z-B-xgBrVgO9CLQ6qUnC8nWE5l0pRFqQGGa5XBkHt8nELipFRjyEZ4sV6NXdjDUE-uJmYYef_WqHWbKtx0YOYyMSfrgoiHDG9leEQwcYWAXZjtUMR_Df-5n7dQVvqX3oK-z1uBWlTJRMwvYyE_ou4TzJqFvaOIgOdb1fnFhEUKS8tOPlJAC4fj4o2fLpxrCGj1DLzFogHzCynqO_qCXVB76pIG9snxotUoVsbhYN8YiNCHyH4N0g28Cw4GXmBOpxk7Aax0c0qWmmz_9d6zAKvjzZKztUotPdcJtKuo9A"}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker環境でcron実行してみる

概要 ローカル環境で、Laravelスケジュールを設定して、自動実行してみたくて調べたことをメモする。 ファイル構成 以下のようなファイル構成で構築してみる。 ※ ソースコード:Github-reflet/laravel5.6 ├ docker // ← docker関連のフォルダ │ ├ cron │ │ ├ cron.root │ │ ├ Dockerfile // ← cron実行する(PHP-CLI) │ │ └ php.ini │ │ │ └ php │ ├ Dockerfile // ← Apacheから実行される(PHP-FPM) │ └ php.ini │ ├ src // ← Laravel関連のフォルダ │ ├ app │ │ ├ Console │ │ │ ├ Commands │ │ │ │ └ EmailSendCommand.php // ← メールをテスト送信するコマンド │ │ │ └ Kernel.php // ← スケジュール設定 │ │ ... │ │ ├ resources │ │ └ views │ │ └ emails │ │ └ test.blade.php // ← テストメール文面 │ │ ... │ │ └ yarn.lock │ └ docker-compose.yml cronコンテナについて phpコンテナ内でcronを起動するのが簡単かと思ったが、意外とうまくいかない。 ※ 下記に記載されている症状が発生... まあCMD変えたら、そらそうなるよね... (^^;)   → PHPコンテナのDockerファイルでCMDを書くとnginxコンテナと接続できなくなった そこで、PHPコンテナをベースにCRONコンテナを別途作成しました。 ./docker/cron/Dockerfile # PHP-FPMではなく、PHP-CLIで構築する (内容はほぼ同じ...) FROM php:7.3-cli # ... # cronインストール RUN apt-get install -y cron # ... # cron設定 ARG CRON_FILE='/etc/cron.d/cron' COPY ./docker/cron/cron.root $CRON_FILE # cron実行ログを標準出力に出力するため、シンボリックリンクを用意する RUN chmod 0644 $CRON_FILE && \ ln -sf /proc/1/fd/1 /var/log/cron.log # ... # cronをフォアグラウンドで起動する CMD cron -f cron設定について crontabの設定は以下のようにしました。 ./docker/cron/cron.root * * * * * root /usr/local/bin/php /var/www/www.example.com/artisan schedule:run >> /var/log/cron.log 2>&1 ※ シンボリックリンク ( /var/log/cron.log )で、標準出力に接続する docker-composeの設定について 以下のようにcronとphpコンテナの2つを起動するようにしてみました。 ./docker-compose.yml version: '3' services: php: image: my-laravel5.6/php:7.3 build: context: ./ dockerfile: ./docker/php/Dockerfile tty: true ports: - '9000:9000' volumes: - ./src:/var/www/www.example.com:cached depends_on: - mysql cron: image: my-laravel5.6/cron:7.3 build: context: ./ dockerfile: ./docker/cron/Dockerfile tty: true volumes: - ./src:/var/www/www.example.com:cached depends_on: - php ... サーバ起動 dockerを起動してみる。 $ docker-compose build スケジュール設定 テストメール送信クラスを追加する。 $ docker-compose exec php php artisan make:mail TestEMail テストメール送信のコマンドを追加する。 $ docker-compose exec php php artisan make:command EmailSendCommand スケジュール設定を追加する。 ./src/app/Console/Kernel.php protected function schedule(Schedule $schedule) { // ↓ 1時間ごとに定期実行する $schedule ->command('email:test') // 実行するコマンド ->hourly() // 実行間隔: 毎時 ; } cron確認 テストメール送信する $ php artisan emai:test コマンドを1分ごとに実行している。 下記コマンドにて、定期状況をログで確認してみる。 $ docker-compose logs cron cron_1 | No scheduled commands are ready to run. cron_1 | No scheduled commands are ready to run. cron_1 | No scheduled commands are ready to run. cron_1 | No scheduled commands are ready to run. cron_1 | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1 cron_1 | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1 cron_1 | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1 cron_1 | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1 問題ないようですね。 参考資料 Laravel 5.6 タスクスケジュール Docker + Cron環境を実現する3つの方法 Laravelでschedule:runしたときのoutputがdockerで標準出力されない問題を解消 LaravelにDockerファイルでcronを導入して定期的にメール送信する方法 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのAuth機能で表示されるナビバーにリンクを追加したい。

LogoutしかメニューがないAuth機能のナビバー。 Laravelでポートフォリオを作成中です!! Auth機能でログイン機能を実装したのち 右上部にログインした後に名前が表示されるのですが そこのナビバーって Logoutしかメニューがないんですね。 今回のポートフォリオで 追加で「新規投稿画面」 「ユーザー情報画面」とか他画面に行くためのリンクの追加がしたい。。。。 日本語で探したかったけどうまく言葉が見つからんやった なので英語で説明あったので。 こちら まだ初心者なので英語のソース見るのドキドキしちゃうんですが 簡単でした。 This should be super simple to do, I'm not sure what you're doing but to add additional links you can add an anchor tag to the dropdown div: スーパーシンプルやで。アナタ様がどのような感じでやったか知らんけど、 a タグを div ん中に追加するんやで。 <a class="dropdown-item" href="#">About</a> たとえばこんな感じやな <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="#">About</a> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </div> て感じでした。 aタグの中に「dropdown-item」のクラス名を追加して あとはお好きなようにリンク貼っちゃっていいみたいです。 簡単じゃった。またレベルが上がりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのレプリケーション遅延対策

LaravelのDBとしてAWSのAuroraを使っていてマスタースレーブ構成にしていたのですが、レプリケーション遅延により書き込んだはずのデータが見当たらないという事態が発生していました。 データを追加したユーザーが一覧ページに戻ってみると、追加したはずのデータが見当たらないというのはユーザー体験として良くないので、自分が取った対応方法について書いていこうと思います。 状況 コントローラの処理としては、以下のようなstoreアクションで書き込んだ後リダイレクト先のindexアクションで読み込みをしていて、この読み込みの際に書き込んだデータが反映されていないという現象が5回に1回程度発生します。 MenuController.php <?php namespace App\Http\Controllers\Web\Professional; use Illuminate\Routing\Controller; use App\Models\Menu; class MenuController extends Controller { public function index(FetchOrderedMenusService $service) { $menus = Menu::all(); return view('menu.index', compact('menus'); } public function store(Request $request) { Menu::create($request->all()); return redirect()->route('menus.index'); } } Auroraのレプリカラグが20ms、書き込み処理からリダイレクトして読み込み直前までの時間を計測すると約100msなので、読み込みのタイミングでスレーブに反映されていないということは無い気がするのですが、たまたまレプリケーションに時間がかかったというのとリクエストの処理時間が早かったという状況が重なって発生しているのではと考えました。 実際に対策をすると今回の現象は発生しなくなりました。 対策 方針 config/database.phpのstickyを有効化している場合だと、同一リクエスト上ではマスターに書き込みを行った後はマスターを参照するようになります。ただ、今回はリダイレクトしたときにマスターを参照して欲しいのでstickyを有効化するだけでは対応できません。 stickyを有効化している場合の処理を追ってみると、DB::recordsHaveBeenModified()を呼び出すことでそれ以降の処理ではマスターを参照するようだったので、この関数を使って書き込み直後のリクエストでマスターを参照させます。 実装 以下のようなミドルウェアを作成し、既存の実装に影響が出ないようにします。 ミドルウェアの実装内容としては、DBに書き込みを行った直後のリクエストかどうかを判別できるように書き込みのタイミングでセッションに値を保存しておき、次のリクエストでセッションに値が存在すればDB::recordsHaveBeenModified()を呼び出します。 <?php namespace App\Http\Middleware; use Closure; use DB; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; class UseWriteConnectionWhenHaveBeenModified { public function handle(Request $request, Closure $next, ...$modelClassNames): mixed { $callback = function () { session()->flash('haveBeenModified'); }; foreach ($modelClassNames as $modelClassName) { if (!is_subclass_of($modelClassName, Model::class)) { continue; } $modelClassName::created($callback); $modelClassName::updated($callback); $modelClassName::deleted($callback); } if (session()->has('haveBeenModified')) { DB::recordsHaveBeenModified(); } return $next($request); } } 書き込み直後のリクエストを処理し終わったらセッションから値が削除されて欲しいのでflash関数を使っています。 作成したミドルウェアをコントローラから利用できるようにKernel.phpに追記します。 app/Http/Kernel.php protected $routeMiddleware = [ ... 'haveBeenModified' => \App\Http\Middleware\UseWriteConnectionWhenHaveBeenModified::class, ]; 最後にコントローラのコンストラクタにミドルウェアを追加します。 MenuController.php public function __construct() { $this->middleware('haveBeenModified:' . Menu::class); } 指定したeloquentモデルのデータに書き込み処理が行われると、直後のリクエストはマスターを参照するようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dbコンテナだけが立ち上がらないdocker-compose build

laravel + dockerでやっていたプロジェクト。 最近dockerをリフレッシュしてコンテナが消えたので環境構築をし直す機会がありました。 いつもどおりgitリポジトリから git clone リポジトリURL docker-compose build docker-compose up とコマンドを入れていく … データベースコンテナだけが起動に失敗している。 エラーログを見ると MYSQL_USER="root", MYSQL_USER and MYSQL_PASSWORD are for configuring a regular user and cannot be used for the root user zac_db | Remove MYSQL_USER="root" and use one of the following to control the root user password: というエラーが出ていた。 以前と同じやり方なのになんで? 英語力低いのでちょっと何言ってるのかわからないですね状態なので、取り敢えずgoogle翻訳にかけてみる mysql_user = "root"、mysql_user、およびmysql_passwordは、通常のユーザーを構成するためのものであり、rootユーザーには使用できません。 mysql_user = "root"を削除し、次のいずれかを使用してrootユーザーのパスワードを制御します。 ??? はい? 取り敢えずrootという名前がだめらしい? 結論 rootを消してみた。 docker-compose.ymlのrootを docker-compose.yml # db db: image: mysql:5.7.33 container_name: database environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: laravel MYSQL_USER: root MYSQL_PASSWORD: root TZ: 'Asia/Tokyo' 以下に変更。rootをroot2に変更 docker-compose.yml # db db: image: mysql:5.7.33 container_name: database environment: MYSQL_ROOT_PASSWORD: root2 MYSQL_DATABASE: laravel MYSQL_USER: root2 MYSQL_PASSWORD: root2 TZ: 'Asia/Tokyo' … 通った。 database起動! よくわからないが最近の仕様変更でrootを使用できなくなったらしい。root以外にすれば大丈夫そう。 なんでやねん (セキュリティー的な問題?) 調べたら同じような症状で躓いている人が多そう。MySQLのDockerイメージが更新されたことが原因らしい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む