僕なりの Laravel 備忘録

# [Blade](https://laravel.com/docs/11.x/blade)



## [Introduction](https://laravel.com/docs/11.x/blade#introduction)

```php
Route::get('/', function () {
    return view('greeting', ['name' => 'Finn']);
});
```





### [Additional Attributes](https://laravel.com/docs/11.x/blade#additional-attributes)

```php
<input type="checkbox"
        name="active"
        value="active"
        @checked(old('active', $user->active)) />
```

- 与えられた条件を評価して、`true`の場合に`checked`を出力する。

> ```php
> @checked((old('active') === "active") || old('active') === null)
> ```
>
> - radioボタンの場合はこんな感じで使えるよ。



```php
<select name="version">
    @foreach ($product->versions as $version)
        <option value="{{ $version }}" @selected(old('version') == $version)>
            {{ $version }}
        </option>
    @endforeach
</select>
```



ここから下はなんかHTMLディレクティブに直接関係あるやつ。

```php
<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
```



```php
<input type="email"
        name="email"
        value="email@laravel.com"
        @readonly($user->isNotAdmin()) />
```



```php
<input type="text"
        name="title"
        value="title"
        @required($user->isAdmin()) />
```



## [Displaying Data](https://laravel.com/docs/11.x/blade#displaying-data)

```php
Route::get('/', function () {
    return view('welcome', ['name' => 'Samantha']);
});
```

> ```php
> Route::get('/', function () {
>     return view('welcome', ['name' => 'Samantha']);
>     return view('admin.index', ['name' => 'Samantha']);
> });
> ```
>
> - ディレクトリを掘る時はこんな感じにする。



```php
Hello, {{ $name }}.
```

```php
The current UNIX timestamp is {{ time() }}.
```

- blade echo statement 内にPHP関数も使える。





#### [Displaying Unescaped Data](https://laravel.com/docs/11.x/blade#displaying-unescaped-data)

```php
Hello, {!! $name !!}.
```

- blade echo statement はデフォルトで`htmlspecialchars()`を実行している。XSS対策にね。
- エスケープ無しで出力したい場合に使う。





## [Forms](https://laravel.com/docs/11.x/blade#forms)



### [CSRF Field](https://laravel.com/docs/11.x/blade#csrf-field)

```php
<form method="POST" action="/profile">
    @csrf
 
    ...
</form>
```



### [Method Field](https://laravel.com/docs/11.x/blade#method-field)

```php
<form action="/foo/bar" method="POST">
    @method('PUT')
 
    ...
</form>
```

- HTMLフォームは`PUT`、`PATHC`、`DELETE`リクエストを作成できないので、Bladeで変わりにやって上げられる。



### [Validation Errors](https://laravel.com/docs/11.x/blade#validation-errors)

```php
<!-- /resources/views/post/create.blade.php -->
 
<label for="title">Post Title</label>
 
<input id="title"
    type="text"
    class="@error('title') is-invalid @enderror">
 
@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror
```

```php
<!-- /resources/views/auth.blade.php -->
 
<label for="email">Email address</label>
 
<input id="email"
    type="email"
    class="@error('email') is-invalid @else is-valid @enderror">
```

```php
<!-- /resources/views/auth.blade.php -->
 
<label for="email">Email address</label>
 
<input id="email"
    type="email"
    class="@error('email', 'login') is-invalid @enderror">
 
@error('email', 'login')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror
```





## print debug

```php
@dd() # こっちはexitも含まれている感じ
@dump() # こっちは出力してその後も続く
```







# [Collections](https://laravel.com/docs/11.x/collections)



## [Introduction](https://laravel.com/docs/11.x/collections#introduction)

```php
$collection = collect(['taylor', 'abigail', null])
    ->map(function (?string $name) {
    	return strtoupper($name);
	})
    ->reject(function (string $name) {
    	return empty($name);
	});
```

- immutable
- `collection()`メソッドは実行される度に、新しいインスタンスを作る



### [Creating Collections](https://laravel.com/docs/11.x/collections#creating-collections)

```php
$collection = collect([1, 2, 3]);
```

- `Eloquet`クエリの返値はいつも `Collection`になっているらしい。つまり、Model経由で取ってきたデータは全てCollectionになっている。



### [Extending Collections](https://laravel.com/docs/11.x/collections#extending-collections)

```php
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
 
Collection::macro('toUpper', function () {
    return $this->map(function (string $value) {
        return Str::upper($value);
    });
});
 
$collection = collect(['first', 'second']);
 
$upper = $collection->toUpper();
 
// ['FIRST', 'SECOND']
```

- `macro()`メソッドを使って、カスタムの関数を追加できる。上記は全て大文字にする関数を追加している例。
- 通常、Service Provider の `boot()`メソッドに追加する。
- Collection Macroと呼ばれている？



## [Available Methods](https://laravel.com/docs/11.x/collections#available-methods)





# Constants

> https://qiita.com/chin-zabro/items/11b4d92f5eacbaa1fbb6



# [Controller](https://laravel.com/docs/11.x/controllers)



```Bash
# 作成
$ php artisan make:controller ApiProductController

# モデルに紐づけながら作成
$ php artisan make:controller ApiProductController --model=Product
```

```bash
$ php artisan route:list
$ php artisan route:list -help
```

```php
namespace App\Http\Controllers;
 
use App\Models\User;
use Illuminate\View\View;
 
class UserController extends Controller
{
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}
```



## resource

> https://laravel.com/docs/11.x/controllers#resource-controllers
>
> https://laravel.com/docs/11.x/controllers#specifying-the-resource-model

一般的なCRUDに必要なメソッドを含ませながらコントローラーを作成する

```bash
$ php artisan make:controller PhotoController --resource
$ php artisan make:controller PhotoController --model=Photo --resource
```

```php
class PhotoController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index() {}

    /**
     * Show the form for creating a new resource.
     */
    public function create() {}

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request) {}

    /**
     * Display the specified resource.
     */
    public function show(News $news) {}

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(News $news) {}

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, News $news) {}

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(News $news) {}
}

```

```php
use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class);
Route::resources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);
```



## single controller

> https://qiita.com/harunbu/items/7fdb824b120cbb2b3d61



# Database

## driver

## SQLiteを使う

データ格納用のファイルを作成

```bash
$ touch database/database.sqlite
```

.envの更新

```diff
+ DB_CONNECTION=sqlite
- DB_CONNECTION=mysql
- DB_HOST=mysql
- DB_PORT=3306
- DB_DATABASE=laravel_breeze_multi_auth
- DB_USERNAME=sail
- DB_PASSWORD=password
```

マイグレート

```bash
$ php artisan migrate
```

テーブルの確認方法

> https://tableplus.com/



## tinker

```bash
$ php artisan tinker
$ sail tinker
```

```shell
$ App\Models\User::all()
```

```shell
$ DB::table('users')->get();
$ DB::table('users')->find(3);
$ DB::table('users')->where('name', 'John')->first();
$ DB::table('users')->where('name', 'John')->value('email');
$ DB::table('users')->pluck('title'); # 全ての行の title が collection で返ってくる
$ DB::table('users')->pluck('title', 'name'); # name がキーとなる形で collection が返ってくる
$ DB::table('users')->where('name', 'john')->exists();
$ DB::table('users')->where('name', 'john')->doesntExist();
```



> https://laravel.com/docs/11.x/artisan#tinker
>
> https://github.com/bobthecow/psysh
>
> https://laravel.com/docs/11.x/queries#running-database-queries



## mysql

```bash
sail mysql
sail shell
mysql -u sail -ppassword -h mysql
mysql -u root -ppassword -h mysql
```

dumpはrootでしかできないから注意。









# Environment

> https://laravel.com/docs/11.x/installation#initial-configuration

```
# .env

APP_NAME=Laravel
APP_TIMEZONE=Asia/Tokyo

APP_LOCALE=ja
APP_FALLBACK_LOCALE=ja
APP_FAKER_LOCALE=ja_JP
```

```php
# config/app.php
return [
    'timezone' => 'Asia/Tokyo',
    'locale' => 'ja',
    'fallback_locale' => 'ja',
    'faker_locale' => 'ja_JP',
],
```

```bash
# 日本語の翻訳ファイルを追加
$ composer require --dev laravel-lang/lang laravel-lang/publisher
$ php artisan lang:add ja

# インストール後はライブラリ不要なのでアンインストールする
$ composer remove --dev laravel-lang/lang laravel-lang/publisher
```





# Factory

> https://laravel.com/docs/11.x/eloquent-factories



```bash
$ php artisan make:factory PostFactory
```

```php
class FlightFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var class-string<\Illuminate\Database\Eloquent\Model>
     */
    protected $model = Flight::class;
}
```

```php
public function definition(): array
{
    return [
        'name' => fake()->name(),
        'email' => fake()->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => static::$password ??= Hash::make('password'),
        'remember_token' => Str::random(10),
    ];
}
```













# [Migration](https://laravel.com/docs/11.x/migrations)





## [Generating Migrations](https://laravel.com/docs/11.x/migrations#generating-migrations)

```bash
$ php artisan make:migration create_flights_table
```



```bash
$ php artisan make:migration alter_users_table --table=users
```

- テーブル編集の場合は`--table`を後ろにつける。
- [参考記事](https://zenn.dev/nenenemo/articles/10a3605c037ab6#--table-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)



```bash
$ php artisan make:migration create_users_table --create=users
```

- 新規作成の場合は`--create`を後ろにつける。
- [参考記事](https://zenn.dev/nenenemo/articles/10a3605c037ab6#--create-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)



マイグレーションファイルの命名について

- [参考記事](https://zenn.dev/nenenemo/articles/10a3605c037ab6#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%91%BD%E5%90%8D%E8%A6%8F%E5%89%87)

```bash
$ php artisan make:migration create_users_table --create=users
```

- 新規作成

```bash
$ php artisan make:migration add_name_column_to_users_table --table=users
$ php artisan make:migration add_columns_to_users_table --table=users
```

- カラム追加と複数カラム追加

```bash
$ php artisan make:migration remove_name_column_from_users_table --table=users
$ php artisan make:migration remove_columns_to_users_table --table=users
```

- カラム削除と複数カラム削除

```bash
$ php artisan make:migration modify_name_column_of_users_table --table=users
$ php artisan make:migration modify_name_columns_of_users_table --table=users
```

- カラム修正と複数カラム修正

```bash
$ php artisan make:migration update_users_table --table=users
```

- テーブル修正



## [Running Migrations](https://laravel.com/docs/11.x/migrations#running-migrations)

```bash
$ php artisan migrate

$ php artisan migrate:status

$ php artisan migrate --pretend
```



### [Rolling Back Migrations](https://laravel.com/docs/11.x/migrations#rolling-back-migrations)

```bash
# 最新を戻す
$ php artisan migrate:rollback

# 5個戻す
$ php artisan migrate:rollback --step=5

# バッチを3個戻す
$ php artisan migrate:rollback --batch=3

# dry-run
$ php artisan migrate:rollback --pretend

# すべてを
$ php artisan migrate:reset
```



#### [Roll Back and Migrate Using a Single Command](https://laravel.com/docs/11.x/migrations#roll-back-migrate-using-a-single-command)

```bash
$ php artisan migrate:refresh
 
# Refresh the database and run all database seeds...
$ php artisan migrate:refresh --seed
```

- 全てのmigrationをrollbackした後、migrateする。テーブルをdropする訳ではない。
- データを一旦綺麗にするというイメージで良い。

```bash
$ php artisan migrate:refresh --step=5
```

- これも、データを一旦綺麗にするというイメージ。
- 大量にmigrationがある状況で、全部rollbackするよりは早いよね？というのが利点だろうと思う。



#### [Drop All Tables and Migrate](https://laravel.com/docs/11.x/migrations#drop-all-tables-migrate)

```bash
$ php artisan migrate:fresh
 
$ php artisan migrate:fresh --seed
```

- 全テーブルをdropした後、migrateする。



## [Tables](https://laravel.com/docs/11.x/migrations#tables)

### [Creating Tables](https://laravel.com/docs/11.x/migrations#creating-tables)

```php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email');
    $table->timestamps();
});
```



### [Updating Tables](https://laravel.com/docs/11.x/migrations#updating-tables)

```php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});
```



### [Renaming / Dropping Tables](https://laravel.com/docs/11.x/migrations#renaming-and-dropping-tables)

```php
use Illuminate\Support\Facades\Schema;
 
Schema::rename($from, $to);
Schema::drop('users');
Schema::dropIfExists('users');
```



## [Columns](https://laravel.com/docs/11.x/migrations#columns)

### [Creating Columns](https://laravel.com/docs/11.x/migrations#creating-columns)

```php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});
```

```bash
php artisan make:migration add_image_column_of_ramens_table --table=ramens
```

```php
public function up(): void
{
    Schema::table('ramens', function (Blueprint $table) {
        $table->string('image');
    });
}

public function down(): void
{
    Schema::table('ramens', function (Blueprint $table) {
        $table->dropColumn('image');
    });
}
```



### [Available Column Types](https://laravel.com/docs/11.x/migrations#available-column-types)

|                                              |
| -------------------------------------------- |
| $table->id();                                |
| $table->boolean('confirmed');                |
| $table->timestamp('added_at', precision: 0); |
| $table->foreignId('user_id');                |
| $table->integer('votes');                    |
| $table->longText('description');             |
| $table->string('name', length: 100);         |
| $table->text('description');                 |
|                                              |





### [Column Modifiers](https://laravel.com/docs/11.x/migrations#column-modifiers)

```php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('users', function (Blueprint $table) {
    $table->string('email')->nullable();
});
```

- カラム型定義の後に続ける修飾子.
- 要は制約のこと。

|                           |
| :------------------------ |
| ->autoIncrement()         |
| ->nullable($value = true) |
| ->useCurrent()            |
| ->useCurrentOnUpdate()    |
| ->after('column')         |

- uniqueは違うよ！



#### [Column Order](https://laravel.com/docs/11.x/migrations#column-order)

```php
$table->after('password', function (Blueprint $table) {
    $table->string('address_line1');
    $table->string('address_line2');
    $table->string('city');
});
```

- MySQLを使っている時に利用できる。
- `after()`メソッドで、第一引数で指定したカラムの後に追加できる。



### [Modifying Columns](https://laravel.com/docs/11.x/migrations#modifying-columns)

```php
Schema::table('users', function (Blueprint $table) {
    $table->string('name', 50)->change();
});
```



```php
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();
});
```

- 全ての保持させたい定義を記述した上で、`change()`メソッドを使う。
- 含めていない定義は削除されるから注意。



```php
// Add an index...
$table->bigIncrements('id')->primary()->change();
 
// Drop an index...
$table->char('postal_code', 10)->unique(false)->change();
```

- indexは削除されない。
- indexの追加・削除・修正をする場合は、明示的に指定する必要がある。



### [Renaming Columns](https://laravel.com/docs/11.x/migrations#renaming-columns)

```php
Schema::table('users', function (Blueprint $table) {
    $table->renameColumn('from', 'to');
});
```



### [Dropping Columns](https://laravel.com/docs/11.x/migrations#dropping-columns)

```php
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('votes');
});
```



```php
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn(['votes', 'avatar', 'location']);
});
```



#### [Available Command Aliases](https://laravel.com/docs/11.x/migrations#available-command-aliases)

| Command                      |
| ---------------------------- |
| $table->dropRememberToken(); |
| $table->dropTimestamps();    |
|                              |



## [Indexes](https://laravel.com/docs/11.x/migrations#indexes)

### [Creating Indexes](https://laravel.com/docs/11.x/migrations#creating-indexes)

```php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('users', function (Blueprint $table) {
    $table->string('email')->unique();
});
```

- 1行で実行しちゃう。



```php
$table->unique('email');
```

- カラム定義したあとに、追加もできる。
- あまり使わないかも？



```php
$table->index(['account_id', 'created_at']);
```

- 複数カラムを同時にもできる。
- カラム定義して、その後まとめて、indexつけるって感じで、こちらの方が使いそう。



```php
$table->unique('email', 'unique_email');
```

- 第二引数で付けたいindex名を指定できる。
- 指定しない限りは、Laravelが勝手に良きにindex名を振ってくれる。



#### [Available Index Types](https://laravel.com/docs/11.x/migrations#available-index-types)

| **Command**              |
| ------------------------ |
| $table->primary('id');   |
| $table->unique('email'); |
| $table->index('state');  |
|                          |



> uniqueでnullableにしたい場合がある。
>
> - 空の場合はunique制約を効かせず、値が入った時には効かせるという。普通にチェーンすれば大丈夫。
>
>     ```php
>     $table->string('email')->nullable()->unique();
>     ```
>
> - 空文字列は`null`ではないので注意する。



### [Foreign Key Constraints](https://laravel.com/docs/11.x/migrations#foreign-key-constraints)





###### WIE

注意

- 外部キー制約の前に、他のカラム定義を書く。
- `up()`で作成し、 `down()`で削除する場合は、カラム削除の命令も書いておく必要がある。

```php
$table->foreign('user_id')->references('id')->on('users');

$table->foreignId('user_id')->constrained();

$table->foreignId('user_id')->constrained(
    table: 'users', indexName: 'posts_user_id'
);

$table->foreignId('user_id')
      ->constrained()
      ->onUpdate('cascade')
      ->onDelete('cascade');

$table->foreignId('user_id')
      ->constrained()
      ->onUpdate('cascade')
      ->onDelete('cascade');

$table->cascadeOnUpdate();
$table->restrictOnUpdate();
$table->noActionOnUpdate();
$table->cascadeOnDelete();
$table->restrictOnDelete();
$table->nullOnDelete();	
```

```php
$table->dropForeign('posts_user_id_foreign');

$table->dropForeign(['user_id']);
```





# [Model](https://laravel.com/docs/11.x/eloquent)



## show

```bash
$ php artisan model:show Flight
```



## Create model and migration simultaneously

```bash
$ php artisan make:model Product -m
```



## [Inserting and Updating Models](https://laravel.com/docs/11.x/eloquent#inserting-and-updating-models)

### [Inserts](https://laravel.com/docs/11.x/eloquent#inserts)

```php
<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Models\Flight;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class FlightController extends Controller
{
    /**
     * Store a new flight in the database.
     */
    public function store(Request $request): RedirectResponse
    {
        // Validate the request...
 
        $flight = new Flight;
 
        $flight->name = $request->name;
 
        $flight->save();
 
        return redirect('/flights');
    }
}
```

- Eloquentを使うおかげで、データベースからモデルを取ってくる必要がない。
- データベースにデータを登録するには、モデルのインスタンスを作成し、各attributeを設定するだけ。
- `save()`メソッドを呼んだタイミングで、`created_at`と`updated_at`にタイムスタンプがセットされるため、手動でセットする必要はない。



```php
use App\Models\Flight;
 
$flight = Flight::create([
    'name' => 'London to Paris',
]);
```

- よりシンプルに書けるね。
- 登録されたモデルのインスタンスが返値で返ってくる。
- `fillabel`プロパティか`guarede`プロパティを設定しておく必要がある。



### [Updates](https://laravel.com/docs/11.x/eloquent#updates)









### [Mass Assignment](https://laravel.com/docs/11.x/eloquent#mass-assignment)



```php
use App\Models\Flight;
 
$flight = Flight::create([
    'name' => 'London to Paris',
]);
```



```php
<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}
```

- `create()`メソッドを使う場合、上記のように、 `fillable`プロパティをモデルクラスに設定する必要がある。
- これは、Eloquent models が DB への mass assignment vulnerability から守るため。
- 基本的には、 mass assignment を使ってデータを格納していくので、設定してあげる。



#### [Allowing Mass Assignment](https://laravel.com/docs/11.x/eloquent#allowing-mass-assignment)

```php
/**
 * The attributes that aren't mass assignable.
 *
 * @var array
 */
protected $guarded = [];
```

- 格納されたくないカラムを配列に書く。
- 逆に全てのカラムを許可する場合は、`fillable`プロパティではなく、こちらで設定する方が簡単かも。





# [Request](https://laravel.com/docs/11.x/requests)







# Route

> https://laravel.com/docs/11.x/routing



## group

> https://laravel.com/docs/11.x/routing#route-groups



## prefix

> https://laravel.com/docs/11.x/routing#route-group-prefixes

```php
Route::prefix('admin')->name('admin.')->group(function () {
    Route::get('/users', function () {
        // Matches The "/admin/users" URL
        // Route assigned name "admin.users"...
    })->name('users');
});
```



## route model binding

> https://laravel.com/docs/11.x/routing#route-model-binding

URLのセグメント名と対応するModel名を一致させることで、Laravelが良きに解決してくれる。

```php
use App\Models\User;
 
Route::get('/users/{user}', function (User $user) {
    return $user->email;
});
```

```php
use App\Http\Controllers\UserController;
use App\Models\User;
 
// Route definition...
Route::get('/users/{user}', [UserController::class, 'show']);
 
// Controller method definition...
public function show(User $user)
{
    return view('user.profile', ['user' => $user]);
}
```





## Resourcesでまとめてnameを付ける

```php
Route::resource('/admin/ramen', RamenController::class)
    ->names('admin');
```



## Resourcesのパラメータを書き替えたい

※urlが深いとできないから注意。

```php
Route::resource('/ramen', RamenController::class)
    ->parameter('raman', 'ramen');
```



## Formでの基本的なルート設定

```php
Route::get('/', [AdmissionController::class, 'show'])
    ->name('show');
Route::post('/', [AdmissionController::class, 'post'])
    ->name('post');

Route::get('/confirm', [AdmissionController::class, 'confirm'])
    ->name('confirm');
Route::post('/confirm', [AdmissionController::class, 'send'])
    ->name('send');

Route::get('/thanks', [AdmissionController::class, 'complete'])
    ->name('complete');
```

- get 問い合わせフォームを表示
- post 問い合わせフォーム遷移先
- get 確認画面
- post 確認画面からフォーム遷移先
- get 完了画面



## viewでの書き方は複数ある

```php
<a href="{{ url('/form') }}">お問い合わせはこちら</a>
<a href="{{ route('form.show') }}">お問い合わせはこちら</a>
<a href="{{ action('SampleFormController@show') }}">お問い合わせはこちら</a>
```

■ url() 
長所：URLがどうなるかわかりやすい
短所：各機能のURLを知っておく必要がある

■ route()
長所：記述がシンプル。URLを書き換える時ルーティングの設定ファイルだけで良い
短所：URLがどうなるかわかりにくい

■ action()
長所：クラス名と関数名がわかるので、遷移先の処理が追いかけやすい
短所：クラス名が長いのテンプレートが読みづらくなる



## 新しいルートを追加したのにNotFoundになる

ルーティングの順番に問題がある場合が多い。

上から読み込まれるので、

```php
Route::get('/users/{user}', 'User@show');
Route::get('/users/hoge', 'User@hoge');
```

の順番にすると、hogeが動かない。











# Response

> https://laravel.com/docs/11.x/responses





## redirect

> https://laravel.com/docs/11.x/responses#redirects



## redirecting to named routes

```php
return redirect()->route('login');

// For a route with the following URI: /profile/{id}
return redirect()->route('profile', ['id' => 1]);

// For a route with the following URI: /profile/{id} ※
return redirect()->route('profile', [$user]);
```

If you would like to customize the value that is placed in the route parameter, you can specify the column in the route parameter definition (`/profile/{id:slug}`) or you can override the `getRouteKey` method on your Eloquent model:

```php
/**
 * Get the value of the model's route key.
 */
public function getRouteKey(): mixed
{
    return $this->slug;
}
```

> https://laravel.com/docs/11.x/responses#populating-parameters-via-eloquent-models



## redirecting with flashed session data

> https://laravel.com/docs/11.x/responses#redirecting-with-flashed-session-data

```php
Route::post('/user/profile', function () {
    // ...
 
    return redirect('dashboard')->with('status', 'Profile updated!');
});
```

```php
@if (session('status'))
    <div class="alert alert-success">
        {{ session('status') }}
    </div>
@endif
```











# Shell

シェルにアクセス

```bash
sail shell
```





# Seeding

> https://laravel.com/docs/11.x/seeding



## 基本

```bash
$ php artisan make:seeder UserSeeder
```

```php
# database/seeders/UsersTableSeeder.php

class UsersTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => 'john@doe.com',
            'password' => Hash::make('password')
        ]);
        
        News::factory()->count(10)->create();
    }
}
```

```bash
$ php artisan db:seed --class=UserSeeder
```



## Model factory

> https://laravel.com/docs/11.x/seeding#using-model-factories

まず、seedingを考える前に、[factory](# Factory) を作成しないといけない。

```php
public function run(): void
{
    User::factory()
            ->count(50)
            ->hasPosts(1)
            ->create();
}
```

`hasPosts(1)`メソッドは １つの関連するPostデータを持っている。



## Calling Additional Seeders

```php
# database/seeders/DatabaseSeeder.php

public function run(): void
{
    $this->call([
        UserSeeder::class,
        PostSeeder::class,
        CommentSeeder::class,
    ]);
}
```



## Running

> https://laravel.com/docs/11.x/seeding#running-seeders

```bash
$ php artisan db:seed
$ php artisan db:seed --class=UserSeeder
```



# Test

```bash
php artisan make:test FirstTest
php artisan make:test FirstTest --unit
```

```bash
php artisan test
```







# [Validation](https://laravel.com/docs/11.x/validation)





## [Validation Quickstart](https://laravel.com/docs/11.x/validation#validation-quickstart)



### [Writing the Validation Logic](https://laravel.com/docs/11.x/validation#quick-writing-the-validation-logic)

- validationが失敗すると、これは自動的に適切な処理である、`Illuminate\Validation\ValidationException`exceptionが投げられる。
- 挙動としては、元居た場所に戻される。
- 全てのバリエーションエラーとリクエストをflashデータとしてセッションに格納する。
- XHR requestの場合は、jsonが返される。

```php
/**
 * Store a new blog post.
 */
public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);
 
    // The blog post is valid...
 
    return redirect('/posts');
}
```



```php
$validatedData = $request->validate([
    'title' => ['required', 'unique:posts', 'max:255'],
    'body' => ['required'],
]);
```

- 配列で区切ることもできるよ



```php
$validatedData = $request->validateWithBag('post', [
    'title' => ['required', 'unique:posts', 'max:255'],
    'body' => ['required'],
]);
```

- 名前付きvalidation error bagでvalidation errorをグループ分けできる。



### [Displaying the Validation Errors](https://laravel.com/docs/11.x/validation#quick-displaying-the-validation-errors)

- `$errors`変数は、`Illuminate\View\Middleware\ShareErrorsFromSession`ミドルウェアによって、アプリケーションの全てのビューと共有される。従って、このミドルウェアを適用させれば、自由に使えるということですね。
- `$errors`変数は、`Illuminate\Support\MessageBag`のインスタンス。



```php
<!-- /resources/views/post/create.blade.php -->
 
<h1>Create Post</h1>
 
@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
 
<!-- Create Post Form -->
```



#### [The `@error` Directive](https://laravel.com/docs/11.x/validation#the-at-error-directive)

- bladeのディレクティブ。
- 個別の attribute の validation error message が存在するか確認する。
- このディレクティブ内で、`$message`変数を書くことで、個別のエラーメッセージを出せる。

```php
<!-- /resources/views/post/create.blade.php -->
 
<label for="title">Post Title</label>
 
<input id="title"
    type="text"
    name="title"
    class="@error('title') is-invalid @enderror">
 
@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror
```



```php
<input ... class="@error('title', 'post') is-invalid @enderror">
```

- named error bag を使っている時は、第二引数で指定してあげる。



### [Repopulating Forms](https://laravel.com/docs/11.x/validation#repopulating-forms)

- 検証エラーのためにリダイレクトレスポンスを生成すると、リクエストの入力値全てをセッションにフラッシュする。
- これは、次のリクエスト中に入力に簡単にアクセスしやすくするため。
- 従って、ユーザーが送信しようとしたフォームを再入力できるようになる。



```php
$title = $request->old('title');
```

- 以前のリクエストからフラッシュされた入力値を取得する。



```php
<input type="text" name="title" value="{{ old('title') }}">
```

- `old`ヘルパーが提供されているので、bladeではそれを使ってアクセスする。わざわざコントローラーで詰め込みみたいなのは、不要。
- 入力がない場合は、`null`が返されるので安心。



> ```php
> <input type="text" name="age" value="{{ old("name", "田中") }}">
> ```
>
> - フォームのeditの時に、初期値を設定したい時に使う。
> - `old()`ヘルパーの第二引数が初期値になる。
> - [参考記事](https://qiita.com/namizatork/items/f94180e38c63071e02d8)



３つある

- `Validator`ファサードを使う方法
- `Request`クラスの`validate()`メソッドを使う方法
- `FormRequest`クラスを使う方法



優先は`FormRequest`クラスを使う方法。Fat Controllerを避けるのがベストプラクティス

継承した子クラスを作って実装していく。

```bash
php artisan make:request [任意のクラス名]
```



`authorized`は認可。`rule`がルール。

```php
public function rules(): array
	{
    	return [
        	'name'  => 'required|max:20',
          	'tel'   => 'nullable|regex:/^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$/|regex:/^0\d{9,10}$/',
          	'body'  => 'required|max:1000',
            'email' => ['required', 'email:filter,dns', 'confirmed'],
            'email_confirmation' => 'メールアドレス確認',
        ];
    }
```

このバリデーションが通らなければ、**直前の画面に302リダイレクト**される



コントローラーに依存性の注入。

```php
use App\Http\Requests\InquiryRequest;

public  function post(InquiryRequest $request)
{
    // ここを通る時点で認可＆バリデーションが通っている

}
```



バリデーションの最初の失敗で停止させる

```php
/**
 * 最初のルールの失敗でバリデーションが停止する。
 * @var bool
 */
protected $stopOnFirstFailure = true;
```



リダイレクト先を変更する

通常、フォームをPostリクエストされた際、エラーが発生すると、リクエスト元のフォームがあったページへリダイレクトされる。

```php
/**
 * バリデーション失敗時に、/dashboardにリダイレクトさせる場合
 * @var string
 */
protected $redirect = '/dashboard';

/**
 * 名前付きルートで指定したい場合
*/
protected $redirectRoute = 'dashboard';
```



バリデーション前に処理を行う

```php
protected function prepareForValidation()
{
    // 入力データ取得　（例　０３－１１１１ー２２２２
    $tel = $this->input('tel');

    // ハイフンに似た文字を半角ハイフンに変換（この処理はちゃんとやるともっと複雑）
    $tel = str_replace(['－', '―', '‐', 'ー'], '-', $tel);

    // 半角数字を全角数字に変換：結果→03-1111-2222
    $tel = mb_convert_kana($tel, 'n');

    // 変換後のtelをリクエストデータにセット
    $this->merge(['tel' => $tel]);
}
```



追加バリデーションの実行（カスタムバリデーションのこと）

`withValidator()`メソッドが用意されています。このメソッド内で`$validator->after()` フックメソッドを定義することで、追加バリデーション機能を実現可能。

```php
use Illuminate\Validation\Validator;

public function withValidator(Validator $validator): void
{
    $validator->after(function (Validator $validator) {
        if ($this->input('email') === 'admin@example.com') {
            $validator->errors()->add('email', 'このメールアドレスは使用できません。');
        }
    });
}
```



バリデーション成功時に処理を行う

`passedValidation()` メソッドの中で行うことで、コントローラのアクションに到達する前にデータを加工することができる。

```php
protected function passedValidation()
{
    // 'price' の値を整数として扱いたい場合
    $this->merge([
        'price' => intval($this->price)
    ]);
}
```



バリデーション失敗時に処理を行う

`failedValidation()`メソッドをオーバーライドすることで、バリデーション失敗時に処理を行うことが可能.

ここでは15行目で独自のレスポンスを返していますが、そうしない場合は必ず、親クラスの同メソッドを呼ぶのを忘れない

```php
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

class CustomRequest extends FormRequest
{
    // ～略～
    protected function failedValidation(Validator $validator)
    {
        $response = [
            'status' => 422,
            'message' => 'Validation failed!',
            'errors' => $validator->errors()
        ];

        throw new HttpResponseException(response()->json($response, 422));
        //  parent::failedValidation($validator);
    }
```



## エラーメッセージのカスタマイズ

Laravelのインストール直後では、バリデーションエラーが英語で表示されます。そのため、日本語でのWebアプリケーションの場合、日本語化することが必須に。

- Laravelの言語ファイルを作成＆設定する

    > https://laranote.jp/laravel-language-file-validation-error-localization/

- `FormRequest`の子クラスで定義する

    ここで定義する日本語のエラーメッセージは、Laravelの言語ファイルよりも優先される。そのクラスでのみ使いたいエラー表示には適している。

    ```php
    public function messages()
    {
        return [
            'title.required' => 'タイトルは必須です。',
            'body.min' => '本文は最低10文字必要です。',
        ];
    }
    ```

    `messages()`メソッドにプレースホルダー（`:attribute`）を用意し、`attribute()`メソッドで置き換える方法もある。

    ```php
    public function messages()
    {
        return [
            'title.required' => ':attribute は必須です。',
            'body.min' => ':attribute は最低 :min 文字必要です。',
        ];
    }
    
    public function attributes()
    {
        return [
            'title' => 'タイトル',
            'body' => '本文',
        ];
    }
    ```

    > https://laranote.jp/laravel-formrequest-validation-error-localization/

## カスタムバリデーション

> https://enginiya.com/entry/how-to-make-custom-validate

## コントローラー内に記述（Fat Controller の原因になるため非推奨）

直接的でシンプル。

特定のアクションや特定のコントローラでのみ使用するような一時的なバリデーションルールに適しています。



## Form Requestクラスで記述

再利用性が高く、バリデーションルールと関連するロジックを1つのクラス内に集約できる。

コントローラがスリムに保たれ、テストも容易になります

```php
public function rules()
{
    return [
        'field' => [
            'required',
            function ($attribute, $value, $fail) {
                if (mb_strtoupper($value) !== $value) {
                    $fail('大文字である必要があります');
                }
            },
        ],
    ];
}
```



## Service Provider内で定義

アプリケーション全体で使用できるカスタムバリデーションルールを定義するのに適している。

再利用性が高い。

```php
public function boot()
{
    Validator::extend('uppercase', function ($attribute, $value, $parameters, $validator) {
        return mb_strtoupper($value) === $value;
    });
    Validator::replacer('uppercase', function ($message, $attribute, $rule, $parameters) {
        return $attribute.' must be uppercase.';
    });
}
```

```php
$request->validate([
    'field1' => 'required|uppercase',
    'field2' => 'required|uppercase',
]);
```



bootメソッドは、Laravelのサービスプロバイダ内で使用され、サービスコンテナのバインディングや、他の起動時の処理を実行するための場所として提供されている。

Validator::extend: extendメソッドを使って新しいバリデーションルールを定義。

uppercase: 新しいバリデーションルールの名前を付ける。

無名関数: バリデーションのロジックを定義するコールバック関数。値がすべて大文字である場合にtrueを返う。

Validator::replacer: replacerメソッドを用いて、特定のバリデーションルールに関連するエラーメッセージのフォーマットを定義します。



### ２つのフィールドに対して同時にバリデーションを掛ける

```php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Validator::extend('name_fields_required', function ($attribute, $value, $parameters, $validator) {
            $data = $validator->getData();
            $firstName = $data['first_name'] ?? '';
            $lastName = $data['last_name'] ?? '';

            // どちらも空、またはどちらかが空である場合、バリデーションエラー
            return !empty($firstName) && !empty($lastName);
        }, 'The first name and last name fields are required.');
    }
}
```

```php
// app/Http/Requests/YourFormRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class YourFormRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'first_name' => 'required_with:last_name|name_fields_required',
            'last_name' => 'required_with:first_name|name_fields_required',
        ];
    }
}
```





## 専用のバリデーションルールクラス（これ一番良さそう）

> https://qiita.com/koinunopochi/items/bd1a9d72f673885f9adf

```bash
$ php artisan make:rule Uppercase
```

```php
// Uppercase.php これ非推奨

public function passes($attribute, $value)
{
    return mb_strtoupper($value) === $value;
}
public function message()
{
    return ':attribute は大文字である必要があります';
}

// Upparcase.php 推奨 こっちが作られる

namespace App\Rules;
 
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
 
class Uppercase implements ValidationRule
{
    /**
     * Run the validation rule.
     */
     // $attribute 属性名、$value 値、$fail 失敗時のメッセージ
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // このコードは、値を大文字にした結果と一致するかどうかを検証している
        // 一致しない場合は、:attribute（属性名）が大文字であるというエラーメッセージをセットで返す。
        if (strtoupper($value) !== $value) {
            $fail('The :attribute must be uppercase.');
        }
    }
}
```

```php
// RequestやControllerでの使い方

$request->validate([
    'field' => ['required', new Uppercase],
]);
```

passesメソッドは、属性の値がルールを満たすかどうかを判定します。

この例では、値がすべて大文字であるかをチェックしています。

すべて大文字であればtrueを、そうでなければfalseを返します。

messageメソッドは、バリデーションルールが失敗したときに表示するエラーメッセージを返します。

:attributeプレースホルダは、バリデーションを実行する際の属性名に置き換えられます。



### ２つのフィールドに対して同時にバリデーションを掛ける

```bash
$ php artisan make:rule DualField
```

```php
# app/Rules/NamesRequired.php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class NamesRequired implements Rule
{
    private $otherValue;

    public function __construct($otherValue)
    {
        $this->otherValue = $otherValue;
    }

    public function passes($attribute, $value)
    {
        // どちらのフィールドも空ではないことを確認
        return !empty($value) && !empty($this->otherValue);
    }

    public function message()
    {
        return 'Both first name and last name are required.';
    }
}
```

```php
use App\Rules\NamesRequired;

public function rules()
{
    return [
        'first_name' => ['nullable', 'string', new NamesRequired(request('lastname'))],
        'last_name' => ['nullable', 'string', new NamesRequired(request('firstname'))],
    ];
}
```



## バリデーション失敗後のデータの復元



```php
<input type="text" name="title" id="title" value="{{ old('title', 'default value') }}">
```

```php
<select name="gender">
    <option value="man" {{ old('gender', 'man') == 'man' ? 'selected' : '' }}>Man </option>
    <option value="woman" {{ old('gender', 'man') == 'woman' ? 'selected' : '' }}>Woman</option>
</select>
```

```php
<input type="checkbox" name="subscribe" value="1" {{ old('subscribe', '1') == '1' ? 'checked' : '' }}>
```





## バリデーション成功後のデータの取得

## 全部のデータ

`FormRequest`の`validated`メソッドでバリデーション済みデータを取得可能。

`$validated`は配列で返される。

```php
 　 public function post(InquiryRequest $request)
    {
        // ここを通る時点で認可＆バリデーションが通っている

        // バリデーション済みデータの取得
        $validated = $request->validated();
        dd($validated);
```



## 一部のデータ

`$request->safe()`メソッドが返す`Illuminate\Support\ValidatedInput`オブジェクトが持つ`only()`や`except()`メソッドを使う。

```php
  public function post(InquiryRequest $request)
  {
    // ここを通る時点で認可＆バリデーションが通っている

    // バリデーション済みの'name'と'email’のみ取得
    $validated = $request->safe()->only(['name', 'email']);

    // バリデーション済みの'name'と'email’以外を取得
    $validated = $request->safe()->except(['name', 'email']);
```



## prepare For Validation()で加工したデータをold()で表示させたい

> https://laranote.jp/understanding-laravel-formrequest/#:~:text=%5D%3B%0A%7D-,prepareForValidation()%E3%81%A7%E5%8A%A0%E5%B7%A5%E3%81%97%E3%81%9F%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92old()%E3%81%A7%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B%E3%81%9F%E3%81%84,-%E4%BE%8B%E3%81%88%E3%81%B0%E3%80%81%E5%89%8D%E9%A0%85%E3%81%AE

## バリデーションエラーをview(Blade)で表示させる

> https://laranote.jp/laravel-blade-validation-error-display-methods/#toc1

```php
@if ($errors->any())
    <div class="error">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
```

```php
@if ($errors->has('email'))
    <div class="error">{{ $errors->first('email') }}</div>
@endif
    
// or
    
@error('name')
    <div class="error">{{ $message }}</div>
@enderror
```

$messageは、@errorディレクティブを使用した際使える変数。直前に利用した名称のエラーを$messageに自動的に入れてくれるため、これでエラーが表示できる。



```bash
php artisan make:component ErrorMessage
```

```php
<x-error-message for="name" />
```

```php
# resources/views/components/error-message.blade.php

@props(['for'])

@error($for)
    <span class="error">{{ $message }}</span>
@enderror
```

`@props`ディレクティブを使用して`for`という名前の属性を受け取る。この`for`属性は、バリデーションエラーをチェックするフォーム項目名を示している。



# [Views](https://laravel.com/docs/11.x/views)

## [Introduction](https://laravel.com/docs/11.x/views#introduction)

## [Creating and Rendering Views](https://laravel.com/docs/11.x/views#creating-and-rendering-views)

```php
php artisan make:view greeting
```

- `blade.php`拡張子を持つファイルを、`resources/view/`に作成してくれる。



```php
Route::get('/', function () {
    return view('greeting', ['name' => 'James']);
});
```

- `view()`ヘルパーでviewを作成。

```php
use Illuminate\Support\Facades\View;
 
return View::make('greeting', ['name' => 'James']);
```

- `View`ファサードでviewを作成。



### [Nested View Directories](https://laravel.com/docs/11.x/views#nested-view-directories)

```php
return view('admin.profile', $data);
```



## [Passing Data to Views](https://laravel.com/docs/11.x/views#passing-data-to-views)

```php
return view('greetings', ['name' => 'Victoria']);
```

- 普通に渡す。

```php
return view('greeting')
            ->with('name', 'Victoria')
            ->with('occupation', 'Astronaut');
```

- こんなんでも渡せる。
- いつ使うのかな、、、？



### [Sharing Data With All Views](https://laravel.com/docs/11.x/views#sharing-data-with-all-views)

```php
<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\View;
 
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // ...
    }
 
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        View::share('key', 'value');
    }
}
```

- 全てのViewに共有させたい場合に使う。
- `AppServiceProvider`クラスに実装してもいいし、切り出しても良い。







## [Optimizing Views](https://laravel.com/docs/11.x/views#optimizing-views)

Viewを返すリクエストが実行された場合に、必要に応じてViewはコンパイルされる。

- コンパイル済みViewがある場合はそれを返す。
- 無ければ再コンパイルして返す。
- 更新されている場合も再コンパイルして返す。

毎回上記のようにリクエストの度に再コンパイルが実行されるとパフォーマンスに悪影響が出る。そのため、開発時でもコンパイルしてからリクエストを実行できる

```bash
$ php artisan view:cache
```

- パフォーマンス向上のためにキャッシュさせることができる。

```bash
$ php artisan view:clear
```

- View Cacheをクリアする。





# 開発環境について

どこの環境でやっているかによってもろもろのコマンドが異なる。

## local

そのままコマンドを叩けば良い

## sail(docker)

それぞれのコマンドの前に `sail`をつけないといけない。

コンテナ内なのかローカルなのかの意識を持っておかないとここらへんで混乱してくる。コンテナ内での開発なら当然コンテナ内にインストールやらアップデートやらをしないといけないよね。sailコマンドはdockerをラップしているわけだから、sailコマンドを叩くということは、内部的にdockerコマンドを叩いていることになるよね。



## development server

バックエンド用の開発サーバーとフロントエンド用の開発サーバーがそれぞれある

基本的には連携して使うことが多いから、両方立ち上げることが多いが、どっちかだけという場合もある。

`sail up -d`や`php artisan serve`なんかがバックエンド用の開発サーバーの起動コマンド

`npm run dev`がフロントエンド用の開発サーバーの起動コマンド



## start project



## プロジェクトの作成

### sailで作成する

```bash
# 最新バージョンでプロジェクト作成
curl -s https://laravel.build/プロジェクト名 | bash
```

※ バージョンを指定したい場合はcomposerで作成する

### composerで作成する

```bash
# バージョンを指定してプロジェクト作成
composer create-project laravel/laravel プロジェクト名 --prefer-dist "10.*"
cd プロジェクト名
composer require laravel/sail --dev
php artisan sail:install
sail up -d
```

以下のようなエラーが出る時があるが、composerのバージョンを最新にしてあげると解決する場合が多い

```bash
The "https://repo.packagist.org/p2/carbonphp/carbon-doctrine-types~dev.json" file could not be downloaded: Failed to open stream: Network is unreachable
```



## 開発環境の起動

```bash
$ sail up -d
> .bashrcにエイリアスを貼ってある。なければ ./vendor/bin/sail up
```



## 開発環境の終了

```bash
$ sail stop
```



## DBへの接続

```bash
$ sail mysql -u sail -ppassword

$ sail mysql <dbName>
```



# APIを作ってみる

```bash
$ php artisan make:model Product -mfs --api

   INFO  Model [app/Models/Product.php] created successfully.

   INFO  Factory [database/factories/ProductFactory.php] created successfully.

   INFO  Migration [database/migrations/2024_01_11_010607_create_products_table.php] created successfully.

   INFO  Seeder [database/seeders/ProductSeeder.php] created successfully.

   INFO  Controller [app/Http/Controllers/ProductController.php] created successfully.
```

`-m`：マイグレーションファイル作成

`-f`：ファクトリ作成

`-s`：シーダー作成

`--api`：APIのCRUD用のコントローラー作成



## マイグレーションファイルの定義

```php
#database/migrations/2024_01_11_010607_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.
     */
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('text'); //追加
            $table->longText('description'); //追加
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

```



## モデルの定義

```php
#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;

    //以下4行追加
    protected $fillable = [
        'title',
        'description',
    ];
}

```



## ファクトリの定義

```php
# 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(): array
    {
        return [
            'title' => $this->faker->title(), //追加
            'description' => $this->faker->text(), //追加
        ];
    }
}

```



## シーダー定義

```php
#database/seeders/ProductSeeder.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.
     */
    public function run(): void
    {
        Product::factory()->count(3)->create(); //追加
    }
}

```

```php
#database/seeders/DatabaseSeeder.php

<?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
    {
        //以下3行追加
        $this->call([
            ProductSeeder::class,
        ]);
    }
}
```

マイグレートとシーディング

```bash
$ php artisan migrate --seed
```

tinkerで確認

```bash
$ sail tinker
> \App\Models\Product::all()
= Illuminate\Database\Eloquent\Collection {#5699
    all: [
      App\Models\Product {#5956
        id: 1,
        title: "Mr.",
        description: "Nulla sit facilis cumque et. Omnis non eligendi suscipit. Occaecati voluptatem quo porro occaecati. Qui sed est officiis ut nihil architecto.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
      App\Models\Product {#5957
        id: 2,
        title: "Dr.",
        description: "Explicabo omnis perferendis repudiandae repudiandae. Fuga pariatur saepe cupiditate dolor.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
      App\Models\Product {#5958
        id: 3,
        title: "Dr.",
        description: "Aut voluptate id officiis ea dolores. Cumque nihil totam maiores. Et odit placeat itaque ut est impedit. Nisi quo ab inventore nemo vel.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
    ],
  }

```



## データの取得

controllerの定義

```php
#app/Http/Controllers/ProductController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product; //追加

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //以下4行追加
        $products = Product::all();
        return response()->json([
            'products' => $products,
        ], 200);
    }

...省略...
```

routesの定義

```php
#routes/api.php

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController; //追加

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::apiResource('products', ProductController::class); //追加
```

postmanで確認



## データの登録

requestの定義

```bash
$ sail php artisan make:request StoreProductRequest
```

```php
# app/Http/Requests/StoreProductRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException; //追加

class StoreProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true; //修正
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'title' => ['required', 'string'], //追加
            'description' => ['required', 'string'], //追加
        ];
    }

    //以下追加
    protected function failedValidation(Validator $validator)
    {
        $res = response()->json([
            'message' => 'Failed create product',
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }
}
```



controllerの定義

```php
# app/Http/Controllers/ProductController.php

...省略...
    
use App\Http\Requests\StoreProductRequest; //追加

class ProductController extends Controller
{
    
...省略...
    
    public function store(StoreProductRequest $request) //修正
    {
        //以下5行追加
        $product = Product::create($request->all());
        return response()->json([
            'message' => 'Product created successfully!',
            'product' => $product,
        ], 200);
    }

...省略...
```



postmanで確認

- jsonオブジェクト無のボディでpostリクエストする

- jsonオブジェクト有のボディでpostリクエストする

```bash
$ sail php artisan
> \App\Models\Product::all()
= Illuminate\Database\Eloquent\Collection {#5960
    all: [
    
	...省略...

      App\Models\Product {#5965
        id: 4,
        title: "input title",
        description: "input description",
        created_at: "2024-01-11 02:51:08",
        updated_at: "2024-01-11 02:51:08",
      },
    ],
  }
```



## sanctum

laravelでapiを作成する時に使うもの





# Formを作ってみる

## 確認画面をどう作るか

## sessionを使う

大きな流れは

- フォームからの遷移先でセッションに入力値を保存
- 確認画面の表示はセッションの入力値を使う
- 確認画面からの遷移先もセッションの入力値を使う
- 送信処理（確認画面からの遷移先）で二重投稿にならないようにセッションの値を空にする

show→post(sessionに入力値を保存)→confirm（sessionから入力値を取り出す）→send（sessionから入力値を取り出して送信、sessionの入力値を削除）→complete



## DBを使う

- 入力データをデータベースに一時保存し、確認画面でそのデータを参照する。最終的に送信する際に、データベースから情報を取得して処理を行う。
- この方法はデータが大量である場合や、複数ステップにわたるフォーム処理に適している。



## inputの値を受け継いでいく

- こっちを基本的に使う
- sessionを上手く使っていく

## File Upload

## 基本のコード

```php
    public function store(Request $request)
    {
		...

        if ($request->file('image')) {
            $imageName = $request->file('image')->store('public/upload');
            $formData['image'] = $imageName;
        }

        Ramen::create($formData);

        return to_route('admin.ramen.index');
    }

    public function store(Request $request)
    {
		...

        // bladeの場合はこっちを使う
        if ($request->file('image')) {
            $imageName = $request->file('image')->store('public/upload');
            $formData['image'] = $imageName;
        }
        
        // inertiaの場合はこっちを使う
        if ($image = $request->file('image')) {
            $imageName = $image->getFilename(). '.'. $image->getClientOriginalExtension();
            $image->storeAs('public/upload', $imageName);
            $formData['image'] = $imageName;
        }

        Ramen::create($formData);

        return to_route('admin.ramen.index');
    }
```

## 公開

- Laravelでは画像の参照先は`storage/app/public/`
- ただし、そのままではビューで参照することがでない
- `storageディレクトリ`を参照するためにはシンボリックリンクが必要

```bash
php artisan storage:link
```

Inertia の場合は注意。

```bash
cd public
ln -s ../storage/public/upload
```



# Deployしてみる

> https://readouble.com/laravel/10.x/ja/deployment.html

オートローダーの最適化

```bash
composer install --optimize-autoloader --no-dev
```

設定のキャッシュ

```bash
php artisan config:cache
```

ルートのキャッシュ

```bash
php artisan route:cache
```

ビューのキャッシュ

```bash
php artisan view:cache
```

デバッグモード

```php
# .env or config/app.php
APP_DEBUG=false
```



## Conoha Wing

流れ

1. 本番環境とリポジトリをSSH接続可能に

2. publicディレクトリ内にルーティングさせる

    ```
    # /
    
    <IfModule mod_rewrite.c>
    	RewriteEngine On
        RewriteRule ^(.*)$ appName/public/$1 [L]
    </IfModule>
    ```

3. バックエンド

    1. composer

        ```bash
        cd app-name
        ~/bin/composer install
        ```

    2. application encryption key

        ```bash
        php artisan key:generate
        ```

    3. 環境変数

        ```bash
        cp .env.example .env
        ```

        ```diff
        -bash-4.2$ diff -uwBb .env.example .env
        --- .env.example        2024-05-21 09:23:34.117917782 +0900
        +++ .env        2024-05-21 09:37:47.499062046 +0900
        @@ -1,19 +1,19 @@
        -APP_NAME=Laravel
        -APP_ENV=local
        +APP_NAME=hoge
        +APP_ENV=production
         APP_KEY=
        -APP_DEBUG=true
        -APP_URL=http://localhost
        +APP_DEBUG=false
        +APP_URL=https://hoge
        +ASSET_URL=https://hoge
        
         LOG_CHANNEL=stack
         LOG_DEPRECATIONS_CHANNEL=null
        -LOG_LEVEL=debug
        +LOG_LEVEL=error
        
         DB_CONNECTION=mysql
        -DB_HOST=127.0.0.1
        +DB_HOST=hoge_host
         DB_PORT=3306
        -DB_DATABASE=app_name
        -DB_USERNAME=root
        -DB_PASSWORD=
        +DB_DATABASE=hoge_db
        +DB_USERNAME=hoge_user
        +DB_PASSWORD=hoge_pw
        
         BROADCAST_DRIVER=log
         CACHE_DRIVER=file
        ```

    4. *public/.htaccess*

        ```diff
        --- .old.htaccess       2024-05-22 21:14:32.710679534 +0900
        +++ .htaccess   2024-05-22 21:14:55.664738128 +0900
        @@ -1,8 +1,4 @@
         <IfModule mod_rewrite.c>
        -    <IfModule mod_negotiation.c>
        -        Options -MultiViews -Indexes
        -    </IfModule>
        -
             RewriteEngine On
        
             # Handle Authorization Header
        ```

    5. AppServiceProvider

        > https://laracasts.com/discuss/channels/vite/laravel-vite-assets-blockedmixed-content-issues-in-production-environment

        ```diff
        diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
        index 452e6b6..03e735c 100644
        --- a/app/Providers/AppServiceProvider.php
        +++ b/app/Providers/AppServiceProvider.php
        @@ -2,6 +2,7 @@
        
         namespace App\Providers;
        
        +use Illuminate\Support\Facades\URL;
         use Illuminate\Support\ServiceProvider;
        
         class AppServiceProvider extends ServiceProvider
        @@ -19,6 +20,8 @@ public function register(): void
              */
             public function boot(): void
             {
        -        //
        +        if($this->app->environment('production')) {
        +            URL::forceScheme('https');
        +        }
             }
         }
        ```

    6. dbのテーブル作成

        ```bash
        php artisan migrate
        ```

        

4. フロントエンド

    - ローカルでプロダクションビルドしてアップロード

        ```diff
            "scripts": {
                "dev": "vite",
                "build": "vite build",
        +       "build:prd": "vite build --mode production"
            },
        ```

        ```bash
        sail npm run build:prd
        ```

    - 本番環境でビルドする

        - nodeとnpmをインストールする

            > https://chigusa-web.com/blog/conoha-wing-laravel/

            - nodebrewのインストール

                ダウンロードする

                ```bash
                cd ~/bin
                wget git.io/nodebrew
                perl nodebrew setup
                ```

                パスを通す

                ```bash
                vim ~/.bash_profile
                > #以下に変更もしくは追記
                > PATH=$HOME/.nodebrew/current/bin:$PATH:$HOME/.local/bin:$HOME/bin
                
                source ~/.bash_profile
                nodebrew -v
                ```

            - node.jsをインストール

                ```bash
                nodebrew install-binary v16.20.0
                nodebrew ls-remote
                nodebrew list
                nodebrew use v16.20.0
                node -v
                npm -v
                ```

                v20.9.0だと必要パッケージが無くて怒られたので注意。

        - Nodeパッケージのインストールとビルド

            - Nodeパッケージのインストール

                ```bash
                cd app
                npm install
                ```

            - ビルド

                ```bash
                npm run buil
                ```