【速習】LaravelとPHPブートキャンプ Part1 - QuickNetwork開発部

このコンテンツはクイック・ネットワークでエンジニアとして開発のお仕事する人が、ベーシックであるPHPとフレームワークのLaravelを容易に学べるよう作成いたしました。外でも使ってもらえるように公開しております。学生インターンとか新卒とか中途で他の言語経験がある方とかを対象にしている感じです。

PHPに関してはいくつかのサイトを参考に、Laravelのブートキャンプは本家サイトから引用しております。  
[![](https://bootcamp.laravel.com/img/favicon/favicon-16x16.png)Laravel - Learn the PHP Framework for Web Artisans](https://bootcamp.laravel.com/introduction)

:::note warn
ちなみに本ドキュメントは[本家](https://bootcamp.laravel.com/introduction)を勝手に翻訳したものであり、意訳もかなり含まれております。
本家には翻訳公開して問題ないかの許諾をお願いしているところです。
その前提で読んでいただければと思います。
:::

Part2は[こちら](https://qiita.com/qn-takami/items/68a1d467e115f4974639)から。
Part3は[こちら](https://qiita.com/qn-takami/items/305fe6065ff824bb85d1)からご覧になれます。よろしければぜひご確認ください。

### 前提条件

-   何かしらの言語を使ってプログラムを書いたことがある
-   とにかくわからなければ調べながらコードを書いて動かして・・・ができること
-   PC環境はMac
    

### PHPの基礎

-   ファイルの拡張子は.php
-   文の最後には;（セミコロン）
-   PHP記載する箇所には<?php で始めて?>で終了（閉じ側はなくても動く）
-   変数は頭に＄をつけ、宣言してもしなくても使える
-   if, forブロックは｛｝で囲む
-   コメントは行単位では//か#を使い、複数行に渡る場合は/\*〜\*/を使う
    ```
    // コメントのテスト
    /* * Here comes * comment */
    ```
-   文字列の接続には.（ピリオド）を使う
    ```
    $str = "Hello"."World!";
    ```
-   複数行に渡る文字列にはヒアドキュメント
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/4e330dc7-11dd-9b4a-babc-1627796cd9a1.png">
    
-   ファイルの実行時には `php xxx.php`
    
#### PHPコードのイメージ

```
<?php
$deposit = 100; //投入金額を変数にセット
$juice_price = 120; //ジュースの値段を変数にセット

//ジュースの値段と投入金額を比較演算子を使って比較します。 
if($deposit >= $juice_price) {
    echo 'ジュースを出す'; 
}else{
    echo '投入金額が足りないです';
    exit();
    //処理を強制終了させる関数
}

//お釣りを計算してあれば出す
$change = $deposit - $juice_price;
//お釣りを計算
if($change > 0){
    echo $change ;//お釣りがあれば金額を出す
} 
?>
```

MacでPHPを動かすにはHomebrewでインストールするか、XAMPPもしくはVagrantでPHP環境を構築するかDockerで環境構築する必要があります。ちなみにクイック・ネットワークではDocker使っています。

### MVCフレームワーク
PHPの言語の特徴がわかったところで、Laravelについて学びます。LaravelはPHPのMVCフレームワークです。MVCフレームワークとはModelViewControllerに役割を分割した仕組みで開発効率を高める枠組み（フレームワーク）となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/321047a1-408b-c747-7283-452652f68e58.png)

Userからリクエスト（https://xxx/yyyyなど）を送られるとControllerが処理を行い、Modelがデータベースからデータを取得、 Controllerがそれを取得してViewで表示処理を行います。これを行うとコードの可読性も上がり、シンプルになるだけでなく、分業もやりやすくなります。Viewの部分を専門に仕事をするフロントエンドエンジニアやデザイナー・コーダーといった人もいます。LaravelはPHPをベースにしたMVCフレームワークとなります。

本ドキュメントはLaravelとPHPを効率よく学ぶために、Bootstrapというというカリキュラムを通してミニブログを作成していきます。

ということでLaravel本家サイトにあるBootcampに沿って学習していきましょう！

[https://bootcamp.laravel.com/blade](https://bootcamp.laravel.com/blade "https://bootcamp.laravel.com/blade")

### Laravelインストール

### Quick Installation

もしすでにPHPやComposerをインストールしていれば、新しいLaravelのプロジェクトをComposerで作ることができます。

```
$ composer create-project laravel/laravel chirper
```

:::note warn
MacへのPHPのインストールはHomebrewを使いますが、Intel版かM1, 2で異なります。[こちら](https://qiita.com/HRI-DAWN/items/b735e636056e5cbe6bfc "https://qiita.com/HRI-DAWN/items/b735e636056e5cbe6bfc")もご参照ください。またComposerのインストールもHomebrewで行います。合わせて[こちら](https://god48.com/composer-install# "https://god48.com/composer-install#")もご参照ください。
:::

話を簡単にするために、Composerのcreate-projectコマンドは、アプリケーションのデータを保存する新しいSQLiteデータベースをdatabase/database.sqliteに自動的に作成します。ローカルに簡易データベース（SQLite）を作ればMySQLやPostgresといったDBMSを個別にインストールする必要がないからです。プロジェクトが作成されたら、Laravel Artisanのserveコマンドを使ってLaravelのローカル開発サーバーを起動します。

```
$ cd chirper $ php artisan serve
```

Artisan developper serverを立ち上げると [<u data-renderer-mark="true">http://localhost:8000</u>](http://localhost:8000/ "http://localhost:8000/") でアクセス可能になります。

### （参考）Docker経由でLaravel

※上記のComposerとか入ってないケースでこちらを選択  
ローカルにPHPがインストールされていない場合は、LaravelのデフォルトのDocker開発環境と対話するための軽量コマンドラインインターフェイスであるLaravel Sailを使用してアプリケーションを開発することができます。始める前に、自分のPCにDocker Desctopをインストールしてください。

Laravelをインストールする最も簡単な方法は、laravel.buildサービスを使用することです。ターミナルを起動し、以下のコマンドを実行します

```
＄ curl -s "https://laravel.build/chirper" | bash
```

Sailのインストールは、Sailのアプリケーションコンテナがローカルマシン上に構築される間、数分かかることがあります。

デフォルトでは、インストーラはLaravel Sailに、SQLiteの代わりにMySQLを使用したい場合のMySQLデータベースサーバーを含む、アプリケーションに役立つ多くのサービスを事前に設定します。必要に応じてSailサービスをカスタマイズすることもできます。

プロジェクトが作成されたら、アプリケーションディレクトリに移動し、Laravel Sailを起動します。

```
$ cd chirper $ ./vendor/bin/sail up
```

Sailを使用してアプリケーションを開発する場合、Artisan、NPM、Composerのコマンドを直接呼び出すのではなく、Sail CLI経由で実行することができます。

```
$ ./vendor/bin/sail php --version 
$ ./vendor/bin/sail artisan --version 
$ ./vendor/bin/sail composer --version 
$ ./vendor/bin/sail npm --version
```

アプリケーションのDockerコンテナが起動したら、ブラウザで[http://localhost](http://localhost/ "http://localhost") でアクセスすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/2d095a2e-007f-40df-c29a-afe8554e5f4f.png)

### Laravel Breeze

次に、ログイン、登録、パスワードリセット、メール認証、パスワード確認など、Laravelのすべての認証機能を最小かつシンプルに実装したLaravel Breezeをインストールして、アプリケーションをスタートさせます。インストール後は、様々なニーズに合わせたコンポーネントをカスタマイズするようにしてください。

Laravel Breezeでは、BladeテンプレートやVue、React with Inertiaなど、ビューレイヤーにいくつかのオプションを用意しています。ここではLaravelの標準テンプレートエンジンであるBladeを使用します。

chirperプロジェクトのディレクトリで新しいターミナルを開き、指定されたコマンドで選択したスタックをインストールします。

```
$ composer require laravel/breeze --dev $ php artisan breeze:install blade
```

Breezeがフロントエンドの依存関係をインストールおよび設定してくれるので、Vite開発サーバーを起動すると、Bladeテンプレートに変更を加えたときに、CSSを自動的に再コンパイルし、ブラウザを自動的に更新してくれます。

ブラウザで先ほど立ち上げたLaravelアプリケーション（ `php artisan serve` で立ち上げたアプリケーション）を更新すると、右上に「Register(登録）」リンクが表示されるはずです。するとLaravel Breezeが提供する登録フォームが見られるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/637ef9b7-e3f1-47b9-50e2-5a1228ac4cc4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/0159d638-5fe9-74fb-927d-e4089f50c899.png)

ご自身のアカウントを登録してログインしてみてください。

### Chirpsを作成

ユーザがChirpsを投稿できるようにするには、モデル、マイグレーション、コントローラを作成する必要があります。それぞれの概念をもう少し掘り下げてみましょう：

モデル（Model）は、データベースのテーブルを操作するための強力で楽しいインターフェイスを提供します。  
マイグレーション（Migration）は、データベースのテーブルを簡単に作成したり変更したりできるようにします。マイグレーションによって、アプリケーションを実行するすべての場所で同じデータベース構造が存在することを保証します。  
コントローラ（Controller）は、アプリケーションへのリクエストを処理してレスポンスを返す役割を担います。  
何か開発するようなケースのほとんどの場合、これらは一緒に動作します。そのため、artisan make:modelコマンドはこれらを一度にまとめて作成することができます。

次のコマンドでChirps用のモデル、マイグレーション、リソースコントローラを作成してみましょう。

```
$ php artisan make:model -mrc Chirp
```

このコマンドは以下の3つのファイルを作成します。

-   app/Models/Chirp.php - Eloquent （データベースを直感的に操作できるORMツール）モデル
    
-   database/migrations/<timestamp>\_create\_chirps\_table.php - データベースのテーブルを作成するマイグレーションファイル
    
-   app/Http/Controller/ChirpController.php - 入ってくるリクエストの処理を行ってレスポンスを返すHTTPコントローラー
    

### ルーティング（Routing）

ブラウザなどからくるリクエストに対してどのようにアプリケーションが反応するかを定義することをルーティング（Routing）といいます。ブラウザに `https://xxx.com/show/1` のように打ち込むとGETで　 `xxx.com` のWEBサーバーに対して `/show/1` というリクエストを送ったりしてきます。このリクエストに対応するコントローラを指定する必要があります。プロジェクトのroutesディレクトリで管理される "routes "を追加することでこれを行うことができます。リソースコントローラという仕組みを使用しているので、1つのRoute::resource()ステートメントを使用して、従来のURL構造に従ってすべてのルートを定義できます。すなわちGET /chirps とすれば一覧で表示されたり、POSTで新規追加されたりなど決まったルールにはなりますがRoute::resourceと書くだけで全てのアクションが定義されるのです。

まず始めに、2つのルートを有効にします：

indexルートはフォームとChirpsのリストを表示します。  
storeルートは新しいChirpsを保存するために使用されます。  
また、これらのルートを2つのミドルウェアの後ろに配置します：

ミドルウェア（middleware）はルーティング時に補助的に動作する機能を定義する機構です。

authミドルウェアは、ログインしたユーザだけがルートにアクセスできるようにします。  
verifiedミドルウェアは、メール認証を有効にする場合に使用します。

```routes/web.php
<?php
+ use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

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');
});

+ Route::resource('chirps', ChirpController::class)
+     ->only(['index', 'store'])
+     ->middleware(['auth', 'verified']);
require __DIR__.'/auth.php';
```

この記述で以下の2つのルートが生成されます。ResourceはデータベースにおけるCRUD（Create/Read/Update/Delete）をまとめて作成する機能ですが使う機能だけピックアップして `only`で限定することができます。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/5636827e-22e8-878c-6647-fbd24c6c42d9.png">

上記のルートは `php artisan route:list` コマンドで確認することが可能です。

それでは上記のルーティングとコントローラーがうまく行っているか、テストメッセージを返すことでテストしてみましょう。

```app/Http/Controllers/ChirpController.php
<?php
 ...
+ use Illuminate\Http\Response; 
class ChirpController extends Controller
{
    /**
     * Display a listing of the resource.
     */
-    public function index()
+    public function index(): Response 
    {
-        //
+        return response('Hello, World!');
    }
 ...
}
```

もしあなたがまださっきからログインしてる状態なら `http://localhost:8000/chirps` で以下のメッセージを確認できます。ちなみにSailを使っているなら `http://localhost/chirps` です。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/aff62485-ce0c-9985-2a2d-e5a1c159468f.png">

### Blade

大した事ないなとお思いでしょうか？ではChirpControllerのindexを編集してみましょう。

```app/Http/Controllers/ChirpController.php
<?php
 ...
+ use Illuminate\View\View;
class ChirpController extends Controller
{
    /**
     * Display a listing of the resource.
     */
-    public function index(): Response
+    public function index(): View
    {
-        return response('Hello, World!');
+        return view('chirps.index');
    }
 ...
}
```

そしたら新しくBladeのビューを作成してみましょう。

```html:resources/views/chirps/index.blade.php
<x-app-layout>
    <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
        <form method="POST" action="{{ route('chirps.store') }}">
            @csrf
            <textarea
                name="message"
                placeholder="{{ __('What\'s on your mind?') }}"
                class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
            >{{ old('message') }}</textarea>
            <x-input-error :messages="$errors->get('message')" class="mt-2" />
            <x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
        </form>
    </div>
</x-app-layout>
```

これだけです。そしたらブラウザをリフレッシュしてBleezeで提供された新しく作ったフォームを確認してみましょう。 `php artisan serve` で起動し、 `http://localhost:8000/chirps` を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/15beae87-14bc-6ce7-d50c-c0bbe9ba36b2.png)

スクリーンショットが上記のように見えない場合は、先ほど作成した新しいファイルのCSSクラスを検出するために、Tailwind用のVite開発サーバーを停止したり起動したりする必要があるかもしれません。

これ以降、Bladeテンプレートに加えた変更は、 `npm run dev` でVite開発サーバーが実行されている時はいつでも、ブラウザで自動的に更新されます。

### Navigationメニュー

ここでナビゲーションにリンクを追加してみましょう。

Bladeのテンプレートにnavigationがあるので、以下のようにメニューを追加します。

```html:resources/views/layouts/navigation.blade.php
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('Dashboard') }}
    </x-nav-link>
+    <x-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')">
+        {{ __('Chirps') }}
+    </x-nav-link>
</div>
```

モバイルも追加します。

```html:resources/views/layouts/navigation.blade.php
<div class="pt-2 pb-3 space-y-1">
    <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('Dashboard') }}
    </x-responsive-nav-link>
+    <x-responsive-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')">
+        {{ __('Chirps') }}
+    </x-responsive-nav-link>
</div>
```

### Chirpの保存

フォームは、先ほど作成したchirps.storeルートにメッセージを投稿するように設定されています。ChirpControllerクラスのstoreメソッドを更新してデータを検証し、新しいChirpを作成しましょう。

```app/Http/Controllers/ChirpController.php
<?php
 ...
+ use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
class ChirpController extends Controller
{
 ...
    /**
     * Store a newly created resource in storage.
     */
-    public function store(Request $request)
+    public function store(Request $request): RedirectResponse
+    {
-        //
+        $validated = $request->validate([
+            'message' => 'required|string|max:255',
+        ]);
+
+        $request->user()->chirps()->create($validated);
+ 
+        return redirect(route('chirps.index'));
    }
 ...
}
```

Laravelの強力なバリデーション機能を使って、ユーザーがメッセージを必須入力で提供し、作成するデータベースカラムの255文字制限を超えないようにしています。

そして、chirpsリレーションシップを利用して、ログインしたユーザーに属するレコードを作成します。このリレーションシップはこのあと定義します。リレーションシップ（Relationship）とは複数のモデルの関係性を定義することを指します。1対1（HasOne）か1対他（HasMany）といった定義になります。

最後に、ユーザーをchirps.indexルートに戻すためにリダイレクトを行っています。

### リレーションの作成

前のステップで、$request->user()オブジェクトのchirpsメソッドを呼び出したことに気づいたかもしれません。このメソッドをUserモデルに作成して、"has many "リレーションを定義する必要があります

```php:app/Models/User.php
<?php
 ...
+ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
 ...
+    public function chirps(): HasMany
+    {
+        return $this->hasMany(Chirp::class);
+    }
}
```

### マス・アサインメント脆弱性対策（Mass Assignment Protection）

リクエストからモデルにすべてのデータを渡すことは危険です。ユーザーがプロフィールを編集できるページがあるとします。リクエスト全体をモデルに渡すと、ユーザーはis\_adminカラムのような好きなカラムを編集できます。これはマス・アサイメント（大量代入）の脆弱性と呼ばれます。

Laravelはデフォルトで大量代入をブロックすることで、誤ってこのようなことをしないように保護しています。大量代入は、各属性を1つずつ代入する必要がなくなるので、とても便利ですが。安全な属性を "fillable "としてマークすることで、一括代入を有効にすることができます。

Chirpモデルに$fillableプロパティを追加して、message属性の一括割り当てを有効にしてみましょう

```app/Models/Chirp.php
<?php
 ...
class Chirp extends Model
{
 ...
+    protected $fillable = [
+        'message',
+    ];
}
```

より詳細にマス・アサイメント脆弱性について知りたい場合は[こちら](https://laravel.com/docs/11.x/eloquent#mass-assignment "https://laravel.com/docs/11.x/eloquent#mass-assignment")（英語）を参照ください。

### Migrationの更新

マイグレーション（Migration）とはデータベースの構造の変更をファイル（マイグレーションファイル）を使って行うことです。1ファイルごとにテーブルを作成したり、カラムの追加、削除、属性の変更などを行うように記載します。

アプリケーションの作成中、Laravelはすでにdatabase/migrationsディレクトリに含まれるデフォルトのマイグレーションを適用しています。現在のデータベース構造は、 `php artisan db:show` および `php artisan db:table` コマンドで確認できます。

```
$ php artisan db:show
$ php artisan db:table users
```

つまり、唯一足りないのは、ChirpとそのUser、そしてメッセージ自体のリレーションシップを保存するためのデータベースの追加カラムだけです。先ほど作成したデータベースのマイグレーションを覚えていますか？そのファイルを開いて、追加のカラムを記載しましょう。

```databases/migrations/<timestamp>_create_chirps_table.php
<?php
 ...
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('chirps', function (Blueprint $table) {
            $table->id();
+            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+            $table->string('message');
            $table->timestamps();
        });
    }
 ...
};
```

ここまでまだデータベースのマイグレーションを実行していなかったので実行します。
```
$ php artisan migrate
```

### 試してみる

作成したフォームを使ってChirpを送信する準備ができました！まだ既存のChirpを表示していないので、結果を見ることはできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/32aedfd9-2adb-7df8-023c-a267a4204615.png)

空のままボタンを押すか、255文字を超えるメッセージを書いてボタンを押すとバリデーションを試すことができます。