20220303のlaravelに関する記事は11件です。

Laravel SailでLaravel 9 (PHP8.1 + node.js 16.x) 環境をサクッと立ち上げる

はじめに Laravelの作業を始めるときは、まずLaravel用のdocker-compose.yml他一式を準備していましたが、今ではデフォルトでLaravelのDocker構築環境Laravel Sailがインストールされているので、それを利用する機会が多くなってきました。備忘録も兼ねて、まとめておきます。 環境 ホスト MacBook Pro (16インチ, 2019) macOS Monterey 12.2.1 Docker Desktop 4.5.0 PHP 8.1.3 Composer 2.2.7 ゲスト Laravel Sail PHP 8.1.3 node.js 16.4.0 MySQL 8.0.x 準備 Laravelのインストール 自分はComposerでインストールするのが好きなので、以下のようにインストールします。laravel-cliが好きな方はそちらで。 composer create-project laravel/laravel laravel-sail-sample Laravel Sailの準備 すでにLaravel Sailはインストール済みなので、作成したLaravelのプロジェクトルートで以下のコマンドを実行します。一緒にどのサービスをインストールするか聞かれるので、ここでは0 (mysql)を選択します。例えばmysqlとmailhogをインストールする場合は0, 7と入力すればOKです。 php artisan sail:install Which services would you like to install? [mysql]: [0] mysql [1] pgsql [2] mariadb [3] redis [4] memcached [5] meilisearch [6] minio [7] mailhog [8] selenium > 0 Sail scaffolding installed successfully. プロジェクトルートにdocker-compose.ymlが以下のような作成されているはずです。 docker-compose.yml # For more information: https://laravel.com/docs/sail version: '3' services: laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.1 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' image: sail-8.1/app extra_hosts: - 'host.docker.internal:host-gateway' ports: - '${APP_PORT:-80}:80' environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' volumes: - '.:/var/www/html' networks: - sail depends_on: - mysql mysql: image: 'mysql/mysql-server:8.0' ports: - '${FORWARD_DB_PORT:-3306}:3306' environment: MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' MYSQL_ROOT_HOST: "%" MYSQL_DATABASE: '${DB_DATABASE}' MYSQL_USER: '${DB_USERNAME}' MYSQL_PASSWORD: '${DB_PASSWORD}' MYSQL_ALLOW_EMPTY_PASSWORD: 1 volumes: - 'sail-mysql:/var/lib/mysql' networks: - sail healthcheck: test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] retries: 3 timeout: 5s networks: sail: driver: bridge volumes: sail-mysql: driver: local これで準備は完了です。 Laravel Sailの利用のための一手間 Laravel Sailの実行ファイルはプロジェクトルート以下の./vendor/bin/sailになります。例えば、Laravel Sailで実行環境を立ち上げる際は、以下のように実行します。 ./vendor/bin/sail up これを毎回打つのは面倒なので、zshやbashのエイリアスを追加します。私はmacOS標準のzshを使っているので、~/.zshrcに以下の行を追加しました。 ~/.zshrc alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail' ~/.zshrcに行を追加したら、以下を実行してzshを再起動しておきます。 exec $SHELL -l こうすることで、先ほどの実行環境立ち上げのコマンドは、以下のように短縮されます。 sail up Laravel Sailの利用 1. 起動その1 以下のコマンドで起動します。この場合、終了はCtrl+Cです。 sail up 標準では http://localhost/ でLaravelの標準のウェルカムページが立ち上がっているはずです。最初のup時はDockerイメージのPULLや準備のため3〜5分ほど完全に起動するまでに時間がかかります。 2. 起動その2 以下のコマンドでバックグラウンド起動します。この場合、終了は3. 終了の手順です。 sail up -d 3. 終了 sail stop 4. composerコマンド composerはsail composerで起動します。以下はバージョンを表示した例です。 sail composer --version Composer version 2.2.7 2022-02-25 11:12:27 5. artisanコマンド Laravelのartisanコマンドはphp artisan ...と入力していましたが以下のように実行します。以下はテストを実行した例です。 sail artisan test PASS Tests\Unit\ExampleTest ✓ that true is true PASS Tests\Feature\ExampleTest ✓ the application returns a successful response Tests: 2 passed Time: 0.49s 6. npmコマンド ホストにnode.jsがインストールされていなくても、Laravel SailのDockerイメージ内にインストール済みですぐ利用出来ます。以下はnpm installを実行した例です。 sail npm install このようにsail ${FOO} ...とすれば、今まで使っていたコマンドが利用出来る事が分かります。 まとめ 時前のdocker-compose.ymlをDockerfileを準備して、色々と時間がかかっていた事を考えると、サクッとできてしまうのはありがたいです。この環境を使って、Laravel Inertiaについて次は書いてみたいなと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ニート日記やで^^

序 無職ニートです!!(唐突) 今日から週2投稿で「ニート日記」をやっていこうと思います。 もうすぐ大学卒業なのに就活をせず独学でプログラミングを学んでるのでガチで無職です… 新卒カァドすてちゃた…アセアセ ※文才がガチでありません。目も当てられません。自分の書いた文章を見直すたびに「またつまらぬ文章を書いてしまった…」といつも後悔してます。でもなるべく退屈しないような文章を心掛けていきます。 破 想定読者 ワタクシと同じような「ゆるゆるエンジニア」の方や「よわよわエンジニア」の方。 そして「ニートの生き様を見たい方」です。 ワタクシが書く記事の知識レベルは低いはずなので、つよつよの方は読んでて退屈すると思いまふ。 使用言語 日本語でぇーす(ニチャア)(激寒)  Webアプリ開発がすこすこザムライなのでぺーえっちぺー(PHP)を使っていきます。 フレーミングワーキングはLaravel8を使っております。 データベースはMySQLでござんす。 2022年度の目標 来年度に年収600万円になるための土台を作ってきマンモスパオーン 急 とりま、読みたい人だけ読んでくださいませ。 読者増えたら読んでくれる方々に向けた文章をちゃんと書こうと思ってます。 いまは好きに独りごつしゃべってくぜ。ついてきてエビバディ。 ~fin~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】autoloadとnamespace を自分なりに解釈できたので

はじめに これは自分が忘れないようにするためのアウトプットとしての投稿です。 laravelを勉強中に 「そういえばファイルの読み込みでrequire使ってないな」 「どうやって読み込んでるんだ?」 となり autoloadとnamespaceの学びに繋がったので 今回はそれを記録しようと思う。 概要 どうやってファイルを読み込んでいるのか? autoloadという仕組みで読み込んでいた。 autoloadという仕組みによって自動で読み込まれると関数名やクラス名が競合してしまうよ namaspace(名前空間)というクラスを階層的に整理するための仕組みを使う autoload(オートロード)とは ファイルを自動で読み込む仕組み これでファイルを毎度requireして読み込まなくてもよくなりました。 laravelではどこでautoloadの仕組みが動いているのか? laravel > public > index.php /* |-------------------------------------------------------------------------- | Register The Auto Loader |-------------------------------------------------------------------------- | | Composer provides a convenient, automatically generated class loader for | this application. We just need to utilize it! We'll simply require it | into the script here so we don't need to manually load our classes. | */ require __DIR__.'/../vendor/autoload.php'; ここですね。 laravelアプリケーションの最初の入り口でそのファイルを読み込んでいました。 そしてそれはcomposerを利用してautoloadを動かしており(言葉あってるか?) laravel > composer.json "autoload": { "psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } } ここで定義されています。 この記述ではapp配下のファイルを APPから始まるnamespaceとして設定しているそうです。 終わりに 知識荒削りですが少しずつその解釈に自信を持てるようになりたいですね。 自分の知識・解釈に自信ある人は尊敬します。 間違っている箇所などありましたら 編集リクエスト頂けたらと思います。 終わりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく使うLaravelのartisanコマンドまとめ

よく使うLaravelのartisanコマンドをまとめておきます。 migrate作成 データベースに新しいテーブル作成や項目追加するときのコマンドです。 # マイグレートファイルの作成 php artisan make:migration create_[テーブル名]_table # マイグレートファイルを元にデータベースを更新 php artisan migrate migrateが失敗する場合は、config、cacheのクリアをすると効果があることがあります また、laravelのバージョンを上げた場合は、以下のことも原因として考えられます seeder作成 テスト用のデータをデータベースに作成する手順です。 # seederファイルの生成 php artisan make:seeder [テーブル名]TableSeeder # seederの中でモデルを利用するのでモデルも生成する必要がある php artisan make:model [クラス名] composer dump-autoload # seedデータの作成 php artisan db:seed --class=[クラス名]TableSeeder CRUD付コントローラ生成 CRUD(データ作成、閲覧、更新、削除関数)付のコントローラを自動生成します。 php artisan make:controller [クラス名]Controller --resource 認証機能を作る 認証機能を生成します。必要なテーブル、ログイン画面、パスワード再発行機能などが自動で生成されます。 php artisan make:auth バリデーション用リクエスト作成 リクエストクラスを継承して、入力データに対するバリデーションクラスを生成します。 php artisan make:request [リクエストクラス名] コンソールコマンド生成 crontabなどで実行するCUIベースのプログラムクラスを生成します。 php artisan make:command [生成するクラス名] --command='[コマンド名]' 実行時は以下のように記述します。 php artisan [コマンド名]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでselectRawでバインドした時に「Invalid parameter number」エラー発生

状況 LaravelでselectRawを使いselect文を書き、バインド変数を設定しクエリを発行したところInvalid parameter numberのエラーが発生した。 $data = 100; $query = self::selectRaw( "'?' as test_column", [$data] ); return $query->get()->toArray(); message: "SQLSTATE[HY093]: Invalid parameter number (SQL: select '100' as test_column from `test_table`)" Invalid parameter number(パラメータの数合ってないよ!)ってなってるけど、バインド変数の数(selectRawの第2引数)もあってるし、エラーで出力されてるSQL見るとちゃんとバインドされてるし何でだ。。。と途方に暮れました。。。 https://readouble.com/laravel/8.x/ja/queries.html 結論 バインド変数の前後の'(シングルコーテーション)を削除したら問題なく動いた $data = 100; $query = self::selectRaw( "? as test_column", [$data] ); return $query->get()->toArray(); 本来もっと複雑なSQLでの出来事だったので、数値型の値を文字列として連結して扱いたかったので、シングルコーテーション無しで?を書き、CONCATを使って連結して文字列にすることで解決 CONCAT('Point(', ?, ' ', ?, ')')) AS `test_column`" 補足 'で値をくくっても問題なく値は取得できる(取得した値がstring型かint型かの違い) $query = self::selectRaw( "'100' as test_column", ); return $query->get()->toArray(); // 問題なく動作する $query = self::selectRaw( "100 as test_column", ); return $query->get()->toArray(); // 問題なく動作する 考察 エラーメッセージからすると、バインド変数の数(?の数)とバインドする値の数(selectRawの第2引数の配列の長さ)が一致していないエラーなので、Laravelの中でSQLの中のシングルコーテーションの中にある?の数の算出がうまくいっていないものと考えられる。 ちょっとソースコード読んでみるかな。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bladeで変数に入れたhtml文字列を表示させる

LaravelではViewの表示にBladeを使っています。 普通は{{と}}で囲んで変数を記述すれば変数の内容をブラウザ表示できますが、変数にhtmlを記述しているとエスケープされてしまうのでタグの内容まで表示されてしまいます。 example.blade.php <?php $html_text='1行目<br />2行目'; ?> {{$html_text}} 結果 1行目<br />2行目 {{ }}はクロスサイトスクリプティングを防ぐ効果があるので便利なのですが、変数にhtmlを入れてそれを画面表示の際に反映させたい場合は、思った結果が得られないことになります。 対応策 結果から言うと以下のように記載すればOKです。 solution.blade.php <?php $html_text='1行目<br />2行目'; ?> {!! e($html_text) !!} e()を使うことでクロスサイトスクリプティング対策を行う {!! !!}で囲むことで変数の中身をそのまま表示する 結果 1行目 2行目 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel学んで2か月で自サービスを開発した話 Part5

皆さんこんにちは、本格的な勉強会に参加したら???になった私です。 今までの開発記録はこちらへ 胡蝶蘭を捨てるくらいならワイが欲しいので、サービス開発する編 公式ドキュメントの言う通り、パッケージをインストールされたら、Inertia.jsが導入されて???になった編 マルチログインを作ってみた編 デザインをtailwindcssに丸投げする編 いよいよユーザー画面を作る 今回の目的はユーザーのプロフィール画面 そのために必要なものは ユーザー詳細表示機能 ユーザー編集機能(Adminでやったひな型を使いまわす) 画像取り込み機能 退会機能 まずはユーザー詳細機能を作っていく ルートはresourceを利用してもよかったが今回は普通に作成 web.php Route::prefix('profiles')->middleware(['auth:users'])->group(function () { Route::get('show/{profile}', [ProfileController::class, 'show'])->name('profiles.show'); Route::get('edit/{profile}', [ProfileController::class, 'edit'])->name('profiles.edit'); Route::post('update/{profile}', [ProfileController::class, 'update'])->name('profiles.update'); Route::get('destroy/{profile}', [ProfileController::class, 'destroy'])->name('profiles.destroy'); Route::post('destroy/{profile}', [ProfileController::class, 'destroy'])->name('profiles.destroy'); }); コントローラー側 ProfileController.php namespace App\Http\Controllers\User; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use App\Models\User; use App\Models\Review; use App\Models\Product; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Auth; use App\Http\Requests\UploadImageRequest; use App\Services\ImageService; use Illuminate\Validation\Rules\Password; class ProfileController extends Controller { public function show($id) { $userProfile=User::findOrFail($id); return view('user.profiles.show', compact('userProfile')); } public function edit(Request $request, $id) //リクエスト入れる { $id=$request->route()->parameter('profile'); if (!is_null($id)) { $userId=User::findOrFail($id)->id; $currentUserId=(int)$userId; $authId=Auth::id(); if ($currentUserId!==$authId) { abort(404); } } $userProfile = User::findOrFail($id); return view('user.profiles.edit', compact('userProfile')); } public function update(UploadImageRequest $request, $id) { $id=$request->route()->parameter('profile'); if (!is_null($id)) { $userId=User::findOrFail($id)->id; $currentUserId=(int)$userId; $authId=Auth::id(); if ($currentUserId!==$authId) { abort(404); } } $request->validate([ 'name' => ['required', 'string', 'max:255'], 'comment' => [ 'string', 'max:200'], 'password' => ['required', 'confirmed','string', Password::defaults()], 'prefecture' => ['string', 'max:50'], 'comment' => ['string', 'max:100'], ]); $userProfile = User::findOrFail($id); $imageFile=$request->image; if (!is_null($imageFile)&&$imageFile->isValid()) { // Storage::putFile('public/profiles', $imageFile);//リサイズなし $fileNameToStore=ImageService::upload($imageFile, 'profiles'); } $user=User::findOrFail($id); $user->name=$request->name; $user->comment=$request->comment; $user->prefecture=$request->prefecture; $user->password=Hash::make($request->password); if (!is_null($imageFile)&&$imageFile->isValid()) { $user->img=$fileNameToStore; } $user->save(); return redirect() ->route('user.profiles.show', ['profile' => $userProfile->id]) } public function destroy(Request $request,$id) { $this->middleware('auth:users'); //直接別ユーザーにアクセスするとはじくシステム $this->middleware(function ($request, $next) { $id=$request->route()->parameter('profile'); if (!is_null($id)) { $userId=User::findOrFail($id)->id; $currentUserId=(int)$userId; $authId=Auth::id(); if ($currentUserId!==$authId) { abort(404); } } return $next($request); }); try { DB::transaction(function () use ($id) { Product::select('id', ) ->where('user_id', Auth::id())->delete(); User::select('id', 'name') ->where('id', Auth::id())->delete(); }, 2); //試行する回数 } catch (Throwable $e) { Log::error($e); throw $e; } Auth::guard('users')->logout(); return redirect('/'); } } Adminのコントローラーの使いまわしだけれど、ところどころそうではない部分があるので一つずつ説明 showメゾット Userテーブルから、入力されたidの情報を取り出して終わり editメゾット ユーザー情報詳細はほかの人からも見られてもいいけれど、ユーザー編集、削除は本人以外からのアクセスをはじくようにする。 最初は、__constructに記述したが、それだとユーザー情報表示も見られなくなっちゃうので、edit,update,deleteのメゾットに書いたのがこれ。 ProfileController.php $id=$request->route()->parameter('profile'); if (!is_null($id)) { $userId=User::findOrFail($id)->id; $currentUserId=(int)$userId; $authId=Auth::id(); if ($currentUserId!==$authId) { abort(404); } } このコードが何を示しているかというと、ユーザーがアクセスしようとしているIDとログイン中のIDを照合して一致していれば、次の処理進み、そうでない場合は404ではじき返す仕組みです。 もう少し中身を見ていくと まずはddヘルパーを利用して、$request->route()を入力すると。 このような画面が出ます parametersのところに"1"がありますね。 $request->route()->parameter('profile')で"1"を取得しましょう。 その後、Userテーブルから、そのidを取得 ただし、このidは文字列で、照合したいAuth::id()は数字だから一致しません。 そのためidを数字に変換 あとはif文で照合させるだけです 画像の取り込み webサイトには必須な機能ですね! ただ画像をアップロードして表示するのだったら、素でもできますが、 と怒られそうなので、画像のリサイズをするためにInterventionImageを利用します composer require intervention/image インストールが終わったらconfig/app.phpへ app.php 'providers' => [ Intervention\Image\ImageServiceProvider::class, ], 'aliases' => [ 'InterventionImage'=>Intervention\Image\Facades\Image::class ], を追記して php artisan cache:clearを押しましょう。 あとはuse InterventionImageで利用できる。 このサイトでも、ユーザーの画像、商品画像には画像をアップロードさせるシステムを作る必要がある。 そうなると2つの問題点が出てくる 画像名が重複せずにどうやって保存するか どこに保存するか 一つ目の問題について、アップロードした画像名をランダムに変更するプログラムを作る 二つ目の問題については、laravelのstorageフォルダに保存する コードが多くなるのと商品投稿のコントローラーとも共有したいので、UploadImageRequestとImageServiceに切り分けて作成 UploadImageRequest.php public function authorize() { return true; //falseになっているのでtrueに切り替える } public function rules() { return [ 'image' => 'image|mimes:jpg,jpeg,png|max:2048', ]; } public function message() { return [ 'image' => '指定されたファイルが画像ではありません', 'mines' => '指定された拡張子(jpg/jpeg/png)ではありません', 'max' => 'ファイルサイズは2MB以内にしてください', ]; } ImageService.php class ImageService { public static function upload($imageFile, $folderName) { $fileName=uniqid(rand().'_'); //ファイル名をランダムに生成 $extension=$imageFile->extension(); //拡張子を判別 $fileNameToStore=$fileName.'.'.$extension; //上二つの文字列をつなげる $resizedImage=InterventionImage::make($imageFile)->resize(400, 400)->encode(); //InterventionImageがリサイズしてくれる Storage::put('public/'.$folderName.'/'.$fileNameToStore, $resizedImage); //storageフォルダのpublicフォルダに$folderNameを作成(今回はProfiles)して保存 return $fileNameToStore; } } 準備は整ったのでコントローラーにも追記 if (!is_null($imageFile)&&$imageFile->isValid()) { $fileNameToStore=ImageService::upload($imageFile, 'profiles');//ImageServiceに画像ファイルとフォルダ名の情報を送る } if (!is_null($imageFile)&&$imageFile->isValid()) { $user->img=$fileNameToStore; //データベースに生成されたランダム名を書き込む } これで画像の保存はOKです よし、これで大量の画像があっても重複せずに処理ができるぞい! herokuに上げても大丈夫や! あのお・・・herokuのストレージの画像は一定時間で消えますよ えええええええ!! Herokuでアップロードした画像が時間経つと消える問題 対処法AWS使うのかよ・・・ 今回で終わらなくてごめんなさい 次回はAWSに挑んできます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel/Livewireページネーションでページ移動後に自動スクロールさせる

バージョン Laravel 9.x (+Jetstream 2.x) Livewire 2.x Apline.js 3.x PHP 8.1 Livewireのページネーション use Livewire\WithPagination; class ShowPosts extends Component { use WithPagination; public function render() { return view('livewire.show-posts', [ 'posts' => Post::paginate(10), ]); } } <div> @foreach ($posts as $post) ... @endforeach {{ $posts->links() }} </div> Laravelのページネーションとほとんど同じ感覚で使えて再読み込みなしでページの表示が切り替わる。 これだけなら簡単だけど実際に使っていると「長いページの下方でページ移動するとそのまま表示が変わるだけなので若干不便」。上の方にスクロールさせたくなる。 dispatchBrowserEvent()とalpine.jsでスクロール まずPHP側。 dispatchBrowserEvent()でPHP側からJSにイベントを発行できる。 https://laravel-livewire.com/docs/2.x/events#browser use Livewire\WithPagination; class ShowPosts extends Component { use WithPagination; public function updatedPage($page) { $this->dispatchBrowserEvent('page-updated'); } public function render() { return view('livewire.show-posts', [ 'posts' => Post::paginate(10), ]); } } updatedPage()でページが切り替わったらイベントを発行。(?page=2のようにpageを使ってる場合にupdatedPage()なので複数のページネーションを使ってるなら調整が必要。) 次にview側。 dispatchBrowserEvent()からのイベントを@page-updated.windowの形で受信できる。これはalpine.jsの機能。 https://alpinejs.dev/essentials/events#listening-for-events-on-window <div x-data @page-updated.window=""> @foreach ($posts as $post) ... @endforeach {{ $posts->links() }} </div> x-dataも必須(忘れやすい) 最後にイベント受信時にスクロール。 <div x-data @page-updated.window="$el.scrollIntoView({behavior: 'smooth'})"> @foreach ($posts as $post) ... @endforeach {{ $posts->links() }} </div> $el.scrollIntoView({behavior: 'smooth'}) $elこのelement自身。この場合はdiv。alpine.jsの機能。 <div x-data @page-updated.window="document.querySelector('#test').scrollIntoView({behavior: 'smooth'})" id="test">などでも同じはず。 scrollIntoViewこのelementの位置までスクロールする。普通のJavaScript。 ただし{behavior: 'smooth'}はSafariで非対応。つまりiOSのChromeでも非対応。対応させたい場合はpolyfillを使う。 https://github.com/iamdustan/smoothscroll resources/js/app.jsなどで require('./bootstrap'); import Alpine from 'alpinejs'; window.Alpine = Alpine; Alpine.start(); import smoothscroll from 'smoothscroll-polyfill'; smoothscroll.polyfill(); @page-updated.windowを書く場所を変えれば好きな位置にスクロールできる。 これで「Livewireのページネーションでページ移動した時に上にスクロール」が実現できる。 Back to top 個人的にはあまり使わないけど「トップに戻る」ボタンなんかもこの記事の内容で実現できる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel livewire]非同期でURLのパラメータを書き変える

はじめに livewireで非同期な検索機能を実装したとして、そのままだとURLにパラメータが無いので検索結果をURLで共有する事が出来ません。 livewireプロパティとURLパラメータを同期する方法があったので書きます。 queryStringプロパティ 例としてこのようなview側とphp側を用意します app/Http/Livewire/Sample0303.php <?php namespace App\Http\Livewire; use Livewire\Component; class Sample0303 extends Component { public $search_word, $search_word2; public function render() { return view('livewire.sample0303') ->extends('adminlte::page') ->section('content'); } } resources/views/livewire/sample0303.blade.php <div> @section('content_header') <h1>検索</h1> @stop <div class="card"> <div class="card-body"> <div class="row"> <div class="col-6"> <div class="row"> <x-adminlte-input name="search_word1" label="検索文字列1" value="" fgroup-class="col-md-4" wire:model="search_word"/> <x-adminlte-input name="search_word2" label="検索文字列2" value="" fgroup-class="col-md-4" wire:model="search_word2"/> </div> </div> </div> </div> </div> </div> この状態でフォームに値を入力してもURLは変わりません。 queryStringプロパティにURLのパラメータと同期させたいプロパティ名を書きます。 app/Http/Livewire/Sample0303.php <?php namespace App\Http\Livewire; use Livewire\Component; class Sample0303 extends Component { public $search_word, $search_word2; + protected $queryString = ['search_word', 'search_word2']; public function render() { return view('livewire.sample0303') ->extends('adminlte::page') ->section('content'); } } フォームに値を入力するとURLパラメータと同期されるようになりました。 同期のタイミングはwire:modelの修飾子と同じです。 何も書いていなければ入力と同時に、lazyならフォームからフォーカスが外れた時、など。 queryStringプロパティを設定していれば、/?search_word=test&search_word2=test2 にアクセスした場合、 search_word、search_word2プロパティに値がセットされた状態でページを開けます。 参考文献 livewireドキュメント:クエリ文字列
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel Livewire]非同期でURLのパラメータを書き変える

はじめに 環境 ・Laravel 8.x ・Livewire 2.x この過去記事と環境は同じです 【Laravel】Laravel-AdminLTEとlivewireを布教したい livewireで非同期な検索機能を実装したとして、そのままだとURLにパラメータが無いので検索結果をURLで共有する事が出来ません。 livewireプロパティとURLパラメータを同期する方法があったので書きます。 queryStringプロパティ 例としてこのようなview側とphp側を用意します app/Http/Livewire/Sample0303.php <?php namespace App\Http\Livewire; use Livewire\Component; class Sample0303 extends Component { public $search_word, $search_word2; public function render() { return view('livewire.sample0303') ->extends('adminlte::page') ->section('content'); } } resources/views/livewire/sample0303.blade.php <div> @section('content_header') <h1>検索</h1> @stop <div class="card"> <div class="card-body"> <div class="row"> <div class="col-6"> <div class="row"> <x-adminlte-input name="search_word1" label="検索文字列1" value="" fgroup-class="col-md-4" wire:model="search_word"/> <x-adminlte-input name="search_word2" label="検索文字列2" value="" fgroup-class="col-md-4" wire:model="search_word2"/> </div> </div> </div> </div> </div> </div> この状態でフォームに値を入力してもURLは変わりません。 queryStringプロパティにURLのパラメータと同期させたいプロパティ名を書きます。 app/Http/Livewire/Sample0303.php <?php namespace App\Http\Livewire; use Livewire\Component; class Sample0303 extends Component { public $search_word, $search_word2; + protected $queryString = ['search_word', 'search_word2']; public function render() { return view('livewire.sample0303') ->extends('adminlte::page') ->section('content'); } } フォームに値を入力するとURLパラメータと同期されるようになりました。 同期のタイミングはwire:modelの修飾子と同じです。 何も書いていなければ入力と同時に、lazyならフォームからフォーカスが外れた時、など。 queryStringプロパティを設定していれば、/?search_word=test&search_word2=test2 にアクセスした場合、 search_word、search_word2プロパティに値がセットされた状態でページを開けます。 as構文でパラメータの表記名称を変更する事も出来ます protected $queryString = [ 'search_word' => ['as' => 's'], 'search_word2' => ['as' => 's2'] ]; 参考文献 livewireドキュメント:クエリ文字列
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

単体系の複数形のdocとタイプヒンティング戻り値の設定で補完できるようにしてプログラミングを快適にするTips

IDEはInteliJを使っているが、ファイルに下線が表示されるのがものすごく気になる。 設定で下線表示されないようにレベルを落とせばいいんだろうけど、それはしたくない。 できるだけおれおれ的なことはせず、グローバルにピュアな状態でアタックしていきたい。 Laravelになるが題材として取得系のデータ操作に限定し、単体系と複数形を用意した。 以下、Repositoryクラスを例に変移を見ていく。 私の手元のIDEでは以下の状態では、firstByIdとgetAllに下線がついている。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } public function firstById(string $id, array $relations = []) { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } public function getAll(array $relations = []) { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } 非常に気持ち悪いので解消していく。 まず、phpdocを生成しよう。 優秀なIDEは、/**と入力してreturn押下することでdocが自動生成される。 あとはそれで本当に正しいのかあなたがチェックするだけだ。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } /** * @param string $id * @param array $relations * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model */ public function firstById(string $id, array $relations = []) { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } /** * @param array $relations * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection */ public function getAll(array $relations = []) { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } 名前空間が長くて使ってるPCによっては見づらいのでuse句を使って最適化する。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } /** * @param string $id * @param array $relations * @return Builder|Model */ public function firstById(string $id, array $relations = []) { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } /** * @param array $relations * @return Builder[]|Collection */ public function getAll(array $relations = []) { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } 次にタイプヒンティングの戻り値を定義していく。 優秀なIDEは下線にマウスオーバーすると詳細がでてきて解決できそうなものであればそのままクリックでこれまた自動生成される。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } /** * @param string $id * @param array $relations * @return Builder|Model */ public function firstById(string $id, array $relations = []): Builder|Model { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } /** * @param array $relations * @return Builder[]|Collection */ public function getAll(array $relations = []): Builder|Collection { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } firstByIdで実際に返される型は、そのModelクラス(この例でいうとApp\Models\Car)ではあるが、 App\Models\CarはIlluminate\Database\Eloquent\Modelを継承しているので、現状のfirstByIdでも動作はする。 システム的にはなんら問題ない! が、プログラミングをする上では少し弱い。 carモデルはcar_class_type_idをプロパティーとして持っているが、今のままだと補完できない。 何故なら、firstByIdの戻り値の型がCarではないとIDEが認識しているから。 web.php Route::get('/', function (\App\Repositories\CarRepository $carRepository) { $car = $carRepository->firstById(1); dd($car->car_class_type_id); }); システム的には、carはcar_class_type_idを取得できるし、問題ないがそのシステムを作るためのプログラミングが快適であれば保守性も可視性もより良くなる。 ということで返り値の型がcarであると認識させるために少し補正する。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } /** * @param string $id * @param array $relations * @return Car|Builder */ public function firstById(string $id, array $relations = []): Car|Builder { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } /** * @param array $relations * @return Builder[]|Collection */ public function getAll(array $relations = []): Builder|Collection { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } ModelクラスはCarクラスで継承されているので、せっかく自動生成されはしたが、消した。 その代わり、Carクラスをdocのreturnにもタイプヒンティングの戻り値にも指定した。 これによって先ほどの、$car->car_class_type_idの部分がIDE上では色が変わりアクティブになっていることがわかる。 $car->caまで打てば補完がでるようになる。 そしたら次は、getAll。 getAllで実際に返される型は、Collection型。 タイプヒンティングの戻り値的にはOKだし動作する。 ただ、先ほどのfirstByIdの結果と同じようにプログラミングを行う上ではこれまた弱い。 Collection型で複数形の取得なので、どこかでループ処理をすることが想定される。 web.php Route::get('/', function (\App\Repositories\CarRepository $carRepository) { $cars = $carRepository->getAll(); foreach ($cars as $car) { dump($car->car_class_type_id); } }); foreach内で、dump($car->car_class_type_id);しているが、システム的に取得もできるし動作もする。 が、補完ができないのでこれもコピペしないプログラマーはタイポミスする可能性がある。 まぁ気づいて修正すれば良いのですが、生産性だとか効率の話になります。 ということでこれもループ内で補完が効くように少し補正する。 CarRepository.php <?php namespace App\Repositories; use App\Core\Repositories\Bases\BaseRepository; use App\Models\Car; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; class CarRepository extends BaseRepository { /** * @var Car */ public Car $car; /** * @param Car $car */ public function __construct(Car $car) { parent::__construct(); $this->car = $car; } /** * idから車両情報を取得する * * @param string $id * @param array $relations * @return Car|Builder */ public function firstById(string $id, array $relations = []): Car|Builder { return $this->car::with($relations) ->where('id', $id) ->firstOrFail(); } /** * 廃車ではない車両情報を取得する * * @param array $relations * @return Car[]|Builder[]|Collection */ public function getAll(array $relations = []): Car|Builder|Collection { return $this->car::with($relations) ->where('is_abolition', false) ->orderBy('display_priority') ->get(); } } これだけです。(これが最終形態) 補完が効くようになっているのはdocのreturnにCar[]を追加したからです。 タイプヒンティングの型指定のCarによって、ではありません。 タイプヒンティングの型指定はCar[]とすることができず 複数型はドキュメント型でのみ許可されていますと警告されるので、つまりやるならarrayを定義しろということですが、Collectionを定義してるのでわざわざ複雑にするまでもないだろうとの判断で、docに合わせる形で定義しました。 docはIDEを操作しやすくするもの タイプヒンティングはシステムの堅牢性をあげて保守性を高めるためのもの と認識しています。 repositoryの変更はレビュー時に特にチェックして行えばみんなが快適に作業できるようになると思います。 ものづくりに幸あれ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む