開発環境構築→ Laravel10 Sail Vite React TypeScript

2023年の時点における、LaravelでReactとTypeScriptの開発環境は、Laravel/Sail による環境がふさわしいようです。 (参考:Laravel/Sailとは)
フロントエンドはgulpやwebpackを用いられていましが、それらは時代遅れになりVite.jsを使うようになりました。 今回、ReactとTypeScriptの開発環境にVite.jsを導入します。

開発環境構築の手順

  1. 事前にDocker環境を構築する必要があります。
    Docker desktopは、すぐ動作不能になるので下記の方法が採用します。
    Docker desktopを使わずWSL上で動作するUbuntuでDockerとdocker-composeを動かす
    Ubuntuは以降の手順で必要になります。
  2. Ubuntuアプリを管理者権限として実行"します。以下のコマンドはUbuntuで実行します。
  3. Dockerサービスを起動します。
    sudo service docker start
  4. cdコマンドでプロジェクトをインストールしているディレクトリへ移動します。
    cd /mnt/c/Users/user/git/react_demo2
  5. Laravelをインストールします。「dev」はプロジェクト名なので任意の名前を付けられます。
    curl -s "https://laravel.build/dev" | bash
    時間がかかります。「dev」になっている箇所は任意です。
  6. Laravelが配置されているディレクトリへ移動します。
    cd dev
  7. Laravel/Sailを起動します。
    ./vendor/bin/sail up -d
    これはdocker-compose.ymlを実行することを意味しています。複数のdockerコンテナが立ち上がります。
    また、sailを通してphp, npm, composer コマンドを実行できるようになります。
  8. ブラウザに以下のURLを入力するとLaravelのwelcomeページが表示されます。
    http://localhost
  9. .envファイルを開き、セッションをDBに保存するようにSESSION_DRIVERの項目を書き換える。(ファイルにセッションのデータを保存するならこの処理は不要)
    			SESSION_DRIVER=database
    			
    ※.envファイルの場所はLaravelをインストールしたディレクトリである、プロジェクトのディレクトリの直下に存在する。
    また、DB_USERNAMEなどデータベースの設定も特に必要にない。
  10. マイグレーションを実行する
    ./vendor/bin/sail artisan migrate
    マイグレーションを実行するとデータベースにテーブル群が作成されれる。ちなみにテーブル名はプロジェクト名と同じである。今回の例でいえば「dev」になる。
  11. まずはnpm をアップデートします。
    ./vendor/bin/sail npm update
    これをしておかないと、バージョン違いによるエラーが多発します。
  12. laravel/uiをダウンロードします。
    ./vendor/bin/sail composer require laravel/ui
  13. laravel/uiをインストールします。
    ./vendor/bin/sail artisan ui bootstrap --auth
    ログイン機能や、bootstrapなどが作成されます。 bootstrapを指定することによりbootstrapによるデザインになるようです。
  14. reactとreact-domをインストールします。
    ./vendor/bin/sail npm install -D react react-dom @types/react @types/react-dom
  15. vite用のreactのプラグインをインストールします。
    ./vendor/bin/sail npm install -D @vitejs/plugin-react
  16. TypeScriptをインストールします。
    ./vendor/bin/sail npm install -D typescript
  17. TypeScriptのコンパイラを設定を初期化および生成します。
    ./vendor/bin/sail npx tsc --init --jsx react-jsx
    tsconfig.json という設定ファイルが生成されます。 この設定ファイルを使用して、TypeScriptのコンパイルオプションや挙動をカスタマイズできます。
  18. 一旦ビルドします。
    ./vendor/bin/sail npm run build
  19. vite.config.js拡張子を変更してvite.config.tsにします。
  20. vite.config.tsを開き、以下のように書き換えます。
    
    import { defineConfig } from 'vite';
    import laravel from 'laravel-vite-plugin';
    import react from '@vitejs/plugin-react'; 
    
    export default defineConfig({
        plugins: [
            laravel({
                input: ['resources/sass/app.scss', 'resources/ts/index.tsx'],
                refresh: true,
            }),
            react(), 
        ],
        server: {
            hmr: {
                host: 'localhost', 
            },
        },
    });
    			
  21. 「resources/ts/index.tsx」ファイルを新規作成します。
    その際、tsディレクトリは手動で作成してください。
  22. resources/ts/index.tsx にReactとTypeScriptで最低限なサンプルコードを記述します。
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    const App = () => {
      return (
    	<div>Hello World! 今日の天気はいかがですか?</div>
      );
    }
    
    ReactDOM.render(<App />, document.getElementById('react_app'));
    			
  23. ビルド(コンパイル)します。
    ./vendor/bin/sail npm run build
    tsxファイルを修正した場合、その都度、上記コマンドでビルドします。
  24. resources/views/welcome.blade.php にReactの処理を埋め込んでみます。
    
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
    
            <title>Laravel</title>
            @viteReactRefresh
              @vite(['resources/sass/app.scss', 'resources/ts/index.tsx'])
            ~略~
            
                <body class="antialiased">
        	<div id="react_app"></div><!-- Reactの埋め込み部分 -->
    			
  25. 「http://localhost」にアクセスし、サンプルコードの出力が表示ができれば成功せす。
  26. 以上でとりあえず環境構築は終わりです。
  27. 一旦、Dockerコンテナを止める場合は以下のコマンドを実行します。
    ./vendor/bin/sail stop
    再スタートするときは以下の通りです。
    ./vendor/bin/sail up -d
    Dockerサービスまで止める場合は以下の通りです。
    sudo service docker stop
  28. DBにアクセスする場合は、以下のコマンドでアクセスできます。
    ./vendor/bin/sail mysql

Laravel/Sailとは

従来docker-compose.ymlは自前で作成してきました。Laravel/sailはLaravelが用意する「docker-compose.yml」という考え方です。
Sailのdocker-compose.ymlには、PHP、MySQL、Redis、Seleniumなど、Laravelアプリケーションの開発に通常必要なサービスが包括的に含まれています。
プロジェクトの要件に合わせてLaravel/sailのdocker-compose.ymlをカスタマイズすることもできます。

phpコマンド、composerコマンド、nmpコマンドはsailを通して実行します。 以下のような感じです。
	./vendor/bin/sail npm npmのコマンド
	./vendor/bin/sail composer composerのコマンド
	./vendor/bin/sail php PHPのコマンド
	
Dockerの停止、開始も「./vendor/bin/sail」コマンドで行います。

Laravel/Sailはdocker上で動作します。
Windowsならdocker環境はdocker desktopが有名ですが不具合も多いので、UbuntuアプリをMicrosoftストアからWindowsにインストールし、UbuntuのコマンドラインでDockerとdocker-composeをインストールする方法が良いです。
Docker desktopを使わずWSL上で動作するubuntuでDockerとdocker-composeを動かす


Laravel/SailでDockerコンテナを一時停止する方法と再開する方法 | Dockerサービスの停止と開始

コンテナの一時停止:
./vendor/bin/sail pause
コンテナの再開(一時停止解除):
./vendor/bin/sail unpause
コンテナを停止:
./vendor/bin/sail stop

コンテナを起動:
./vendor/bin/sail up -d
Dockerサービスを停止します。(Dockerそのものを停止させます。)
sudo service docker stop
Dockerサービスを起動します。
sudo service docker start
Vmmemプロセスを停止する。 コマンドプロプトで以下を実行
wsl --shutdown
全ての Docker コンテナを停止し、ネットワークを削除します。
./vendor/bin/sail down

MySQLにアクセスする

./vendor/bin/sail mysql

ボリュームをすべて削除する。(MySQLのデータベースを初期化する)

Dockerまわりと思われる不具合が多発するときに試すことができる。 データベースもすべて削除されるので注意する。

sail down --rmi all -v

PHPMyAdminをインストールする

「docker-compose.yml」に下記を記述し、「./vendor/bin/sail up -d --build」を実行する。

ブラウザで「http://localhost:8080」にアクセスするとphpMyAdminが開く。
phpMyAdminのログインに必要なユーザー名とパスワードは .envに記述されているDB_USERNAMEとDB_PASSWORDにセットしている値と同じである。

docker-compose.yml

    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: '%'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sail-mysql:/var/lib/mysql'
            - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
        networks:
            - sail
        healthcheck:
            test:
                - CMD
                - mysqladmin
                - ping
                - '-p${DB_PASSWORD}'
            retries: 3
            timeout: 5s
    phpmyadmin:
        image: phpmyadmin/phpmyadmin:latest
        ports:
            - 8080:80
        depends_on:
            - mysql
        environment:
            PMA_HOST: mysql
        networks:
            - sail
	

Laravelのマイグレーションの考え方と基本操作

Laravelは、開発者の生産性を高めるために設計されたフレームワークです。その多くの便利な機能の中でも、マイグレーションは特に開発チーム内でのデータベースの変更を円滑に共有し、追跡するための重要な仕組みです。

マイグレーションの基本概念

マイグレーションは、データベースのテーブル作成や変更をプログラムの形で記述することを可能にし、これらの変更をバージョン管理システムに保存できるようにします。これにより、チームメンバー間での変更内容の共有が容易になるだけでなく、開発と本番環境のデータベース構造を同期しやすくなります。

順次実行の重要性

Laravelのマイグレーションは「順次実行」される必要があります。これは、マイグレーションファイルの命名規則によって保証されています。マイグレーションファイル名の先頭にはタイムスタンプが付されており、Laravelはこのタイムスタンプをもとにマイグレーションを適切な順序で実行します。

マイグレーションのバージョン管理

Laravelのマイグレーションファイルはソースコードと一緒にバージョン管理システム(例えばGit)で管理されるべきです。これにより、過去に行ったデータベースの変更履歴を追跡でき、必要に応じて以前の状態に戻すことができます。

マイグレーションの変更とロールバック

マイグレーションファイルを修正する必要が出てきた場合、直接既存のファイルを編集するのではなく、新しいマイグレーションを作成することが推奨されます。ただし、何らかの理由で直接修正が必要な場合は、「php artisan migrate:rollback」コマンドを用いて直前のマイグレーションを取り消した後、「php artisan migrate」コマンドで再適用します。

テーブルの直接操作に関する警告

Laravelでマイグレーションを利用している場合、phpMyAdminなどのツールを使用して直接データベースのテーブルを変更することは推奨されません。これは、マイグレーションの履歴とデータベースの現状が不整合になり、競合問題を引き起こすリスクがあるからです。

マイグレーションの状態確認

ロールバックの操作を行った結果、どの変更が適用され、どれが取り消されたのか混乱してしまった場合には、「php artisan migrate:status」コマンドを使います。このコマンドは現在のデータベースのマイグレーション状態を一覧表示し、どのマイグレーションが実行済みであるかを確認するのに役立ちます。
以上のマイグレーションの考え方と基本操作に慣れることで、Laravelを使った開発プロセスがよりスムーズに、かつ一貫性をもって行えるようになります。データベースの変更管理は、アプリケーションの安定性とメンテナンスの容易さに直結するため、Laravelのマイグレーション機能を適切に活用することが非常に重要です。

よく使うコマンド

マイグレーションファイルを作成する。(イグレーションファイルからDBテーブルを作成するという一方向的な考えであるため、DBテーブルからマイグレーションを生成するわけではない)
php artisan make:migration create_テーブル名_table
マイグレーションを順次実行する
php artisan migrate
マイグレーションを1step戻る。マイグレーションファイルの記述を間違えてマイグレーションを実行したとき、このコマンドで間違えたマイグレーションファイルの場所まで戻って、そのマイグレーションファイルを修正します。修正が終わったら再びマイグレーションを実行します。
php artisan migrate:rollback
現在、どのマイグレーションファイルまで実行したのか確認する。「php artisan migrate:rollback」でどこまで戻ったか分からなくなった時に下記コマンドを実行し、現在どのマイグレーションまで適用しているか確認します。
php artisan migrate:status
既存のテーブルに新しいフィールドを追加するマイグレーションファイルを作成する例。(実際にDBに新フィールドを追加する処理は下記コマンドで作成されたファイルを手動で修正する)
php artisan make:migration add_cat_category_id_to_cats_table --table=cats

マイグレーション:シーダーファイルでサンプルデータを生成する

Laravelでは、データベースのテーブル構造を設定するためのマイグレーションファイルと、テーブルに初期データやテストデータを挿入するためのシーダーファイルという2種類のファイルがあります。ここでのポイントは、サンプルデータはマイグレーションファイルではなく、シーダーファイルに記述するべきだということです。開発の流れとしては、まずマイグレーションを実行してデータベーステーブルを作成し、その後シーダーファイルを使用してサンプルデータをデータベースに追加します。

シーダーファイルは、必要に応じて開発者が自らコードを記述する必要があります。これにより、アプリケーション開発中やテスト時に、簡単にデータベースにデータを追加できるようになります。

補足事項:

シーダーファイルを作成するコマンド

php artisan make:seeder RichmenusTableSeeder
コマンドを実行すると空の「database/seeders/RichmenusTableSeeder.php」ファイルが生成される。

シーダーファイルの記述例

database/seeders/RichmenusTableSeeder.php

<?php

namespace Database¥Seeders;

use Illuminate¥Database¥Console¥Seeds¥WithoutModelEvents;
use Illuminate¥Database¥Seeder;
use Illuminate¥Support¥Facades¥DB;
use Illuminate¥Support¥Str;

class RichmenusTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
    	$sampleData = [];
    	for ($i = 0; $i < 12; $i++) {
    		$sampleData[] = [
    				'id'                => Str::random(26), // 26文字のランダム文字列を生成
    				'line_account_id'   => Str::random(26),
    				'size_w'            => rand(800, 1200),
    				'size_h'            => rand(400, 800),
    				'default_selected'  => rand(0, 1),
    				'name'              => 'Sample Rich Menu ' . $i,
    				'chat_bar_text'     => 'Chat with us!',
    				'line_rich_menu_id' => Str::random(26),
    				'rich_menu_img'     => 'https://example.com/img/' . $i . '.jpg',
    				'review_svg'        => 'https://example.com/svg/' . $i . '.svg',
    				'start_time'        => now()->toDateTimeString(),
    				'end_time'          => now()->addDays(rand(1, 30))->toDateTimeString(),
    				'segment'           => null, // 適宜必要な値に設定してください。
    				'rich_menu_category'=> null, // 適宜必要な値に設定してください。
    				'delete_flg'        => 0,
    				'created_at'        => now(),
    				'updated_at'        => now(),
    		];
    	}
    	
    	DB::table('richmenus')->insert($sampleData);
    }
}

	

シーダーファイルを実行する

php artisan db:seed --class=RichmenusTableSeeder

一括でシーダーファイルを実行できるようにする

「database/seeders/DatabaseSeeder.php」にシーダーファイルを記述する。
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     */
    public function run(): void
    {
    	// 各シーダークラスを実行する順序で呼び出す
    	$this->call([
    			RichmenusTableSeeder::class,
    			RichmenuAreasTableSeeder::class,
    	]);
    }
}
	
すべてのシードを実行するには、以下のコマンドを使います。
php artisan db:seed

おまけ:マイグレーションとシーダーを同時に実行する場合

php artisan migrate --seed

UUID形式のidを生成する

重複の可能性が気になりそうだが、現実として重複の可能性は極めて低確率であり気にするレベルではないそうだ。

	$uuid = Str::uuid()->toString(); // UUIDを生成 36桁
	$uuid = str_replace('-', '', $uuid); // ハイフンを削除
	$uuid = substr($uuid, 0, 26); // 26文字に短縮
	

Laravelのコントローラクラスの共通初期化 | コンストラクタではない


	public function __construct(RichMenuTemplateService $service) {
		$this->service = $service;
		
        $this->middleware(function ($request, $next) {
            $this->boot();

            return $next($request);
        });
		
	}
	
	
	/**
	 * 共通の初期化クラス
 	*/
	protected function boot()
    {
        // ここに共通の初期化ロジックを書きます。
        // この段階でセッションや認証情報にアクセス可能です。
        
    	$user_id = auth()->user()->id;
		$this->tmp_img_dir = 'tmp/rich_menu/' . $user_id;
    }