Git 過去のコミットに戻る
過去ログ表示
コミットIDを調べてgit reset --hard
git reset --hard 531f777acdc93aeb3a93297aa23949475b305d28
Micromodal
Micromodal.js
簡単にモーダルウィンドウを実装できるライブラリ。
CSSのデザインは標準でははいっていないので追加する。
デザインされた雛形を使う
- 軽量
- jQuery未使用
- 背景スクロールを固定
- 閉じるボタン、背景クリック、ESCボタン押下で閉じる事が可能 micromodal.vercel.app
install
インストールできたらpackage.jsonに追加される。
import
app.cssでインポート
@import 'micromodal'; @tailwind base; @tailwind components; @tailwind utilities;
下記フォルダに記載。
initメソッドの中にオプションを書く。
今回はdisableScroll
を設定
これにより、モーダルが開いている間はページのスクロールが無効になります。デフォルト値はfalse
import MicroModal from 'micromodal'; // es6 module MicroModal.init({ disableScroll: true });
書き方↓
スタイリング
CSSの雛形↓をコピーしてresource/css/micromodal.css
ファイルを作り、貼り付ける。
→npm run dev
実行しコンパイルする。
View
product新規作成時、画像を4枚選択できるようにする
コンポーネントを作成し、雛形のHTMLを貼り付け。
button
はそのままだとsubmitになってpost通信してしまうのでtype="button"
を追加
モーダルを表示するためのボタンを追加
<a data-micromodal-trigger="modal-1" href='javascript:;'>Open Modal Dialog</a>
<x-select-image name="image1" />
<div class="modal micromodal-slide" id="modal-1" aria-hidden="true"> <div class="modal__overlay" tabindex="-1" data-micromodal-close> <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title"> <header class="modal__header"> <h2 class="modal__title" id="modal-1-title"> Micromodal </h2> <button type="button" class="modal__close" aria-label="Close modal" data-micromodal-close></button> </header> <main class="modal__content" id="modal-1-content"> <p> Try hitting the <code>tab</code> key and notice how the focus stays within the modal itself. Also, <code>esc</code> to close modal. </p> </main> <footer class="modal__footer"> {{-- `button`はそのままだとsubmitになってpost通信してしまうので`type="button"`を追加 --}} <button type="button" class="modal__btn modal__btn-primary">Continue</button> <button type="button" class="modal__btn" data-micromodal-close aria-label="Close this dialog window">Close</button> </footer> </div> </div> </div> {{-- モーダルを表示するためのボタンを追加 --}} <a data-micromodal-trigger="modal-1" href='javascript:;'>Open Modal Dialog</a>
Eager Loading
概要
リレーション先の情報を取得する際には、N+1問題が発生するので、EagerLoading Withメソッドを使い取得する必要がある。
画像一覧のindex.bladeを確認すると
:filename="$product->imageFirst->filename"
とリレーションでリレーション先の情報を取得している
SQLを確認すると
画像が表示されている情報分SQLが発行されて折り、画像が1000件なら1000回SQLが発行されてパフォーマンス落ちる
→N+1問題
N + 1問題の対策
→EagerLoading
リレーション先のリレーション情報を取得 withメソッド、リレーションをドットでつなぐ
‘id’, Auth::id()
をwhereすることでログインしているOwnerをgetできる。
$ownerInfo = Owner::with(‘shop.product.imageFirst’) ->where(‘id’, Auth::id())->get();
$ownerInfoをforeachで回してview側で確認していく
dd($owner->shop->product);
を確認すると、productが5つ表示されていて、これを1つずつ表示する必要があるので、さらにforeachをかく。
ownerに紐付いたproduct情報を表示。
dd($product->imageFirst->filename);
を確認すると、画像が取得されてるとわかる。
<?php foreach($ownerInfo as $owner) dd($owner->shop->product); foreach($owner->shop->product as $product) { dd($product->imageFirst->filename); }
foreachをview側で実装する
→発行されているSQLを確認すると
select * from
imageswhere
images.
idin (1, 2, 3, 4)
と1つにまとまっていることがわかる。
Laravel 外部キーやリレーションなど
productsテーブル作成
productモデル、マイグレーション作成
外部キーを設定する際には、親のモデルを削除するかどうか、親を削除したときに同時に削除するかを考える必要がある。
ownerを削除したらshopも消える、shopが消えたらproductも消えるようにする→cascadeで削除
今回はcategoryは消えないようにするので、cascadeなし。
モデル名_id
とすることでLaravelが自動でどのモデルか推測してくれるがimage1
はできないので、constrained('images')
でどのモデルか指定する
<?php public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); //ownerを削除したらshopも消える、shopが消えたらproductも消えるようにする→cascade $table->foreignId('shop_id')->constrained()->onUpdate('cascade')->onDelete('cascade'); $table->foreignId('secondary_category_id')->constrained(); //モデル名_idとすることでLaravelが自動でどのモデルか推測してくれるがimage1はできないので、constrained('images')どのモデルか指定する //画像はからの場合もあるのでnullableをつける $table->foreignId('image1')->nullable()->constrained('images'); $table->timestamps(); }); }
Route
ルーティング(リソース)
<?php //productのルート(resource) Route::resource('products', ProductController::class) //adminで認証していたらで表示 ->middleware('auth:owners')->except('show');
ProductController作成(リソース)
モデルの中でリレーションを定義
productモデルにリレーションを追記していく。
categoryやimageを複数の中から1つを選ぶのでbelongs toとする
- リレーション関係のメソッドの書き方
第1引数・・紐付けるモデル
第2引数・・カスタム外部キー名
第3引数・・親モデルが主キーとしてidを使用していない場合、カスタムキーを指定する
モデル名_id
とすることでLaravelが自動でどのモデルか推測してくれるがimage1のように
_id
をつけないと推測されないので、idと紐付けると追加
<?php namespace Database\Seeders; use Illuminate\Support\Facades\DB; use Illuminate\Database\Seeder; class ProductSeeder extends Seeder class Product extends Model { use HasFactory; public function shop() { //productはshopに属する。 return $this->belongsTo(Shop::class); } public function imageFirst() { //productはImageに属する。 return $this->belongsTo(Image::class,'image1','id'); } public function category() { //productはsecondaryCategoryに属する。メソッド名を変えたので外部キーもかく return $this->belongsTo(SecondaryCategory::class,'secondary_category_id'); } }
Tinkerでリレーションの確認
Stocksテーブル作成
在庫管理・履歴用のテーブルとして作成。
在庫管理は処理が頻繁に発生するので、実際の開発環境では
マスターテーブル(参照用), トランザクションテーブル(処理用) に分けて管理する。
$ php artisan make:model Stock -m
1つのproductが複数のStockをもつ→hasMany
<?php public function stock() { //1つのproductが複数のStockをもつ。 return $this->hasMany(Stock::class); }
トランザクションテーブルとしてt_テーブル名
という名前で作るので、テーブル名をかえる場合はモデルに記載
する
Stock.php
→ protected $table = ’t_stocks’;
マイグレーション
テーブル名をt_stocksへ変更
Upメソッド、downメソッドともにテーブル名変更
<?php public function up() { Schema::create('t_stocks', function (Blueprint $table) { $table->id(); $table->foreignId('product_id')->constrained()->onUpdate('cascade')->onDelete('cascade'); $table->tinyInteger('type'); $table->integer('quantity'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('t_stocks'); }
Seeder
root@aba451969df6:/var/www/html# php artisan make:seed StockSeeder
Seeder created successfully.
root@aba451969df6:/var/www/html# php artisan migrate:refresh --seed
<?php public function run() { DB::table('t_stocks')->insert([ [ 'product_id' => 1, 'type' => 1, 'quantity' => 5, ], [ 'product_id' => 1, 'type' => 1, 'quantity' => -2, ]]); }
Tinkerでリレーションの確認
Laravel Seeder複数テーブル書く方法
primary_categoriesテーブルとsecondary_categoriesテーブルを作成。
どちらも関係のあるカテゴリーなので1つのマイグレーションファイルに書いていくので1つだけ作成。
primary_categories
は複数のカテゴリーをもつので1対多の関係になる。
php artisan make:model PrimaryCategory -m
php artisan make:model SecondaryCategory
PrimaryCategoryモデル
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use APP\Models\SecondaryCategory; class PrimaryCategory extends Model { use HasFactory; public function secondary() { //primaryは複数のcategoryをもてるのでhasManyとする return $this->hasMany(SecondaryCategory::class); } }
SecondaryCategoryモデル
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use APP\Models\PrimaryCategory; class SecondaryCategory extends Model { use HasFactory; public function primary() { //primaryに属する return $this->belongsTo(PrimaryCategory::class); } }
マイグレーションファイル
2つのテーブルを記載
※secondaryテーブルから先に削除しないと外部キーエラーになる。
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateCategoriesTable extends Migration { public function up() { //schema::create('テーブル名') Schema::create('primary_categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->integer('sort_order'); $table->timestamps(); }); Schema::create('secondary_categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->integer('sort_order'); //外部キーFK設定 $table->foreignId('primary_category_id')->constrained(); $table->timestamps(); }); } public function down() { //migate:refreshなどのコードでテーブルを削除する処理を書く //secondaryテーブルから先に削除しないと外部キーエラーになるので注意 Schema::dropIfExists('secondary_categories'); Schema::dropIfExists('primary_categories'); } }
Seeder作成
DatabasesSeeder
に記載→CategorySeeder::class,
楽天のカテゴリーを参考にSeederを作成。2つのテーブルを書いていく。
CategorySeeder.php
migrate
Tinkerでリレーションの確認
Laravel 画像の複数アップロード
inputタグに追記
multiple属性、name属性をfiles[image]とする。
files[image]としてファイルを選択して送信すると、Requestとして配列で渡ってくるので配列のバリデーションが必要。
→フォームリクエストに追記する。
<div class="p-2 w-1/2 mx-auto"> <div class="relative"> <label for="image" class="leading-7 text-sm text-gray-600">画像</label> {{-- 画像の場合file,name属性でコントローラのほうでimageとすれば画像を取得できる --}} <input type="file" id="image" name="files[][image]" multiple accept=“image/png,image/jpeg,image/jpg” class="w-full ~~"> </div> </div>
バリデーション追記
readouble.com
配列内の属性をバリデーションするには、「ドット記法」が使える。
リクエストにfiles[][image]
が渡るので、以下のように書ける。
files[].*.image
→フォームリクエストに記述。
<?php public function rules() { $rules = [ 'image' => ['image','mimes:jpg,jpeg,png','max:2048'], 'files.*.image' => ['image','mimes:jpg,jpeg,png','max:2048'], ]; if($this->route === 'owner.images.store'){ $rules = array_merge($rules,[ 'files' => 'required', ],); } return $rules; } public function messages() { return [ 'image' => '指定されたファイルが画像ではありません。', 'mines' => '指定された拡張子(jpg/jpeg/png)ではありません。', 'max' => 'ファイルサイズは2MB以内にしてください。', ]; }
Controller
画像を複数アップロードする場合、取得する方法が変わってくる。
fileメソッドの引数としてname属性であるfilesをいれることで複数の画像を配列形式で取得できる。
<?php public function store(UploadImageRequest $request) { //create.blade側のinputのname属性filesを引数とすることで、複数の画像を配列形式で取得 $imageFiles = $request->file('files'); if(!is_null($imageFiles)){ //foreachで1つずつImageServiceでファイル名作る→拡張子取得→interventionImageでリサイズ //出来上がったファイル名を$fileNameToStoreとして取得し、createで保存 foreach($imageFiles as $imageFile){ $fileNameToStore = ImageService::addByImage($imageFile, 'products'); Image::create([ 'owner_id' => Auth::id(), 'filename' => $fileNameToStore ]); } } return redirect()->route('owner.images.index')->with(['message' => '画像登録を実施しました。','status'=>'info']); }
Docker上のLAMP環境にXdebugをいれる。(VScode)
概要
VScode拡張機能、Xdebug v3.2.0のインストール
Docker上に構築したLAMP環境にxdebugを入れてステップイン・アウトを実行する
vscode上で実行できるようにする
- ディレクトリ構造
現在のversion
- mysql5.7
- phpmyadmin
- Laravel Framework 8.83.26
- PHP Version 8.1.13
- Apache/2.4.54 (Debian)
php.ini作成
(Xdebugのv3系)として設定する。
compose.ymlのvolumesでphp.ini
のPATH追加、デフォルトではdockerにはphp.ini
はないので、ローカルにphp.ini
を作ってdocker側にマウント。→Dockerfileと同じ場所に配置する。
[xdebug] xdebug.mode=debug xdebug.start_with_request = yes ; ホスト側のIP ; host.docker.internalとすることでdockerのhostマシンのIPを解決 xdebug.client_host=host.docker.internal xdebug.client_port=9013 xdebug.discover_client_host = 1
Dockerfile
Dockerhubからwith apacheのものを使用し、xdebugをインストール。
またdocker-php-ext-enableでpecl拡張を有効化できるのでdocker-php-ext-enable xdebugを実行。
ビルド後、/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
にzend_extension=xdebug
が出力。
# image FROM php:8.1-apache # Laravelの依存PHPモジュールのInstall # Laravelで必要なmodRewriteを有効化 RUN apt update \ && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \ && mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled # Install MySQL RUN docker-php-ext-install pdo pdo_mysql # コンテナの作業ディレクトリを指定 WORKDIR /var/www/html # Install Composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Install xdebug RUN pecl install xdebug \ && docker-php-ext-enable xdebug # Install node.js,InterventionImage RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ && apt-get install -y nodejs \ && docker-php-ext-install -j$(nproc) gd \
Docker-compose.yml
デフォルトではdockerにはphp.iniはないので、ローカルにphp.iniを作ってdocker側に追加。
アプリケーションコンテナのvolumesにマウントするPATHを記載。
→マウントされてるか確認。
version: '3' services: # php-apacheコンテナ web_1: container_name: laravel_umarche # Dockerfileを使って、コンテナをビルドするpath。web_umarcheディレクトリに宣言されたDockerfileがコンテナのビルドに使用される。 build: ./web_umarche/app # php-apacheコンテナの前にまずDBコンテナをビルドする必要があることをDockerに認識 depends_on: - db # コンテナとホスト側のディレクトリをマウントする volumes: # laravelのソースが入るpath - ./umarche/:/var/www/html/ # Apacheによりデフォルトで有効化されている000-default.confホストとアプリケーションの仮想ホストの設定をリンク - ./web_umarche/app/default.conf:/etc/apache2/sites-enabled/000-default.conf # php.iniにxdebugのport設定 - ./web_umarche/app/php.ini:/usr/local/etc/php/php.ini # コンテナ内部80番portを開いて、ホストの8005番にポートフォワーディング ports: - "8005:80" phpmyadmin: container_name: phpmyadmin image: phpmyadmin restart: always #コンテナ内部80番portを開いて、ホストの8080番にポートフォワーディング ports: - "8080:80" #自動ログイン環境設定、host、ログイン情報指定 environment: # 1に設定すると任意のサーバーへの接続が許可される - PMA_ARBITRARY=1 # SQLサーバーのportの設定 - PMA_HOST=db:3306 # SQLサーバーに接続するユーザー名 - PMA_USER=root # SQLサーバーに接続するユーザーのパスワード - PMA_PASSWORD=rootroot db: container_name: mysql # DockerHubからMySQL5.7イメージをDLしてくる指定 image: mysql:5.7 # 起動時のコマンド command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci # コンテナ内の環境変数→.envにも記載 environment: MYSQL_DATABASE: laravel_umarche MYSQL_USER: docker MYSQL_PASSWORD: root MYSQL_ROOT_PASSWORD: rootroot TZ: Asia/Tokyo # mysqlはデフォルトで3306port ports: - "3311:3306" # 設定ファイルとMySQLのデータが保存されるpathをマウント。コンテナは基本的に起動時に変更されてもコンテナ自体が止まるとデータが消えてしまうため、保存しておきたいものはホストマシンと同期しておく必要がある。 volumes: - ./web_umarche/db/data:/var/lib/mysql - ./web_umarche/db/my.cnf:/etc/mysql/conf.d/my.cnf
ビルド!
docker-compose down && docker-compose build && docker-compose up
インストールに成功するとphpinfoにxdebug項目が追加される
Vscode側の設定
xdebugをインストール
端を右クリックして実行とデバッグのアイコンを出す。
右の歯車マークを押してlaunch.json
を編集。
「pathMappings」はドキュメントルートではなく、ローカルとコンテナ側でマウントしている同じPATHをマッピングする
→docker-compose.ymlのvolumes
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Xdebug Cloud", "type": "php", "request": "launch", "port": 9013, "pathMappings": { "/var/www/html":"${workspaceRoot}/umarche" } },