20211128のlaravelに関する記事は5件です。

【Laravel】ログインしているユーザーを検索画面に表示させなくしたい

なにをしているか Laravel6.xでツイッタークローンサイト作成中。備忘録。 なにをしたいか トップ画面から、サイトに登録しているユーザーの一覧を表示させたい。 ユーザーの検索機能を実装したが、ログインしているユーザー(自分)を表示させないようにしたい。 Controllerで検索機能実装済み。 コード search.blade.php @foreach($users as $user) @if ($user->id !== Auth::user()->id) <div class="userSearchList"> <figure><img src="images/{{ $user->images }}"></figure> <p>{{ $user->username }}</p> </div> <div class="userSearchButton"> @if (auth()->user()->isFollowing($user->id)) <form action="/users/{{ $user->id }}/unfollow" method="post"> @csrf <input type="submit" name="button" class="followButton" value="フォロー解除"> </form> @else <form action="/users/{{ $user->id }}/follow" method="post"> @csrf <input type="submit" name="follow" class="followButton" value="フォローする"> </form> @endif </div> @endif @endforeach 検索画面でユーザーのプロフィール画像・フォローボタンを設置しています(フォローしていなければフォローボタン、フォローしていればフォロー解除ボタンが表示される) search.blade.php @if ($user->id !== Auth::user()->id) この部分でテーブルに登録されているユーザーとログインしているユーザーが不一致のユーザー(要するに自分以外)の表示が実装できました! 意外とシンプルに実装できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel PJに値オブジェクトを導入してちょっと幸せになった話

はじめに 最近、開発チームでテックブログを書こうという取り組みをはじめたもので折角ならとアドベントカレンダーもやってみることにしました! テーマをしぼれるだけの下地がないのでノーテーマでお送りしますこちらのアドベントカレンダー記念すべき一発目よろしくお願い致します。 Laravelプロジェクトに値オブジェクトを導入したら、ちょっと幸せになったというお話です。 結論 まずはバリデーションに値オブジェクトを導入してみるだけでも幸せになれました。 たとえばのお話 ある日 たとえばユーザーという概念のあるWebサービスを開発しているとして、最大10文字の名前をつけることができるということにします。 まずはユーザーの登録機能を作っていきましょう。 UsersController.php public function register (UserRegisterRequest $request) { // ユーザー登録処理 } UserRegisterRequest.php public function rules() { return [ 'name' => [ 'required', 'max:10', ],      'email' => [ 'required', // 略 ], // 上記以外の項目のルール ]; } ※ 簡略化の為、Controllerクラス、Requestクラスだけにしてます。 Laravelでバリデーションを実施しようと思った際、便利なのがRequestクラスでして上記のように書くだけで名前における制約「最大10文字」を満たすことができます。 では、つぎに登録したユーザーの名前を変更する機能を追加する場合はどうでしょうか? UsersController.php public function register (UserRegisterRequest $request) { // ユーザー登録処理 } public function updateName (UserUpdateNameRequest $request) { // ユーザー名変更処理 } UserUpdateNameRequest.php public function rules() { return [ 'name' => [ 'required', 'max:10', ] ]; } 名前だけを変更する機能ということですのでControllerクラスへのメソッド追加に加え、新しいRequestクラス「UserUpdateNameRequest」クラスを作ります。 これで登録後に名前を変更できるようになりました。 それから時が経ったある日 ユーザー名を15文字許容してほしいという要望があったとしましょう。 恐らく西武ライオンズの「タイシンガーブランドン大河」選手でも登録できるようするという意図の仕様変更でしょう。 以前に作成したユーザー登録処理と名前変更処理の修正をする場合、それぞれのRequestクラスの max:10 の部分を max:15 にすれば良いのですが、これだと変更が発生するたびに漏らさず修正する必要がありますし、名前を扱う処理が増えるたび修正箇所が増えてしまいます。 私は2箇所でも共通化したいタイプなのでもうこの時点でリファクタリングしたくてたまりません。 値オブジェクトの導入により解決 Name.php class Name { private const MAX_LENGTH = 15; private $value; public function __construct(string $value) { if (!$value) { throw new \Exception('値が存在しない'); } if (self::MAX_LENGTH < mb_strlen($value)) { throw new \Exception('文字数オーバー'); } $this->value = $value; } public function getValue(): string { return $this->value; } } UserNameRule.php <?php namespace App\Rules\User; use App\Domain\Object\ValueObjects\User\Name; use Illuminate\Contracts\Validation\Rule; class UserNameRule implements Rule { // 略 public function passes($attribute, $value): bool { try { new Name($value); return true; } catch (\Exception $e) { $this->message = $e->getMessage(); return false; } } // 略 } UserRegisterRequest.php public function rules() { return [ 'name' => [ 'required', new UserNameRule(), ],      'email' => [ 'required', // 略 ], // 上記以外の項目のルール ]; } だいぶ雑ですが上記のようなクラスを導入することで修正箇所をまとめることができました。 値オブジェクトを用いたカスタムバリデーションを実装する為にRuleクラスを用意しています。 これで、メジャーリーガーの「ダルビッシュ・セファット・ファリード・有」(ダルビッシュ有)投手に対応する為20文字まで可とする場合にも1行の修正で事足ります。(ころころ名前の文字数上限変えられても困りますが) Name.php class Name { private const MAX_LENGTH = 20; // 略 } おわりに 私個人として昨今、興味のあるドメイン駆動設計についてのお話でした。 この記事ではバリデーション実装にフォーカスした話でしたが、そこに限らず値オブジェクト・エンティティを実際に導入してみて気持ちの部分含め変更が容易になったと感じると共に表現の幅が広がっていることを感じています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「例外処理」と「バリデーション」の使い分けを自分なりに理解

背景 当初の実装 以下の一連の処理を実行するプログラム 1, 「製品」リストから「注文」ボタン押下 2, 同じルートにPOST送信を実行 3, 2,のリクエストの中にorder_idを一緒に送る 4, そのidをもとに「製品」テーブルからデータを取得 5, 4,で取得した「製品」データを「カート」テーブルに格納 6, 5,の保存処理が終了すると、「カート」一覧に移動 この状況で3, での想定外処理であるリクエスト値の書き換えに対して、4, の実装時に例外処理を実装していた 例外処理にしていた理由は以下の考えによる 基本的にユーザーはここで入力処理をすることはないので、エラーが起きる可能性は非常に低い ここでエラーが発生するのは基本的に「ユーザーに悪意がある場合」のみ この「ユーザーに悪意がある場合」というのはもはや開発者の想定外の事象 開発環境 ※注意 状況をイメージできるために書いているので、正常に動くかは検証していないです。ご了承ください。 環境 PHP 8.0.4 Laravel 8.x DB 必要最低限だけ書いてます。 Userテーブル カラム名 内容 補足 id ユーザーID PK ... ... ... 製品(Product)テーブル カラム名 内容 補足 id 製品ID PK name 製品名 path 画像パス ... ... ... カート(Cart)テーブル カラム名 内容 補足 id カートID PK product_id 製品ID FK ... ... ... ※前提 userとcartはリレーション構築済み ソースコード ルート // 製品一覧表示 Route::get('/products', [App\Http\Controllers\ProductController::class, 'index'])->name('products.index'); // 製品をカートに保存 Route::post('/products', [App\Http\Controllers\ProductController::class, 'store'])->name('product.store'); コントローラ カート関連のコントローラはビューファイルをcart/index.blade.phpを表示するだけなので割愛 class ProductController extends Controller { /** * 製品一覧表示 */ public function index() { // 製品一覧情報 $products = Product::get(); return view('products.index', compact('products')); } /** * 製品をカートに保存 */ // フォームリクエストバリデーション実装 public function store(Request $request) { $validated = $request->validated(); // ユーザー情報取得 $user = User::findOrFail($validated['user_id']); // ★例外処理開始 try { // ★製品idに紐づく製品情報を取得 // Note: ココで製品IDが異常時もキャッチできるようにする $product = Product::findOrFail($validated['product_id']); // 製品をカートに保存 // Note: userとcartはリレーション構築済み $user->cart->fill([ 'product' => $product, ]); $user->cart->save(); DB::commit(); } catch (Throwable $e) { DB::rollback(); throw $e; } // カート一覧に遷移 return redirect()->route('cart.index'); } ビュー カート関連のビューファイルは本題と関係ないので割愛 ↓カート一覧 cart/index.blade.php <body> <div> @foreach ($products as $product) <form action="" method="POST"> // 画像表示 <img src= "/img/{{ $product->path }}"> // 製品名表示 <div>{{ $product->name }}</div> <div> // hiddenでリクエスト時に製品IDを一緒に送る <input type="hidden" name="productId" value="{{ $product->id }}"> <input type="submit" value="送信"> </div> </form> @endforeach </div> </body> 指摘 これに対して、レビュワーから 「リクエスト値にはバリデーションを実装しようか」 という指摘をもらった レビュワーの考えは以下の通り 「『ユーザーに悪意がある場合』というのはもはや開発者の想定外の事象」という認識は合っている ただ、これだと『ユーザーに悪意がある場合』と『システム要因によるエラー(DBの障害など)』が一緒くたになるのは問題 この時Throwableを投げてるのみで例外処理をしていた さらに、そもそも、リクエストの異常はバリデーションで防げるからこっちのが理想的 フォームリクエストバリデーションを使用すれば、次のビジネスロジック(Controller)の処理に入る前にレスポンスを返せる 特に 『ユーザーに悪意がある場合』と『システム要因によるエラー(DBの障害など)』が一緒くたになるのは問題 と 「リクエストの異常はそもそもバリデーションで防げる」 の2点については納得して修正。 だが、同時に 「じゃあ、そもそも『例外』と『バリデーション』が想定する使われ方ってなんだろ?」 と素朴な疑問が浮かんだので、メモ 今回解決したいギモン 例外 「当初実装していた例外処理の範囲ってどこまで?例外ってどういう状況?」 バリデーション 「『入力』以外のアクション(今回のような『ボタン押下』)に対してバリデーションを使うべきか」 内容 例外 概要 コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 プログラムも失敗します。 … 大惨事になる前に気付くことができるようにプログラミング言語にもガス漏れ検知器のような「失敗を伝えるしくみ」が必要なのです。 IT用語辞典 e-Words プログラムが通常の処理では想定していない事態や事象を「例外」(exception)と呼び、例外が生じた時の対応を記述したコードを例外処理という https://e-words.jp/w/例外処理.html 個人的な解釈 e-Wordsで述べられる「『想定外の処理』が実行されたときの処理」が正しそう 以下認識であながち間違いなさそう 想定できる処理:バリデーション 上記以外(=想定できない処理):例外 本題について、コーディングを支える技術 ~成り立ちから学ぶプログラミング作法には以下2つの状況を「例外を投げる状況」としている 関数呼び出し時に不足している場合 配列の範囲外を取得しようとした場合 ただ同書は言語ごとに例外の扱いが違う(返り値が違う、など)ことから 「何が『例外的状況か』は正解がない」 と結論付けている 結論 「当初実装していた例外処理の範囲ってどこまで?例外ってどういう状況?」 ? ? ぶっちゃけ「コレ!」という正解はない。 しいて言うならバリデーション以外でリクエストが飛んでくる状況 バリデーション 概要 いくつか情報ソースがあったので、列挙 ISO 9000 「観察・測定・試験などの手段によって得られた客観的証拠を提示して、利害関係者が意図する用途・適用に関する要求事項が満されていることを実環境あるは模擬環境で確認すること」 ※参考 https://www.itmedia.co.jp/im/articles/1111/07/news145.html IEEE Std 610.12 「開発プロセスの途中または最後に、利用者のニーズや意図された利用法などの要求事項を満たしているかを決定するために、システム/コンポーネントを評価するプロセス」 ※参考 https://www.itmedia.co.jp/im/articles/1111/07/news145.html IT用語辞典 e-Words 記述・入力されたデータが、あらかじめ規定された条件や仕様、形式などに適合しているかどうかを検証・確認することを表す。 ※参考 https://e-words.jp/w/バリデーション.html 個人的な解釈 ISO 9000やIEEE はプロダクト全体のことを指して述べられているが、具体化して同じことが言えると思う 特に、IEEEの「利用者のニーズや意図された利用法などの要求事項を満たしているか」という点を抜粋すれば、バリデーションの対応範囲はe-Wordsが述べている「記述・入力されたデータ」のみではなさそう 以上の文献からバリデーションとは データの受け手が受け取る対象が、想定する形式のモノか否かを正否判断する という一つの解釈を導けて、送られる形式は問わない、と言えそう。 結論 「『入力』以外のアクション(今回のような『ボタン押下』)に対してバリデーションを使うべきか」 ? ? ボタン押下でリクエスト値が送られてくるので、これも一つの入力値と捉え、バリデーションの管轄内である これにより、冒頭のレビュワーの指摘である 「リクエストの異常はそもそもバリデーションで防ぐ」 というスタンスはより説得力を帯びました。 なので、「全てのエラー系を例外処理で処理」から「バリデーションも使用」という方向性に修正し // 追加 : フォームリクエストバリデーション実装 public function store(ProductStoreRequest $request) { ... ... ... } とし、製品IDの異常値をコントローラ処理に入る前にはじくようにしました。 所見 具体的事象を乗せたとしても、かなり抽象的なアプローチをしたため解釈の余地、及び至らぬ点があるかと思います。 「私は~と思う」「~な処理の仕方もあるんじゃない?」など、忌憚ないご意見あればコメントいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php artisan migrate:refreshでMigration not found:になる

環境 laravel6.20.37 エラー内容 $ php artisan migrate:refresh Migration not found: 2021_11_28_041826_create_likess_table 原因 DBにmigrationが保存されているため mysql> SELECT * FROM migrations; +----+---------------------------------------+-------+ | id | migration | batch | +----+---------------------------------------+-------+ | 18 | 2021_11_28_041826_create_likess_table | 1 | +----+---------------------------------------+-------+ 1 row in set (0.00 sec) migration削除 mysql> delete from migrations where batch = 1; 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CircleCiでテスト時にCaused by PDOException: SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "users" does not exist LINE 1:

環境 laravel 6.20.37 phpuit 8.5.21 エラー内容 Caused by PDOException: SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "users" does not exist LINE 1: insert into "users" ("name", "email", "email_verified_at", "... ^ 原因 DatabaseTransactionsを使用していたことが原因のようでした。 AnswerControllerTest.php use DatabaseTransactions; RefreshDatabaseを使用することでエラーがなくなりました。 AnswerControllerTest.php use Illuminate\Foundation\Testing\RefreshDatabase; use RefreshDatabase; RefreshDatabaseは各テストの後にデータベースをリセットできるので前のテストがその後のテストデータに影響しないのに対し、DatabaseTransactionsは各テストケースをデータベーストランザクションでラップしてデータベースをリセットするという違いがあるようです。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む