- 投稿日:2020-09-13T18:35:56+09:00
[Laravel�] joinで結合先のカラムの取得がものすごく簡単でびっくりした話
User.phppublict function content() { $this->join('content', 'content.user_id', 'user.user_id') }とすると、発行されるSQLは
SELECT * from user inner join content on content.user_id = user.user_id where user.deleted_at is NULLとなる。多分(間違っていたらすみません)
ここでcontentの主キ-も(content_id)取得したいとする。
そこでselect()の出番次のようにする
$this->join('content', 'content.user_id', 'user.user_id') ->select('user.*', 'content.content_id')すると
SELECT user.*, content.content_id from user inner join content on content.user_id = user.user_id where user.deleted_at is NULL簡単に結合先のカラム取得できた。
- 投稿日:2020-09-13T17:31:00+09:00
Laravel 8 の新機能!マイグレーションスカッシングで肥大化したマイグレーションファイルを1つにまとめる
大規模または長期間運用されているアプリケーションでは、
数年以上前に作成されたマイグレーションファイルが膨大な数になってきます。データベーステストを行っているプロジェクトでは、マイグレーションを適用するのに時間がかかりテストの速度が低下する懸念があります。
これの解決策として、Laravel 8ではマイグレーションスカッシング(スキーマダンプ)と呼ばれる新機能が追加されました。
mysqldump
まはたpgdump
を使用して現在のスキーマの状態からダンプを作成します。
(MySQL、PostgreSQL、SQLiteデータベースのみサポート)公式ドキュメント・ソース
- https://readouble.com/laravel/8.x/ja/migrations#squashing-migrations
- https://laravel.com/api/8.x/Illuminate/Database/Console/DumpCommand.html
- https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Console/DumpCommand.php
マイグレーションファイルを確認
初期状態のままですが...下記のマイグレーションファイルがあるとします。
$ ls -1 database/migrations 2014_10_12_000000_create_users_table.php 2014_10_12_100000_create_password_resets_table.php 2019_08_19_000000_create_failed_jobs_table.phpスキーマダンプ コマンド
$ php artisan schema:dump
--prune
オプションを付けると、既存のマイグレーションファイルの削除が行われます。$ php artisan schema:dump --prune
database/schema/mysql-schema.sql
が生成されます。
/database/schema/<DB_CONNECTION>-schema.mysql
database/schema/mysql-schema.sql/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `failed_jobs` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `connection` text COLLATE utf8mb4_unicode_ci NOT NULL, `queue` text COLLATE utf8mb4_unicode_ci NOT NULL, `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL, `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `migrations` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `batch` int NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `password_resets` ( `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `created_at` timestamp NULL DEFAULT NULL, KEY `password_resets_email_index` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `users` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `email_verified_at` timestamp NULL DEFAULT NULL, `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `users_email_unique` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; INSERT INTO `migrations` VALUES (1,'2014_10_12_000000_create_users_table',1); INSERT INTO `migrations` VALUES (2,'2014_10_12_100000_create_password_resets_table',1); INSERT INTO `migrations` VALUES (3,'2019_08_19_000000_create_failed_jobs_table',1);
migrations
以外のテーブルのデータはダンプされず、テーブル構成のみダンプされるみたいです。(マイグレーションなのにデータまでダンプされたら困りますが...)マイグレーションを実行する
$ php artisan migrate:reset $ php artisan migrate Loading stored database schema: /Users/ucan-lab/laravel-love/backend/database/schema/mysql-schema.sql Loaded stored database schema. (128.57ms) Nothing to migrate.
mysql-schema.sql
が読み込まれて、既存のマイグレーションファイルは無視されてます。スキーマダンプのメリット
前述の通り、データベーステストが高速化されるメリットがあります。
また、スキーマダンプはマイグレーションファイルではなく
mysqldump
コマンドを使って現在のデータベースからダンプファイルを作成されています。
本来の用途とは違うかもしれませんが、マイグレーションファイルでデータベースが管理されていないプロジェクトからLaravelに移行する際はとても役に立つ機能ではないでしょうか。使ってみた感想
mysqldump
コマンドをLaravelで実行しているので、Dockerでやろうとするとmysqldump
コマンドをコンテナ内で使えるようにしてあげる必要があるし、debianのdefault-mysql-client
はMariaDBに統合されたようでLaravelで実行しているオプションが存在していなかったりとDockerで頑張ろうとすると手間かもしれません...今回はローカルで試してみました。参考
- 投稿日:2020-09-13T15:59:56+09:00
Laravel開発環境構築
Laravel-base
ネットを探すと色々あるのですが、個人的に一番しっくりくるLaravel開発環境です。
忘れないようにメモ的な意味でアップします。
DockerでNginx、PHP-FPM、MySQLを構成しています。
ペースにしているのは下記サイトに記載されていた方法です。ディレクトリやPHPのバージョンが違いますが、ほぼ同じやり方で記載しています。参考:
DigitalOcean - How To Set Up Laravel, Nginx, and MySQL with Docker Compose
https://www.digitalocean.com/community/tutorials/how-to-set-up-laravel-nginx-and-mysql-with-docker-composeGithubにもアップしています。
https://github.com/noktone/laravel-baseDockerのインストール
インストール
以下はUbuntu18.04での作業になります。Macの人はDocker for Macを、Windowsの人はDocker for Windowsをインストールしてください。
# 旧バージョンのDockerをアンインストール $ sudo apt-get remove docker docker-engine docker.io # aptのアップデート $ sudo apt-get update # HTTPSリポジトリを利用できるようにする $ sudo apt install apt-transport-https ca-certificates curl software-properties-common # GPGキーの追加 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # DockerリポジトリをAPTソースに追加 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # インストール $ sudo apt-get update $ sudo apt-get install docker-ce docker-composeインストール後の処理
一般ユーザーでDockerを実行できるようにする
# ユーザーをdockerグループに追加 $ sudo groupadd docker $ sudo usermod -aG docker $USER # テスト $ docker run hello-worldエラーの一例
下記エラーが出る場合(idコマンドなどでdockerグループが表示されない場合です)があります。
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.39/containers/create: dial unix /var/run/docker.sock: connect: permission denied. See 'docker run --help'.dockerグループへログインして有効にします。
$ newgrp docker # プライマリのグループがdockerになるので、ユーザーのグループに戻す(hogeユーザーのhogeグループの場合) $ newgrp hogeLaravelのベースプロジェクトをGitから取得する
composerなどで作成してもよいですが、ここではgitから取得する方法で作成します。
また、ホームディレクトリにsrc/laravel-baseというディレクトリを作ることを前提にしています。適宜変更してください。$ mkdir -p ~/src/laravel-base $ cd ~/src/laravel-base # laravleの最新版をclone $ git clone https://github.com/laravel/laravel.git # 特定のバージョンを指定する場合は、cloneする際にバージョンを指定します。 $ git clone -v 5.5.28 https://github.com/laravel/laravel.gitComposer
$ cd ~/src/laravel-base/laravel $ docker run --rm -v $(pwd):/app composer installdocker runコマンドに--vオプションをつけることで現在のディレクトリをバインドマウントしたコンテナが作成されます。Windowsの人は「$(pwd)」のところに直接パスを書きます。--rmはDocker終了後に削除するオプションです。別にこの段階やらなくても大丈夫です。
Linuxの人はアクセス権の問題を回避するため、所有者をroot以外のユーザーに設定します。
$ sudo chown -R $USER:$USER ~/src/laravel-baseDocker Composeファイルの作成
mroongaのDockerイメージはrootパスワードやdatabaseを自動作成しませんが、ここではMySQLのDockerイメージを使用する場合に備えて記載しています。
PHPはDockerfileで指定するため、後ほど作成します。$ vim docker-compose.ymlversion: '3' services: #PHP Service app: build: context: . dockerfile: ./php/Dockerfile container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./laravel:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network #Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./laravel:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network #MySQL Service db: image: mysql:8 container_name: db restart: unless-stopped tty: true ports: - "3306:3306" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: rootpassword MYSQL_USER: laravel MYSQL_PASSWORD: password TZ: 'Asia/Tokyo' SERVICE_TAGS: dev SERVICE_NAME: mysql volumes: - dbdata:/var/lib/mysql/ - ./mysql/log:/var/log/mysql - ./mysql/my.cnf:/etc/mysql/my.cnf networks: - app-network #Docker Networks networks: app-network: driver: bridge #Volumes volumes: dbdata: driver: localDockerfileの作成
PHP7.4-FPM用のDockerfileを作成します。
$ vim ~/src/laravel-base/php/DockerfileFROM php:7.4-fpm # Copy composer.lock and composer.json COPY ./laravel/composer.lock /var/www/ COPY ./laravel/composer.json /var/www/ # Set working directory WORKDIR /var/www # Install dependencies RUN apt-get update && apt-get install -y \ build-essential \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ locales \ zip \ jpegoptim optipng pngquant gifsicle \ vim \ unzip \ git \ libonig-dev \ curl \ libzip-dev \ zlib1g-dev # Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/* # node.js RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash - RUN apt-get install -y nodejs # Install extensions RUN docker-php-ext-install pdo_mysql zip exif pcntl RUN docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ RUN docker-php-ext-install gd # Install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www # Copy existing application directory contents COPY . /var/www # Copy existing application directory permissions COPY --chown=www:www . /var/www # Change current user to www USER www # Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]PHPの設定
PHP7.2-FPMの設定です。phpディレクトリを作成して、その配下にiniファイルを設置します。
$ mkdir ~/src/laravel-base/php $ vim ~/src/laravel-base/php/local.iniupload_max_filesize=40M post_max_size=40M date.timezone = "Asia/Tokyo"Nginxの設定
Nginxのコンフィグファイルを作成します。80版ポートで動作させてます。
$ mkdir -p ~/src/laravel-base/nginx/conf.d $ vim ~/src/laravel-base/nginx/conf.d/app.confserver { listen 80; index index.php index.html; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /var/www/public; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location / { try_files $uri $uri/ /index.php?$query_string; gzip_static on; } }動作時にタイムアウトが問題になる場合は下記 keepalive_timeout、send_timeout、fastcgi_read_timeout、fastcgi_connect_timeout、fastcgi_send_timeoutオプションを指定する(未検証。増やしすぎるとパフォーマンスの劣化につながるため注意)
server { listen 80; index index.php index.html; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /var/www/public; keepalive_timeout 600; send_timeout 600; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; fastcgi_read_timeout 600; fastcgi_connect_timeout 600; fastcgi_send_timeout 600; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location / { try_files $uri $uri/ /index.php?$query_string; gzip_static on; } }MySQLの設定
$ mkdir ~/src/laravel-base/mysql $ vim ~/src/laravel-base/mysql/my.cnfgeneral_logなどは状況によって。
[mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_bin general_log = 0 general_log_file = /var/lib/mysql/general.log log_timestamps=SYSTEM secure-file-priv=NULL default-authentication-plugin = mysql_native_password [mysql] default-character-set = utf8mb4 [client] default-character-set = utf8mb4ログ用のディレクトリも作成しておきます。
$ mkdir ~/src/laravel-base/mysql/logLaravelの環境設定ファイルの作成
Laravelの環境設定ファイルを作成します。環境設定ファイル(.env)はgitの管理下からは外しますので、本番環境では別途用意します。
$ cp .env.example .env $ vim .envlaravel-baseをgit cloneした場合は下記を参考にしてください。DB_HOSTにはデータベースのコンテナ名を記載します。
APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=null MAIL_FROM_NAME="${APP_NAME}" AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"Gitの管理下に含めないファイル
Docker用のファイルなど、アプリケーションに直接関係のないファイルはGitの管理下には置かないようにします。
$ vim .gitignore/node_modules /public/hot /public/storage /storage/*.key /vendor .env .phpunit.result.cache Homestead.json Homestead.yaml npm-debug.log yarn-error.log docker-compose.yml Dockerfileパッケージのインストール
下記を実行して、venderパッケージをインストールします。
$ docker-compose exec app php composer installDockerコンテナの作成とLaravelの起動時の設定
コンテナの作成と起動を実行します。
$ docker-compose up -d作成が終わったら、動作しているか確認します。
# 起動中のコンテナの確認 $ docker psLaravelのアプリケーションキーを生成します。
$ docker-compose exec app php artisan key:generate設定をキャッシュする場合は以下を実行します。設定内容が/var/www/bootstrap/cache/config.phpコンテナにロードされます。
$ docker-compose exec app php artisan config:cacheマイグレーションを実行し、データベース無いに認証機能に必要なテーブルを作成
$ docker-compose exec app php artisan migrate設定が完了したら、下記URLで確認します。
http://localhost
終了する場合は下記を実行します。$ docker-compose down # volumeごと消すとき(MySQLがうまく設定できないとか。データが消えるので注意) docker-compose down --volumes不調のときに試すもの。データが消えたりするので注意
# 止まってるコンテナ、使われてないボリューム、使われてないネットワーク、使われてないイメージを削除します。 $ docker system prune # 個別にやる場合は下記になります。 $ docker image prune $ docker container prune $ docker network prune $ docker volume pruneMySQLのDBとユーザーの作成
docker-composeで定義しているのですでに作成されているはずですが、やり方を忘れないように記載
$ mysql -u root -p --protocol=tcp$ CREATE DATABASE laravel; $ CREATE USER 'laravel'@'%' IDENTIFIED BY 'password'; $ GRANT ALL ON laravel.* TO 'laravel'@'%' WITH GRANT OPTION; $ FLUSH PRIVILEGES; $ exit
- 投稿日:2020-09-13T14:50:32+09:00
Laravelでサブドメイン対応(さくらレンタルサーバ)
やりたいこと
main.hoge.hoge → routes/main.phpでルーティング
api.hoge.hoge → routes/api.phpでルーティング
test.hoge.hoge → routes/test.phpでルーティング①テスト環境と本番環境でドメインを分けるためにconfig設定
configフォルダ内にmyapp.php作成
\Config::get('myapp.domain.main')でアクセスできるconfig/myapp.php<?php return [ 'domain' => [ 'main' => 'main.hoge.hoge', 'api' => 'api.hoge.hoge', 'test' => 'test.hoge.hoge', ] ];コンフィグファイルのキャッシュクリアをしないと反映されない。
$ php artisan config:cache②ルーティング設定
/app/Providers/RouteServiceProvider.phpclass RouteServiceProvider extends ServiceProvider { public function map() { $this->mapApiRoutes(); //$this->mapWebRoutes(); $this->mapMainRoutes(); $this->mapTestRoutes(); } protected function mapMainRoutes() { Route::domain(\Config::get('myapp.domain.main')) ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/main.php')); } protected function mapTestRoutes() { Route::domain(\Config::get('myapp.domain.test')) ->middleware('web') ->namespace($this->namespace) ->group(base_path('routes/test.php')); } protected function mapApiRoutes() { Route::domain(\Config::get('api.domain.test')) ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); }routes/main.php,routes/test.phpを作ってルーティングさせる。
おわり。
CROS対応
サブドメインで分けるとaxiosからの通信がクロスドメインで引っかかるので設定
※バージョン指定しないとエラーになります。$ composer require barryvdh/laravel-cors:^0.11続きは下記参照
Laravelでクロスオリジン(CORS)に対応する為のメモです※crosの設定を上に持ってこないと一部ヘッダが付与されない場合がある?
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'cros', 'throttle:60,1', 'bindings', ], ]; /** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ 'cros' => \Barryvdh\Cors\HandleCors::class, 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ];セッション共有
.envにSESSION_DOMAIN設定すればサブドメイン間でセッション共有可能
'''.enb
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_DOMAIN='.hoge.hoge'
'''
- 投稿日:2020-09-13T12:36:57+09:00
EC2・RDSでLaravelの環境構築【PHP・MySQL・Nginx】
実現したいこと
既存で作成したLaravelのWebアプリケーションをEC2・RDSの環境にデプロイすることです。
前提
- GitHubに自身で作成したLaravelのWebアプリケーションがリポジトリに上がっている
- Laravel6.1
- PHP7.3
- MySQL5.7.31
- Nginx1.17.10
EC2
EC2の作成
ステップ 1: Amazon マシンイメージ (AMI)
- Amazon Linux 2 AMIを選択
ステップ 2: インスタンスタイプの選択
- t2.micro
ステップ 3: インスタンスの詳細の設定
- 全てデフォルト
ステップ 4: ストレージの追加
- 全てデフォルト
ステップ 5: タグの追加
- キー Name
- 値 適宜好きな名前
ステップ 6: セキュリティグループの設定
- ルールの追加
- SSH *アクセス元を絞るため、ソースをマイIPを選択しましょう。
- HTTP
ステップ 7: インスタンス作成の確認
- 起動
- キーペアの作成 *すでに作っているなら既存のキーペアを使用しましょう。ECSにSSHでログインする際に必要になります
SSHで接続
- キーペアファイルを配置
- アクセス権を400に設定
- 接続
ssh -i ~/.ssh/[SHHキー.pem] ec2-user@[ドメイン名かIPアドレス(パブリック DNS (IPv4))]
■参考記事
MacのターミナルでEC2にSSHでログインするNginxのインストール
yum update
$ sudo yum update -yNginxをインストール
*適宜、ローカルのバージョンに合わせてインストールしてください。$ sudo amazon-linux-extras install nginx1.12 -yNginxを起動
$ sudo systemctl start nginxIPv4パブリックIPをブラウザに入力すると下記の画面が表示されます。
PHPのインストール
PHPのインストール
*適宜、ローカルのバージョンに合わせてインストールしてください。$ sudo amazon-linux-extras install php7.3下記のコマンドでインストールされているか確認
$ php -vNginxを使用するため、PHPの設定情報を変更
$ sudo vi /etc/php-fpm.d/www.conf/etc/php-fpm.d/www.conf# 略 ; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user's group ; will be used. ; RPM: apache user chosen to provide access to the same directories as httpd user = nginx # デフォルトは「user = apache」 ; RPM: Keep a group allowed to write in log dir. group = nginx # デフォルトは「user = apache」 # 略Nginxを再起動
$ sudo systemctl restart nginx■参考記事
AWS EC2 + nginx + Laravel + RDS(PostgreSQL)の環境を構築するLaraveのリポジトリをclone
Composer
Composerをインストール
$ cd ~ $ sudo curl -sS https://getcomposer.org/installer | php # コンポーザーのインストール $ sudo chown root:root composer.phar $ sudo mv composer.phar /usr/bin/composer # パスを通す $ composer # インストールされたか確認php-mbstringとphp-xmlをインストール
$ sudo yum install php-mbstring php-xml -yGit
Gitのインストール
$ sudo yum install git # インストール $ git --version # インストールされたかバージョン確認GitHubのリポジトリからclone
ディレクトリ作成
$ sudo mkdir /var/www $ cd /var/wwwclone
*今回はHTTPS方式でクローンしましたが、もちろんSSH方式でも可能です。$ sudo git clone https://github.com/[ユーザー名]/[リポトリ名].gitNginxの設定情報の変更
$ sudo vi /etc/nginx/nginx.conf/etc/nginx/nginx.conf# 略 http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /var/www/リポジトリ名/public; # ここを修正 # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { try_files $uri $uri/ /index.php$is_args$args; # ここを修正 } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } # Settings for a TLS enabled server. # # server { # listen 443 ssl http2 default_server; # listen [::]:443 ssl http2 default_server; # server_name _; # root /usr/share/nginx/html; # # ssl_certificate "/etc/pki/nginx/server.crt"; # ssl_certificate_key "/etc/pki/nginx/private/server.key"; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 10m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # # # Load configuration files for the default server block. # include /etc/nginx/default.d/*.conf; # # location / { # } # # error_page 404 /404.html; # location = /40x.html { # } # # error_page 500 502 503 504 /50x.html; # location = /50x.html { # } # } } # 略Nginxを再起動
$ sudo systemctl restart nginx各種インストール、設定
$ cd /var/www/リポジトリ名 $ sudo composer install # composerのインストール $ sudo chmod -R 777 storage/ # 権限変更.envファイルの作成
$ sudo touch .env.envの情報を適宜変更してください。DBの設定はRDSの設定後に行います。
/var/www/リポジトリ名/.envAPP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE= DB_USERNAME= DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=null MAIL_FROM_NAME="${APP_NAME}"これでIPv4パブリックIPをブラウザに入力すればLaravelの画面が表示されると思います。
■参考記事
Laravelのプロジェクトをgit cloneしたときの手順RDS
設定情報の値は適宜情報を入力し、作成を行ってください。
参考までに私は下記の画像の値を入力しました。
Virtual Private Cloud (VPC)、サブネットグループ、既存のVPCセキュリティグループはEC2の設定に合わせました。
最初のデータベース名の値は.envファイルを設定する際に使用します。セキリティグループ
今回のRDSのセキリティグループの設定はEC2と同一のものにしました。
セキリティグループのインバウンドルールの設定は、「HTTP」「SSH」を既存で追加していますので、ここで新たに「MYSQL/Auora」を追加しましょう。
.envの設定、マイグーレション
.envの設定に先ほどのRDSの設定した値を入力します。
$ sudo vi .env/var/www/リポジトリ名/.envDB_CONNECTION=mysql DB_HOST=エンドポイント DB_PORT=3306 DB_DATABASE=最初のデータベース名 DB_USERNAME=マスターユーザー名 DB_PASSWORD=マスターパスワード私は誤ってDB_DATABASEの値をDBクラスター識別子で設定してかなりハマりました。
最後にマイグレーションを実行
$ sudo php artisan migrateこれでIPv4パブリックIPをブラウザに入力するとデータベースが繋がった状態でLaravelの画面が表示されます。
全体像の参考
- 投稿日:2020-09-13T09:39:17+09:00
react + laravelで予約機能付きHPを作成する
おおまかな流れ
laravelでreactを使えるようにする
ターミナルで以下のコマンド群を実行する
まずは、laravelのuiパッケージをインストールする
composer require laravel/ui
次に、reactを使えるようにする
php artisan ui react
最後にnpmをインストールし、自動的にファイルの変更を検知してコンパイルをしてもらう
npm install && npm run watch-poll
reactで開発できるようにするため、laravelのファイル群を変更する
一番最初にリクエストを投げた時にレスポンスで返ってくるbladeファイルを作成する。
下記のコードにある<div id="index"></div>
の部分に注目する。
ここ部分にreactで作成する箇所が埋め込まれるイメージ。top.blade.php<body> <div id="index"></div> </body>次は、下記のコードにある
if (document.getElementById('index')) {
ReactDOM.render(<Index />, document.getElementById('index'));
}
の部分に注目する。
先ほど設定したid・indexの部分にこのIndexコンポーネントを埋め込むIndex.jsimport React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import Top from './Top'; import Greetings from './Greetings'; import Profile from './Profile'; import Fee from './Fee'; import Contact from './Contact'; import Access from './Access'; import Reservation from './Reservation'; import '../../../public/css/index.css'; export default class Index extends Component { render() { return ( <div> <BrowserRouter> <Switch> <Route exact path="/" component={Top} /> <Route exact path="/greetings" component={Greetings} /> <Route exact path="/profile" component={Profile} /> <Route exact path="/reservation" component={Reservation} /> <Route exact path="/fee" component={Fee} /> <Route exact path="/contact" component={Contact} /> <Route exact path="/access" component={Access} /> </Switch> </BrowserRouter> </div> ); } } if (document.getElementById('index')) { ReactDOM.render(<Index />, document.getElementById('index')); }SPAを作成するためにreact-router-domをインストールして設定する
ターミナルで
npm install react-router-dom
を実行すると、package.jsonにインストールしたモジュールが記述される。
これでモジュールを使用する準備が整った。まずは、BrowserRouter、Route、Switchモジュールをimportする。
次に、BrowserRouter、Route、Switchの順に入れ子にする。
<Route path="/" component={Top} />
の解説をする。
/にアクセスがあった場合に、Topコンポーネントを表示させるということである。ここに遷移させるためのアンカー部分は、Linkモジュールを使用する。下記に例を示す。
Linkモジュールはレンダーされるとaタグになる。このLinkモジュールで生成したアンカーをクリックすると、
上記で設定した<Route path="/" component={Top} />
の通り、Topコンポーネントが表示される。ルーティングはこれで完成。
Header.js<Link to="/"</Link>Index.jsimport React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import Top from './Top'; export default class Index extends Component { render() { return ( <div> <BrowserRouter> <Switch> <Route path="/" component={Top} /> </Switch> </BrowserRouter> </div> ); } }予約機能の作成
コードが長くてみにくいので必要な部分をピックアップして解説していく。
下記のコードにあるconstructor内では、stateを定義したり関数をバインドする。
これをしないとstate・関数が使えない。
constructor(props) {
super(props)
this.state = {
date: "",
}
this.onDateChange = this.onDateChange.bind(this);
}下記のコードは、stateであるdateにフォームで入力された値を格納している。
onDateChange(e) {
this.setState({ date: e.target.value })
}下記のコードのdisabled={this.state.isDisabled}はisDisabledのstateがtrueの場合はsubmitできないようにしている。
onClick={this.postReservation}は、submitした際にpostReervationメソッドが実行される。
postReservationメソッドについての説明はこの後に行う。
<Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary">
送信する
</Button>
下記のコードの解説をする。
変数dataにフィールドに入力された値を格納する。
axiosを使用して非同期通信を行なっている。post送信で/reservationに対して先ほど定義した変数dataを送っている。
では。送信した先の/reservationを見ていく。const data = { date: this.state.date, } axios.post('/reservation', data)先ほど解説した通りに非同期通信を行うと下記の処理が実行される。
先ほど定数dataで送った値が$requestに格納されている。
ReservationSendmailクラスをインスタンス化する際に$date渡す。ReservationController.php<?php namespace App\Http\Controllers; use App\Mail\ReservationSendmail; use Illuminate\Support\Facades\Mail; use App\Http\Requests\ReservationRequest; class ReservationController extends Controller { public function store(ReservationRequest $request) { $date = $request->date; $to = 'test@gmail.com'; Mail::to($to)->send(new ReservationSendmail($date); return; } }次はReservationSendmailクラスをみる。
ReservationSendmail.php<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; class ReservationSendmail extends Mailable { use Queueable, SerializesModels; private $date; /** * Create a new message instance. * * @return void */ public function __construct($date) { $this->date = $date; //ReservationControllerから送られてきた$dateをプロパティである$this->dateに格納している。 } /** * Build the message. * * @return $this */ public function build() { return $this ->from('tatataabcd@gmail.com') //送信元のメールアドレス ->subject('自動送信メール') //メールのタイトル ->view('reservation.mail') //viewはreservation.mail.blade.phpを使用 ->with([ 'date' => $this->date, //先ほど格納したプロパティをreservation.mail.blade.phpでも使えるようにしている ]); } }次にreservation.mail.blade.phpを見る
mail.blade.phpお問い合わせ内容を受け付けました。<br> <br> ■日時<br> {!! $date !!}<br> この内容がReservationControllerのtoに指定したメールアドレスに送信される。Reservation.jsimport React, { Component } from 'react'; import moment from 'moment'; import Header from './Header'; import { Button } from '@material-ui/core'; import '../../../public/css/reservation.css'; class Reservation extends Component { constructor(props) { super(props) this.state = { date: "", name: "", phone: "", email: "", age: "", state: "", gender: "", isDisabled: false, errors: { date: [], name: [], phone: [], email: [], age: [], state: [], gender: [], } } this.onDateChange = this.onDateChange.bind(this); this.postReservation = this.postReservation.bind(this); } onDateChange(e) { this.setState({ date: e.target.value }) } postReservation(e) { if (this.state.date !== "" && this.state.name !== "" && this.state.phone !== "" && this.state.email) { this.setState({ isDisabled: true }); } axios .post('/reservation', data) .then(response => { alert('予約を受け付けました。') this.setState({ isDisabled: false }); this.setState({ errors: [] }); }) .catch(error => { this.setState({ isDisabled: false }); console.log(error.response.data.errors); const errors = this.state.errors; // 次の日から const nextDay = moment().add('1', 'd').format('YYYY-MM-DD'); let reservationDay = this.state.date; reservationDay = reservationDay.slice(-16, -6); // 水木のみ const date = moment(this.state.date); const dayOfWeek = date.day(); // 時間指定 const hour = date.hour(); const minute = date.minute(); const hourAndMinute = hour + ':' + minute; if (this.state.date === "" || nextDay > reservationDay || dayOfWeek !== 3 && dayOfWeek !== 4 || hourAndMinute !== 10 + ':' + 0 && hourAndMinute !== 11 + ':' + 0 && hourAndMinute !== 13 + ':' + 0 && hourAndMinute !== 14 + ':' + 0 && hourAndMinute !== 15 + ':' + 0 && hourAndMinute !== 15 + ':' + 30 && hourAndMinute !== 16 + ':' + 0 && hourAndMinute !== 16 + ':' + 30 ) { errors.date = error.response.data.errors.date[0]; } else { errors.date = ""; } this.setState({ errors: errors }); if (this.state.name === "") { errors.name = error.response.data.errors.name[0]; } else { errors.name = ""; } this.setState({ errors: errors }); if (this.state.phone === "" || isNaN(this.state.phone)) { errors.phone = error.response.data.errors.phone[0]; } else { errors.phone = ""; } this.setState({ errors: errors }); if (this.state.email == "" || this.validateEmail(this.state.email)) { errors.email = error.response.data.errors.email[0]; } else { errors.email = ""; } this.setState({ errors: errors }); }); } render() { return ( <React.Fragment> <Header /> <div className="container"> <div className="row"> <div className="col-12"> <h1 class="h3 mb-5 mt-5 text-center">予約フォーム</h1> <div className="form-group"> <span>予約日時</span> <input className="form-control" type="datetime-local" name="date" value={this.state.date} onChange={this.onDateChange} /> <p className="err-msg">{this.state.errors.date}</p> </div> <Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary"> 送信する </Button> </div> </div> </div> </React.Fragment > ); } } export default Reservation;
- 投稿日:2020-09-13T09:39:17+09:00
laravel + reactで予約機能付きSPAのHPを作成する
おおまかな流れ
1、laravelでreactが使えるようにターミナルから必要なものをインストールする。
2、ルーティングを設定する
3、予約機能の作成laravelでreactを使えるようにする
ターミナルで以下のコマンド群を実行する
まずは、laravelのuiパッケージをインストールする
composer require laravel/ui
次に、reactを使えるようにする
php artisan ui react
最後にnpmをインストールし、自動的にファイルの変更を検知してコンパイルをしてもらう
npm install && npm run watch-poll
reactで開発できるようにするため、laravelのファイル群を変更する
一番最初にリクエストを投げた時にレスポンスで返ってくるbladeファイルを作成する。
下記のコードにある<div id="index"></div>
の部分に注目する。
ここ部分にreactで作成する箇所が埋め込まれるイメージ。top.blade.php<body> <div id="index"></div> </body>次は、下記のコードにある
if (document.getElementById('index')) {
ReactDOM.render(<Index />, document.getElementById('index'));
}
の部分に注目する。
先ほど設定したid・indexの部分にこのIndexコンポーネントを埋め込むIndex.jsimport React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import Top from './Top'; import Greetings from './Greetings'; import Profile from './Profile'; import Fee from './Fee'; import Contact from './Contact'; import Access from './Access'; import Reservation from './Reservation'; import '../../../public/css/index.css'; export default class Index extends Component { render() { return ( <div> <BrowserRouter> <Switch> <Route exact path="/" component={Top} /> <Route exact path="/greetings" component={Greetings} /> <Route exact path="/profile" component={Profile} /> <Route exact path="/reservation" component={Reservation} /> <Route exact path="/fee" component={Fee} /> <Route exact path="/contact" component={Contact} /> <Route exact path="/access" component={Access} /> </Switch> </BrowserRouter> </div> ); } } if (document.getElementById('index')) { ReactDOM.render(<Index />, document.getElementById('index')); }SPAを作成するためにreact-router-domをインストールして設定する
ターミナルで
npm install react-router-dom
を実行すると、package.jsonにインストールしたモジュールが記述される。
これでモジュールを使用する準備が整った。まずは、BrowserRouter、Route、Switchモジュールをimportする。
次に、BrowserRouter、Route、Switchの順に入れ子にする。
<Route path="/" component={Top} />
の解説をする。
/にアクセスがあった場合に、Topコンポーネントを表示させるということである。ここに遷移させるためのアンカー部分は、Linkモジュールを使用する。下記に例を示す。
Linkモジュールはレンダーされるとaタグになる。このLinkモジュールで生成したアンカーをクリックすると、
上記で設定した<Route path="/" component={Top} />
の通り、Topコンポーネントが表示される。ルーティングはこれで完成。
Header.js<Link to="/"</Link>Index.jsimport React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import Top from './Top'; export default class Index extends Component { render() { return ( <div> <BrowserRouter> <Switch> <Route path="/" component={Top} /> </Switch> </BrowserRouter> </div> ); } }予約機能の作成
コードが長くてみにくいので必要な部分をピックアップして解説していく。
下記のコードにあるconstructor内では、stateを定義したり関数をバインドする。
これをしないとstate・関数が使えない。
constructor(props) {
super(props)
this.state = {
date: "",
}
this.onDateChange = this.onDateChange.bind(this);
}下記のコードは、stateであるdateにフォームで入力された値を格納している。
onDateChange(e) {
this.setState({ date: e.target.value })
}下記のコードのdisabled={this.state.isDisabled}はisDisabledのstateがtrueの場合はsubmitできないようにしている。
onClick={this.postReservation}は、submitした際にpostReervationメソッドが実行される。
postReservationメソッドについての説明はこの後に行う。
<Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary">
送信する
</Button>
下記のコードの解説をする。
定数dataにフィールドに入力された値を格納する。
axiosを使用して非同期通信を行なっている。post送信で/reservationに対して先ほど定義した定数dataを送っている。
では。送信した先の/reservationを見ていく。const data = { date: this.state.date, } axios.post('/reservation', data)先ほど解説した通りに非同期通信を行うと下記の処理が実行される。
先ほど定数dataで送った値が$requestに格納されている。
ReservationSendmailクラスをインスタンス化する際に$date渡す。ReservationController.php<?php namespace App\Http\Controllers; use App\Mail\ReservationSendmail; use Illuminate\Support\Facades\Mail; use App\Http\Requests\ReservationRequest; class ReservationController extends Controller { public function store(ReservationRequest $request) { $date = $request->date; $to = 'test@gmail.com'; Mail::to($to)->send(new ReservationSendmail($date); return; } }次はReservationSendmailクラスをみる。
ReservationSendmail.php<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; class ReservationSendmail extends Mailable { use Queueable, SerializesModels; private $date; /** * Create a new message instance. * * @return void */ public function __construct($date) { $this->date = $date; //ReservationControllerから送られてきた$dateをプロパティである$this->dateに格納している。 } /** * Build the message. * * @return $this */ public function build() { return $this ->from('tatataabcd@gmail.com') //送信元のメールアドレス ->subject('自動送信メール') //メールのタイトル ->view('reservation.mail') //viewはreservation.mail.blade.phpを使用 ->with([ 'date' => $this->date, //先ほど格納したプロパティをreservation.mail.blade.phpでも使えるようにしている ]); } }次にreservation.mail.blade.phpを見る
mail.blade.phpお問い合わせ内容を受け付けました。<br> <br> ■日時<br> {!! $date !!}<br> この内容がReservationControllerのtoに指定したメールアドレスに送信される。Reservation.jsimport React, { Component } from 'react'; import moment from 'moment'; import Header from './Header'; import { Button } from '@material-ui/core'; import '../../../public/css/reservation.css'; class Reservation extends Component { constructor(props) { super(props) this.state = { date: "", name: "", phone: "", email: "", age: "", state: "", gender: "", isDisabled: false, errors: { date: [], name: [], phone: [], email: [], age: [], state: [], gender: [], } } this.onDateChange = this.onDateChange.bind(this); this.postReservation = this.postReservation.bind(this); } onDateChange(e) { this.setState({ date: e.target.value }) } postReservation(e) { if (this.state.date !== "" && this.state.name !== "" && this.state.phone !== "" && this.state.email) { this.setState({ isDisabled: true }); } axios .post('/reservation', data) .then(response => { alert('予約を受け付けました。') this.setState({ isDisabled: false }); this.setState({ errors: [] }); }) .catch(error => { this.setState({ isDisabled: false }); console.log(error.response.data.errors); const errors = this.state.errors; // 次の日から const nextDay = moment().add('1', 'd').format('YYYY-MM-DD'); let reservationDay = this.state.date; reservationDay = reservationDay.slice(-16, -6); // 水木のみ const date = moment(this.state.date); const dayOfWeek = date.day(); // 時間指定 const hour = date.hour(); const minute = date.minute(); const hourAndMinute = hour + ':' + minute; if (this.state.date === "" || nextDay > reservationDay || dayOfWeek !== 3 && dayOfWeek !== 4 || hourAndMinute !== 10 + ':' + 0 && hourAndMinute !== 11 + ':' + 0 && hourAndMinute !== 13 + ':' + 0 && hourAndMinute !== 14 + ':' + 0 && hourAndMinute !== 15 + ':' + 0 && hourAndMinute !== 15 + ':' + 30 && hourAndMinute !== 16 + ':' + 0 && hourAndMinute !== 16 + ':' + 30 ) { errors.date = error.response.data.errors.date[0]; } else { errors.date = ""; } this.setState({ errors: errors }); if (this.state.name === "") { errors.name = error.response.data.errors.name[0]; } else { errors.name = ""; } this.setState({ errors: errors }); if (this.state.phone === "" || isNaN(this.state.phone)) { errors.phone = error.response.data.errors.phone[0]; } else { errors.phone = ""; } this.setState({ errors: errors }); if (this.state.email == "" || this.validateEmail(this.state.email)) { errors.email = error.response.data.errors.email[0]; } else { errors.email = ""; } this.setState({ errors: errors }); }); } render() { return ( <React.Fragment> <Header /> <div className="container"> <div className="row"> <div className="col-12"> <h1 class="h3 mb-5 mt-5 text-center">予約フォーム</h1> <div className="form-group"> <span>予約日時</span> <input className="form-control" type="datetime-local" name="date" value={this.state.date} onChange={this.onDateChange} /> <p className="err-msg">{this.state.errors.date}</p> </div> <Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary"> 送信する </Button> </div> </div> </div> </React.Fragment > ); } } export default Reservation;
- 投稿日:2020-09-13T02:59:32+09:00
Laravel 8 ルーティングで「Target class [コントローラ名] does not exist」がでたときの対処法
Laravel 8 をインストールして、とりあえずCRUDシステム作って8系がどんな感覚か確かめようとして、すぐにつまづいた…(; ・`д・´)
エラーがでたのはルーティングが正しく記述されていなかったのが理由でした。注意すべきルーティングの設定
Laravel8のルーティングは今までのルーティングと記述の仕方が少しことなります。https://readouble.com/laravel/8.x/ja/routing.html
Laravel8のルーティングを定義する場合、
web.phpRoute::resource('/blogs', BlogController::class);こんな感じで記述しています。
ここで注意しなければならないのが、ルート定義ファイル
web.php
の冒頭にルート定義で使用されているコントローラの名前空間をuseキーワードを使用して書くということです。上記の例でいえば
web.php<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\BlogController; Route::resource('/blogs', BlogController::class);BlogControllerクラスを使用しているならば、
use App\Http\Controllers\BlogController;
と記述してインポートしてください。これを書き忘れると、「Target class [コントローラ名] does not exist」とエラーがでてしまいます。上記ではリソースフルなルート設定をしましたが、普通にルーティングする場合は、
web.php<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\BlogController; Route::get('/blogs', [BlogController::class, 'index']);第2引数のところは配列になっていますのでご注意ください。もちろん、この記述でも
use App\Http\Controllers\BlogController;
を忘れずに!
- 投稿日:2020-09-13T01:51:01+09:00
Laravel8を試したら即効でエラー「Target class [〇〇〇Controller] does not exist.」が表示された
はじめに
最近になってLaravel8がリリースされたと聞いて、とりあえず動かしてみようと思ったら開始早々にエラーが発生しました。
web.php
でPostController
を呼び出す処理を書いたのに上記エラーが発生。Laravel6ではちゃんと呼び出せたのに
そのときのweb.php
はこちら。web.php<?php use Illuminate\Support\Facades\Route; Route::get('/', 'PostController@index');原因調査したら、どうやらLaravel8の変更点の一つである「
RouteServiceProvider.php
ファイルからデフォルトの名前空間を削除」したのが大きく関係していると判明!Laravel7.xのプロジェクトからLaravel8.xに移行した場合は、この変更点の影響は受けない。Laravel8で作成した新規プロジェクトは変更点を考慮する必要があるそうです。RouteServiceProvider.phpの変化
Laravel7までは、
RouteServiceProvider.php
には次のコードが含まれていました。RouteServiceProvider.phpprotected $namespace = 'App\Http\Controllers'; Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php'));webミドルウェアと
App\Http\Controllers
の名前空間を使用して、routes/web.php
にルートをロードするよう指示しています。web.php// Laravelは、App\Http\Controllers\PostControllerを検索します Route::get('/', 'PostController@index');
Laravel8では、$namespace変数が削除され、Route宣言が次のように変更されました。
RouteServiceProvider.phpRoute::middleware('web') ->group(base_path('routes/web.php'));
App\Http\Controllers
の名前空間を使用していないです。web.php// Laravelは、App\Http\Controllers内でコントローラーを検索しません Route::get('/', 'PostController@index');このあと、どう修正すればよいのか解決方法を3つご紹介します。
解決方法
RouteServiceProvider.php
で名前空間を手動で追加するweb.php
で完全な名前空間を使用するweb.php
でアクション構文を使用するRouteServiceProvider.phpで名前空間を手動で追加する
Laravel7.x以前と同じ方法です。
RouteServiceProvider.phpprotected $namespace = 'App\Http\Controllers'; //追加 public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::middleware('web') ->namespace($this->namespace) //追加 ->group(base_path('routes/web.php')); Route::prefix('api') ->middleware('api') ->namespace($this->namespace) //追加 ->group(base_path('routes/api.php')); }); }web.phpで完全な名前空間を使用する
コントローラーの名前の前に名前空間を追加します。
例として、app/Http/Controllers
フォルダ内のPostController
を呼び出したいときは、次の通り記載します。web.phpRoute::get('/', 'App\Http\Controllers\PostController@index');web.phpでアクション構文を使用する
web.phpuse Illuminate\Support\Facades\Route; use App\Http\Controllers\PostController; Route::get('/', [PostController::class, 'index']);配列で使用したいクラスとメソッドを指定します。
具体的に配列内の値は
PostsController :: class -> App \ Http \ Controllers \ PostsControllerを返している
index -> PostController.phpのindexメソッドを呼び出している終わりに
Laravel開発で初めて遭遇したエラーでしたので勉強になりました。
ただ、Laravel8の新機能・変更点を理解してから使用してみようと思いました。理解せず使用すると痛い目に遭いますね参考
- 投稿日:2020-09-13T00:24:03+09:00
Laravelでneo4jを使う④マイグレーション
事前準備
・app/database/labelsフォルダの作成
$ php artisan neo4j:make:migration create_race_label
$ php artisan neo4j:migrate
コマンド 説明 $label->unique('email') プロパティに一意の制約を追加する $label->dropUnique('email') プロパティから一意の制約を削除する $label->index('uuid') プロパティにインデックスを追加する $label->dropIndex('uuid') プロパティからインデックスを削除しています コマンド 説明