tailwindCSS環境構築とミニファイ化
tailwind CLI
前提 nodeJSとnpmがインストールされていること。
1- Tailwind CSS をインストールする
npm install -D tailwindcss npx tailwindcss init
npmの初期化
npm init -y
2- テンプレートのパスを構成する
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
3- Tailwind ディレクティブを CSS に追加します
src/input.css
@tailwind base; @tailwind components; @tailwind utilities;
4- Tailwind CLI ビルド プロセスを開始する
npx tailwindcss -i ./src/input.css -o ./src/output.css --watch
5- HTML で Tailwind の使用を開始する
src/index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="./output.css" rel="stylesheet">
</head>
<body>
<h1 class="text-3xl font-bold underline">
Hello world!
</h1>
</body>
</html>
スクリプト登録とエラーの削除
- ビルドプロセスをスクリプトに登録
package.json
これでnpm run buildでビルドされるようになる。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npx tailwindcss -i ./src/input.css -o ./src/output.css --watch"
},
ミニファイ化
build.cssがプロジェクトディレクトリ直下に作られる。
デプロイはミニファイファイルにて。
npx tailwindcss -o build.css --minify
FontAwesomeをダウンロードして使う
ダウンロード
- FontAwesome公式
- zipを展開
- webアプリに設置
設置するもの - html headで読み込み
<link rel="stylesheet" href="./css/all/min.css">
Laravel ECサイト step03
認証
Laravel Breeze追加
- breezeパッケージの追加
sail composer require'laravel/breeze:*' --dev
:* の意味「利用可能な任意のバージョン」 - Laravel Breezeパッケージのインストール
sail artisan breeze:install
| 質問 | 回答 |
|---|---|
| Which Breeze stack would you like to install?(どのBreezeスタックをインストールしますか?) | 0 |
| Would you like dark mode support?(ダークモードをサポートしますか?) | no |
| Would you prefer Pest tests instead of PHPUnit?(PHPUnitの代わりにPestを使いますか?) | no |
- ルーティングの復元
<?php
use App\Http\Controllers\ProfileController;
+use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require __DIR__.'/auth.php';
+Route::resource('products', ProductController::class);
依存関係のインストール
npm installアセットのビルド
npm run build
urlにアクセスするとログインと会員登録ページのリンクが追加されている。
Laravel Breezeエラーメッセージの日本語化
以下のコマンドを実行
php -r "copy('https://readouble.com/laravel/8.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"
- 日本語化ファイルをインストールするためのスクリプトを、install-ja-lang.phpという名前でダウンロードする
- 1でダウンロードしたinstall-ja-lang.phpファイルを実行する(resources/lang/jaフォルダの中に各種日本語化ファイルが生成される)
- 不要になったinstall-ja-lang.phpファイルを削除する
-> resources/lang/jaフォルダの中に生成されたvalidation.phpというファイルには、日本語化されたエラーメッセージが記述されています。
configフォルダ内のapp.phpファイルを開き、以下のように編集
config/app.php
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'Asia/Tokyo',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
- 'locale' => 'en',
+ 'locale' => 'ja',
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
'fallback_locale' => 'en',
各ページの表記の日本語化
resources/lang/ja.jsonファイルを作成
ja.json
{
"Email": "メールアドレス",
"Password": "パスワード",
"Remember me": "ログイン状態を保存する",
"Log in": "ログイン",
"Forgot your password?": "パスワードをお忘れですか?",
"Name": "名前",
"Confirm Password": "パスワード(確認用)",
"Register": "登録",
"Already registered?": "すでに登録済みですか?",
"Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.": "パスワードをお忘れですか?ご心配なく。メールアドレスをお知らせいただければ、パスワード再設定用のリンクをメールでお送りします。",
"Email Password Reset Link": "パスワード再設定用リンクの送信",
"Reset Password": "パスワード再設定",
"Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another.": "ご登録ありがとうございます!ご利用を開始する前に、メールアドレスをご確認ください。メールが届いていない場合は、再度お送りいたします。",
"A new verification link has been sent to the email address you provided during registration.": "ご登録いただいたメールアドレスに新しい認証リンクを送信しました。",
"Resend Verification Email": "認証メールの再送信",
"Profile": "アカウント",
"Log Out": "ログアウト",
"Dashboard": "ダッシュボード",
"You're logged in!": "ログインしました!",
"Profile Information": "アカウント情報",
"Update your account's profile information and email address.": "アカウント情報とメールアドレスを更新できます。",
"Your email address is unverified.": "メールアドレスが未確認です。",
"Click here to re-send the verification email.": "こちらをクリックして認証メールを再送信してください。",
"A new verification link has been sent to your email address.": "あなたのメールアドレスに新しい認証リンクを送信しました。",
"Save": "保存",
"Saved.": "保存しました。",
"Update Password": "パスワード更新",
"Ensure your account is using a long, random password to stay secure.": "アカウントの安全性を保つために、長くてランダムなパスワードを使用してください。",
"Current Password": "現在のパスワード",
"New Password": "新しいパスワード",
"Delete Account": "アカウント削除",
"Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.": "アカウントを削除すると、関連するすべてのデータが永久に削除されます。アカウントを削除する前に、必要に応じてデータをバックアップしてください。",
"Are you sure you want to delete your account?": "本当にアカウントを削除してもよろしいですか?",
"Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.": "アカウントを削除すると、関連するすべてのデータが永久に削除されます。アカウントを削除してよろしければ、パスワードを入力してください。",
"Cancel": "キャンセル",
"Verify Email Address": "メールアドレス確認",
"Hello!": "こんにちは!",
"Please click the button below to verify your email address.": "下のボタンをクリックしてメールアドレスを確認してください。",
"If you did not create an account, no further action is required.": "心当たりがない場合は、本メールを破棄してください。",
"Regards": "よろしくお願いいたします",
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "「:actionText」ボタンを押しても何も起きない場合、以下のURLをコピーしてWebブラウザに貼り付けてください。",
"Reset Password Notification": "パスワード再設定のお知らせ",
"You are receiving this email because we received a password reset request for your account.": "あなたのアカウントでパスワード再設定のリクエストがありました。",
"This password reset link will expire in :count minutes.": "このパスワード再設定リンクの有効期限は :count 分です。",
"If you did not request a password reset, no further action is required.": "心当たりがない場合は、本メールを破棄してください。"
}
メール認証の有効化
- メール認証機能を有効
app/Models/User.phpを編集
<?php
namespace App\Models;
-// use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
-class User extends Authenticatable
+class User extends Authenticatable implements MustVerifyEmail
{
//======== 中略 ========
}
Laravelプロジェクトとメールサーバーを連携
- Gmailの2段階認証プロセスを有効に
Googleアカウントのトップページ
「アプリパスワード」 > 「App name」にアプリ名を入力してパスワードを生成 - GmailをSMTPサーバーに設定
.envを編集
- Gmailの2段階認証プロセスを有効に
#======== 前略 ========
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
-MAIL_HOST=mailpit
+MAIL_HOST=smtp.gmail.com
-MAIL_PORT=1025
+MAIL_PORT=587
-MAIL_USERNAME=null
+MAIL_USERNAME=アプリパスワードを発行したアカウントのGmailアドレス
-MAIL_PASSWORD=null
+MAIL_PASSWORD=アプリパスワード
-MAIL_ENCRYPTION=null
+MAIL_ENCRYPTION=tls
-MAIL_FROM_ADDRESS="hello@example.com"
+MAIL_FROM_ADDRESS=アプリパスワードを発行したアカウントのGmailアドレス
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
#======== 後略 ========
リダイレクト先を変更
- 会員登録後
app/Http/Controllers/Auth/RegisteredUserController.phpを編集
//======== 前略 ========
/**
* Handle an incoming registration request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
- return redirect(RouteServiceProvider::HOME);
+ return redirect('/verify-email');
}
}
- ログイン後
app/Providers/RouteServiceProvider.phpを編集
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
- public const HOME = '/dashboard';
+ public const HOME = '/';
//======== 後略 ========
Laravel ECサイト step02
CRUD
Create
- 商品モデルを作成
| カラム名 | 説明 | Laravel関数 | MySQLのデータ型 |
|---|---|---|---|
| name 商品名 | string | varchar(255) | |
| description | 商品の説明文 | text | text |
| price | 価格 | integer | int(11) |
sail artisan make:model Product -m
database\migrations\XXXX_XX_XX_XXXXXX_create_products_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
+ $table->string('name');
+ $table->text('description');
+ $table->integer('price')->unsigned();
/**
* マイナスの値が保存できないようにしている。
*/
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
};
商品のコントローラーを作成
CRUDをまとめて作成するので「--resource」オプション
sail artisan make:controller ProductController --resource --model=Productカテゴリと商品とを紐づけ
app\Models\Product.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
+
+ public function category()
+ {
+ return $this->belongsTo(Category::class);
+ }
}
app\Models\Category.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
+
+ public function products()
+ {
+ return $this->hasMany(Product::class);
+ }
}
マイグレーションファイルdatabase\migrations\XXXX_XX_XX_XXXXXX_create_products_table.phpに1行追加
新しいカラムとして、カテゴリのIDを追加
| カラム名 | 説明 | Laravel関数 | MySQLのデータ型 |
|---|---|---|---|
| category_id | カテゴリID | integer | int(11) |
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->integer('price')->unsigned();
+ $table->integer('category_id')->unsigned();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
};
Eloquentは外部キーカラムを自動的に決定します。規約により、親モデルの小文字クラス名に「_id」という接尾辞を付けます。
この場合では、Categoryモデルに対する外部キーカラムはcategory_idとなり、商品とカテゴリが紐づきます。
カテゴリーIDにはマイナスの値はあり得ないので、unsignedを指定しています。
<?php
use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\ProductController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
+
+Route::resource('products', ProductController::class);
- コントローラー
app\Http\Controllers\ProductController.php- indexアクション
public function index()
{
- //
+ $products = Product::all();
+
+ return view('products.index', compact('products'));
}
indexで呼び出すviewファイル
resources\views\products\index.blade.php
+<a href="{{ route('products.create') }}"> Create New Product</a>
+
+<table>
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ <th>Price</th>
+ <th>Category ID</th>
+ <th >Action</th>
+ </tr>
+ @foreach ($products as $product)
+ <tr>
+ <td>{{ $product->name }}</td>
+ <td>{{ $product->description }}</td>
+ <td>{{ $product->price }}</td>
+ <td>{{ $product->category_id }}</td>
+ <td>
+ <a href="{{ route('products.show',$product->id) }}">Show</a>
+ <a href="{{ route('products.edit',$product->id) }}">Edit</a>
+ </td>
+ </tr>
+ @endforeach
+</table>
- createアクション
public function create()
{
- //
+ return view('products.create');
}
データ入力フォーム
resources\views\products\create.blade.php
+<div>
+ <h2>Add New Product</h2>
+</div>
+<div>
+ <a href="{{ route('products.index') }}"> Back</a>
+</div>
+
+<form action="{{ route('products.store') }}" method="POST">
+ @csrf
+
+ <div>
+ <strong>Name:</strong>
+ <input type="text" name="name" placeholder="Name">
+ </div>
+ <div>
+ <strong>Description:</strong>
+ <textarea style="height:150px" name="description" placeholder="Description"></textarea>
+ </div>
+ <div>
+ <strong>Price:</strong>
+ <input type="number" name="price" placeholder="Price">
+ </div>
+ <div>
+ <button type="submit">Submit</button>
+ </div>
+
+</form>
- storeアクション
app\Http\Controllers\ProductController.php
public function store(Request $request)
{
- //
+ $product = new Product();
+ $product->name = $request->input('name');
+ $product->description = $request->input('description');
+ $product->price = $request->input('price');
+ $product->save();
+
+ return to_route('products.index');
}
- showアクション
app\Http\Controllers\ProductController.php
public function show(Product $product)
{
- //
+ return view('products.show', compact('product'));
}
viewファイル作成
resources\views\products\show.blade.php
+<div>
+ <h2> Show Product</h2>
+</div>
+<div>
+ <a href="{{ route('products.index') }}"> Back</a>
+</div>
+
+<div>
+ <strong>Name:</strong>
+ {{$product->name}}
+</div>
+
+<div>
+ <strong>Description:</strong>
+ {{$product->description}}
+</div>
+
+<div>
+ <strong>Price:</strong>
+ {{$product->price}}
+</div>
- editアクション
public function edit(Product $product)
{
- //
+ return view('products.edit', compact('product'));
}
resources\views\products\edit.blade.php
+<div>
+ <h2>Edit Product</h2>
+</div>
+<div>
+ <a href="{{ route('products.index') }}"> Back</a>
+</div>
+
+<form action="{{ route('products.update',$product->id) }}" method="POST">
+ @csrf
+ @method('PUT')
+
+ <div>
+ <strong>Name:</strong>
+ <input type="text" name="name" value="{{ $product->name }}" placeholder="Name">
+ </div>
+ <div>
+ <strong>Description:</strong>
+ <textarea style="height:150px" name="description" placeholder="description">{{ $product->description }}</textarea>
+ </div>
+ <div>
+ <strong>Price:</strong>
+ <input type="number" name="price" value="{{ $product->price }}">
+ </div>
+ <div>
+ <button type="submit">Submit</button>
+ </div>
+
+</form>
- updateアクション
public function update(Request $request, Product $product)
{
- //
+ $product->name = $request->input('name');
+ $product->description = $request->input('description');
+ $product->price = $request->input('price');
+ $product->update();
+
+ return to_route('products.index');
}
- destroyアクション
public function destroy(Product $product)
{
- //
+ $product->delete();
+
+ return to_route('products.index');
}
resources\views\products\index.blade.phpを変更
<a href="{{ route('products.create') }}"> Create New Product</a>
<table>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Category ID</th>
<th >Action</th>
</tr>
@foreach ($products as $product)
<tr>
<td>{{ $product->name }}</td>
<td>{{ $product->description }}</td>
<td>{{ $product->price }}</td>
<td>{{ $product->category_id }}</td>
<td>
+ <form action="{{ route('products.destroy',$product->id) }}" method="POST">
<a href="{{ route('products.show',$product->id) }}">Show</a>
<a href="{{ route('products.edit',$product->id) }}">Edit</a>
+ @csrf
+ @method('DELETE')
+ <button type="submit">Delete</button>
+ </form>
</td>
</tr>
@endforeach
</table>
商品登録時にカテゴリを選択できるようにしよう
app\Http\Controllers\ProductController.phpにモデルを追加
<?php namespace App\Http\Controllers; use App\Models\Product; +use App\Models\Category; use Illuminate\Http\Request; class ProductController extends Controller /* === 後略 === */
app\Http\Controllers\ProductController.phpのcreateアクションを修正
public function create()
{
- return view('products.create');
+ $categories = Category::all();
+
+ return view('products.create', compact('categories'));
}
resources\views\products\create.blade.phpを修正
<div>
<h2>Add New Product</h2>
</div>
<div>
<a href="{{ route('products.index') }}"> Back</a>
</div>
<form action="{{ route('products.store') }}" method="POST">
@csrf
<div>
<strong>Name:</strong>
<input type="text" name="name" placeholder="Name">
</div>
<div>
<strong>Description:</strong>
<textarea style="height:150px" name="description" placeholder="Description"></textarea>
</div>
<div>
<strong>Price:</strong>
<input type="number" name="price" placeholder="Price">
</div>
+ <div>
+ <strong>Category:</strong>
+ <select name="category_id">
+ @foreach ($categories as $category)
+ <option value="{{ $category->id }}">{{ $category->name }}</option>
+ @endforeach
+ </select>
+ </div>
<div>
<button type="submit">Submit</button>
</div>
</form>
app\Http\Controllers\ProductController.phpのstoreアクションでcategory_idをデータベースに保存できるように修正
public function store(Request $request)
{
$product = new Product();
$product->name = $request->input('name');
$product->description = $request->input('description');
$product->price = $request->input('price');
+ $product->category_id = $request->input('category_id');
$product->save();
return to_route('products.index');
}
app\Http\Controllers\ProductController.phpのedhitアクションを変更
public function edit(Product $product)
{
- return view('products.edit', compact('product'));
+ $categories = Category::all();
+
+ return view('products.edit', compact('product', 'categories'));
}
resources\views\products\edit.blade.phpを修正
<div>
<h2>Edit Product</h2>
</div>
<div>
<a href="{{ route('products.index') }}"> Back</a>
</div>
<form action="{{ route('products.update',$product->id) }}" method="POST">
@csrf
@method('PUT')
<div>
<strong>Name:</strong>
<input type="text" name="name" value="{{ $product->name }}" placeholder="Name">
</div>
<div>
<strong>Description:</strong>
<textarea style="height:150px" name="description" placeholder="description">{{ $product->description }}</textarea>
</div>
<div>
<strong>Price:</strong>
<input type="number" name="price" value="{{ $product->price }}">
</div>
+ <div>
+ <strong>Category:</strong>
+ <select name="category_id">
+ @foreach ($categories as $category)
+ @if ($category->id == $product->category_id)
+ <option value="{{ $category->id }}" selected>{{ $category->name }}</option>
+ @else
+ <option value="{{ $category->id }}">{{ $category->name }}</option>
+ @endif
+ @endforeach
+ </select>
+ </div>
<div>
<button type="submit">Submit</button>
</div>
</form>
app\Http\Controllers\ProductController.phpのupdadeアクションを修正
public function update(Request $request, Product $product)
{
$product->name = $request->input('name');
$product->description = $request->input('description');
$product->price = $request->input('price');
+ $product->category_id = $request->input('category_id');
$product->update();
return to_route('products.index');
}
ダミーデータの追加
Fakerのランダム値の日本語化。設定ファイルを編集
config/app.php
'faker_locale' => 'ja_JP',
- ファクトリ
コマンドsail artisan make:factory ProductFactory
database/factories/ProductFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends
\Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
*/
class ProductFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'name' => fake()->name(),
'description' => fake()->realText(50, 5),
'price' => fake()->numberBetween(100, 200000),
'category_id' => 1,
];
}
}
- シーダー
コマンドsail artisan make:seeder ProductSeederdatabase/seeders/ProductSeecer.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Product;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// ProductFactoryクラスで定義した内容にもとづいてダミーデータを20件生成
し、productsテーブルに追加する
Product::factory()->count(20)->create();
}
}
- ファクトリとシーダークラスの作成後にコマンド実行でダミーデータを追加
コマンドsail artisan db:seed --class=ProductSeeder
Laravel ECサイト step01
ひな型作り
- プロジェクトを作成
curl -s https://laravel.build/example-app | bash
- タイムゾーン
config/app.php
//======== 前略 ========
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
- 'timezone' => 'UTC',
+ 'timezone' => 'Asia/Tokyo',
//======== 後略 ========
データベースを作成
- phpMyAdminを追加
docker-compose.yml
phpmyadmin:
image: phpmyadmin/phpmyadmin
links:
- mysql:mysql
ports:
- 8080:80
environment:
#PMA_USER: "${DB_USERNAME}"
#PMA_PASSWORD: "${DB_PASSWORD}"
PMA_HOST: mysql
networks:
- sail
phpMyAdmin上で新規データベースを作成
設定ファイル
.env
- DB_DATABASE=laravel +DB_DATABASE=samuraimart
カテゴリのモデルとマイグレーションファイルを生成
sail artisan make:model Category -m
-mオプションはマイグレーションファイルも同時に作成するオプション
- マイグレーションファイルを編集
| カラム名 | 説明 | Laravel関数 | MySQLのデータ型 |
|---|---|---|---|
| name | カテゴリ名 | string | varchar(255) |
| description | カテゴリの説明文 | text | text |
| major_category_name | カテゴリの大分類 | string | varchar(255) |
database\migrations\XXXX_XX_XX_XXXXXX_create_categories_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
+ $table->string('name');
+ $table->text('description');
+ $table->string('major_category_name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
};
- マイグレーションを実行
sail artisan migrate
Tinkerでカテゴリーを追加
sail artisan tinkerTinkerを起動
Psy Shell v0.11.8 (PHP 8.1.6 — cli) by Justin Hileman >>>
例えば以下のような、Categoryモデルのデータを取得するコードを、コントローラーに実装する前に確認できます。
App\Models\Category::all()
[!] Aliasing 'Category' to 'App\Models\Category' for this Tinker session.
=> Illuminate\Database\Eloquent\Collection {#4284
all: [],
}
Tinkerを使ってカテゴリを作成
$category = new App\Models\Category() >> => App\Models\Category {#3566}
$category->major_category_name = "Food" >> => "Food"
$category->name = "meat" >> => "meat"
$category->description = "description for meat." >> => "description for meat."
$category->save() >> => true
データが作成されているかCategoryモデルのデータを取得するコードを実行
App\Models\Category::all()
=> Illuminate\Database\Eloquent\Collection {#4500
all: [
App\Models\Category {#4498
id: 1,
name: "meat",
description: "description for meat.",
major_category_name: "Food",
created_at: "2022-08-06 21:52:58",
updated_at: "2022-08-06 21:52:58",
},
],
}
- 終了
exit
シーダーでカテゴリを追加
php artisan make:seeder CategoriesTableSeeder >> INFO Seeder created successfully.
database\seeders\CategoriesTableSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
+use App\Models\Category;
class CategoriesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
- //
+ $major_category_names = [
+ '本', 'コンピュータ', 'ディスプレイ'
+ ];
+
+ $book_categories = [
+ 'ビジネス', '文学・評論', '人文・思想', 'スポーツ',
+ 'コンピュータ・IT', '資格・検定・就職', '絵本・児童書', '写真集',
+ 'ゲーム攻略本', '雑誌', 'アート・デザイン', 'ノンフィクション'
+ ];
+
+ $computer_categories = [
+ 'ノートPC', 'デスクトップPC', 'タブレット'
+ ];
+
+ $display_categories = [
+ '19~20インチ', 'デスクトップPC', 'タブレット'
+ ];
+
+ foreach ($major_category_names as $major_category_name) {
+ if ($major_category_name == '本') {
+ foreach ($book_categories as $book_category) {
+ Category::create([
+ 'name' => $book_category,
+ 'description' => $book_category,
+ 'major_category_name' => $major_category_name
+ ]);
+ }
+ }
+
+ if ($major_category_name == 'コンピュータ') {
+ foreach ($computer_categories as $computer_category) {
+ Category::create([
+ 'name' => $computer_category,
+ 'description' => $computer_category,
+ 'major_category_name' => $major_category_name
+ ]);
+ }
+ }
+
+ if ($major_category_name == 'ディスプレイ') {
+ foreach ($display_categories as $display_category) {
+ Category::create([
+ 'name' => $display_category,
+ 'description' => $display_category,
+ 'major_category_name' => $major_category_name
+ ]);
+ }
+ }
+ }
}
}
作成したシーダーファイルを読み込むためのコマンドを実行
sail composer dump-autoload
全テーブルを削除
php artisan migrate:fresh
シーダーでカテゴリーデータを作成
sail artisan db:seed --class=CategoriesTableSeeder
phpMyAdminでテーブルにデータが追加されているか確認する。
Laravel ミニブログ作成4 投稿のCRUD機能
投稿のコントローラーPostControllerを作成
sail artisan make:controller PostController
Read
投稿一覧(indexアクション) と 投稿詳細(showアクション)の登録
投稿一覧
- 現在ログイン中のユーザーに属するすべての投稿を取得し、作成日時が新しい順に並べ替える
「現在ログイン中のユーザー」を取得するには、Authファサードを利用
Auth::user() - 1で取得した投稿を変数$postsに代入し、ビューに渡す
Userモデルのインスタンス(ここではAuth::user())に対してposts()メソッドが使えるようになった理由は、ミニブログ作成3 投稿用テーブルとモデルでUserモデルとPostモデルのリレーションシップを設定したから。
投稿詳細
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
/app/Http/Controllers/PostController
// Postモデルのインスタンスを受け取る
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
このようにモデルのインスタンスを直接受け取ることでコードが減り、可読性が上がります。
ただしこの場合、ビュー内においてroute()ヘルパ関数の第2引数にモデルのインスタンスを渡す必要があるので注意しましょう(ビューは次節で作成します)。
<a href="{{ route('posts.show', $post) }}">詳細</a>
app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use App\Models\Post;
class PostController extends Controller
{
+ // 一覧ページ
+ public function index()
+ {
+ $posts = Auth::user()->posts()->orderBy('created_at', 'desc')->get();
+
+ return view('posts.index', compact('posts'));
+ }
+ // 詳細ページ
+ public function show(Post $post)
+ {
+ return view('posts.show', compact('post'));
+ }
}
投稿詳細
Laravel シーディングとファクトリ
シーディング
データベースにダミーデータを追加
シーダークラスを作成
sail artisan make:seeder シーダークラス名
database/seedersフォルダの中にシーダクラスが生成される
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}
クエリビルダで追加
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
DB::table('products')->insert([
'product_name' => 'ノート5冊セット',
'price' => 600,
'created_at' => '2023-06-01 00:00:00',
'updated_at' => '2023-06-01 00:00:00',
'vendor_code' => 1111
]);
}
}
Eloquent ORMで追加
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Product;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$product = new Product([
'product_name' => 'ノート5冊セット',
'price' => 600,
'vendor_code' => 1111
]);
$product->save();
}
}
シーダークラスを実行
--classオプションに指定したシーダークラスのみが実行
sail artisan db:seed --class=シーダークラス名
--classオプションをつけない場合、database/seedersフォルダの中にあらかじめ用意されているDatabaseSeederクラスが実行
sail artisan db:seed
複数のシーダークラスを一度に実行するにはDatabaseクラス内で他のシーダークラスを呼び出す
database/seeders/DatabaseSeeder
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
+ シーダー1Seeder::class,
+ シーダー2Seeder::class
]);
}
}
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}
ファクトリ
ダミーデータを生成
例えばproductsテーブル(Productモデル)用のファクトリであればProductFactory、vendorsテーブル(Vendorモデル)用のファクトリであればVendorFactoryです。このように命名することで、Laravel側はProductモデルとProductFactory、VendorモデルとVendorFactoryを自動的に紐づけてくれます。
- Fakerのランダム値の日本語化
config/app.php
'faker_locale'の値に'ja_JP'を指定。
//======== 前略 ========
/*
|--------------------------------------------------------------------------
| Faker Locale
|--------------------------------------------------------------------------
|
| This locale will be used by the Faker PHP library when generating fake
| data for your database seeds. For example, this will be used to get
| localized telephone numbers, street address information and more.
|
*/
'faker_locale' => 'ja_JP',
//======== 後略 ========
ファクトリを作成
sail artisan make:factory ファクトリ名
database/factoriesフォルダの中にファクトリが生成される。
definition()メソッドの中で以下のように連想配列を返すことで、ダミーデータを生成
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Vendor>
*/
class VendorFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'vendor_code' => fake()->randomNumber(5, true),
'vendor_name' => fake()->company()
];
}
}
fake()ヘルパ関数を使うことで、ファクトリからFakerというPHPライブラリにアクセスでき、ダミーデータ用のさまざまなランダム値を生成
Fakerの代表的なメソッド
| メソッド | 生成されるランダム値 | 例 | 引数 |
|---|---|---|---|
| name() | 姓名 | 木村 舞 | 'male'を指定した場合は男性の姓名、'female'を指定した場合は女性の姓名が生成される(オプション)。 |
| lastName() | 姓 | 工藤 | なし |
| firstName() | 名 | 裕太 | 'male'を指定した場合は男性名、'female'を指定した場合は女性名が生成される(オプション)。 |
| kanaName() | カタカナの姓名 | ササダ アケミ | 'male'を指定した場合は男性の姓名、'female'を指定した場合は女性の姓名が生成される(オプション)。 |
| lastKanaName() | カタカナの姓 | イシダ | なし |
| firstKanaName() | カタカナの名 | シュウヘイ | 'male'を指定した場合は男性名、'female'を指定した場合は女性名が生成される(オプション)。 |
| email() | メールアドレス(ドメイン部分もランダムなので、実在する可能性がある) | hideki.fujimoto@aoyama.org | なし |
| safeEmail() | 安全なメールアドレス(ドメイン部分が数種類の中から固定であり、実在しない) | akira61@example.com | なし |
| address() | 住所 | 8983502 長崎県伊藤市北区鈴木町宮沢1-10-8 ハイツ中村109号 | なし |
| postcode() | 郵便番号 | 2746333 | なし |
| city() | 市 | 田辺市 | なし |
| streetName() | 町 | 山岸町 | なし |
| streetAddress() | 町以降の住所 | 石田町西之園4-2-9 | なし |
| secondaryAddress() | 建物以降の住所 | ハイツ吉本101号 | なし |
| phoneNumber() | 電話番号 | 06-1841-5463 | なし |
| company() | 会社名 | 株式会社 山口 | なし |
| realText() | 文章 | ぼくには一昨日おとといたいへん元気な便たよりがあったんだ。レールを七つ組み合わせました。そのとき舟。 | 第1引数は最大文字数(最小値は10、初期値は200)。第2引数はインデックスサイズ(1〜5を指定、初期値は2)。インデックスサイズが大きいほど自然な文章になる。 |
| randomNumber() | 整数 | 8424749 | 第1引数は桁数(1〜9を指定、初期値はnull)。第2引数は桁数を固定するかどうか(trueまたはfalseを指定、初期値はfalse)。桁数を固定しない場合(第2引数がfalseの場合)、1桁から第1引数で指定した桁数までの整数が生成される。 |
| numberBetween() | 指定した範囲内の整数 | 1615557015 | 第1引数(初期値は0)から第2引数(初期値は2147483647)までの整数が生成される。 |
| dateTime() | 日時 | 1976-07-20 02:35:16 | 第1引数は日時の最大値(初期値は'now')。第2引数はタイムゾーン(初期値はシステムのデフォルトのタイムゾーン)。 |
| date() | 日付 | 1988-01-13 | 第1引数はフォーマット(初期値は'Y-m-d')、第2引数は日付の最大値(初期値は'now')。 |
| time() | 時間 | 15:28:38 | 第1引数はフォーマット(初期値は'H:i:s')、第2引数は時間の最大値(初期値は'now')。 |
| dateTimeBetween() | 指定した期間内の日時 | 2003-05-26 09:45:02 | 第1引数(初期値は'-30 years')から第2引数(初期値は'now')までの日時が生成される。第3引数はタイムゾーン(初期値はシステムのデフォルトのタイムゾーン)。 |
ファクトリでダミーデータの各カラムの値を定義したら、あとはシーダークラスで以下のようにモデル名::factory()->create();と記述すれば、ダミーデータをテーブルに追加できます。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Vendor;
class VendorSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void {
// VendorFactoryクラスで定義した内容にもとづいてダミーデータを生成し、vendorsテーブルに追加する
Vendor::factory()->create();
}
}
以下のようにcount()メソッドを使うことで、生成するダミーデータの数を指定することもできます。
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Vendor;
class VendorSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void {
// VendorFactoryクラスで定義した内容にもとづいてダミーデータを5つ生成し、vendorsテーブルに追加する
Vendor::factory()->count(5)->create();
}
}