- 投稿日:2020-12-06T22:34:31+09:00
laravelでゲストログイン機能を作成
今やポートフォリオには必須の機能とされているゲストログイン機能を作成しました
環境
PHP 7.3.11
Laravel Framework 7.29.3今回はLoginControllerをいじっていきます
LoginController.php//省略// public function guestLogin() { $name = 'ゲスト'; $password = 'guestpass'; if(Auth::attempt(['name' => $name, 'password' => $password])) { return redirect('/home'); } return redirect('/'); }Auth::attemptは引数に指定したレコードがDB内にあればtrue,そうでなければfalseを返す
今回は予めゲストログイン用のユーザーアカウントを作成しておき名前とパスワードをname,passwordに代入、
Auth::attemptメソッドの引数に指定すればもちろんtrueが帰ってくるので認証が成功し
/homeにリダイレクトされるといった感じです。web.phpRoute::get('/login/guest', 'Auth\LoginController@guestLogin');/login/guestを踏めばguestLoginメソッドが実行されます
guestLoginComponent.vue<template> <div> <a class="nav-link" @click="openModal">ゲストログイン</a> <div class="overlay" @click="closeModal" v-if="showContent"> <div class="dialog" @click="stopEvent"> <p class="borderbottom pt-2 pb-4">ゲストユーザーとしてこのサイトをお試しになれます</p> <p class="text-right pr-2"> <a @click="closeModal" class="mr-4 black">キャンセル</a> <a href="/login/guest" class="ml-auto">ログイン</a> </p> </div> </div> </div> </template> <script> export default { data() { return { showContent: false } }, methods:{ stopEvent(){ event.stopPropagation() }, openModal(){ this.showContent = true }, closeModal(){ this.showContent = false } } } </script>今回はゲストログインボタンを押したらダイアログが表示され「ログイン」をクリックしたらメソッドが発火するというデザインにしてみました。
- 投稿日:2020-12-06T21:50:05+09:00
【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン
概要
docker(docker-compose)でLEMP環境(PHP/nginx/MySQL)を構築し、Laravelの新規プロジェクト作成、構築した環境を破棄してから環境の再構築までをハンズオン形式で行います。
ご注意
記事を寄稿したタイミングでは、補足の解説は特になくコマンドだけのご紹介だったので、コマンド部分のみコピペすれば20分で終わる内容でした。
しかし、継ぎ足しで解説を追記していった結果、普通に読みながら進めていくと2時間ぐらいのボリュームになってしまってます?ご了承下さい?♂️
更新履歴
- 2020/09/09 Laravel8(当時はLaravel6が出た頃)がリリースされ、こちらの記事も内容が古くなったので全て書き直しました。
目的
Dockerの細かい概念などはすっ飛ばして、
このハンズオンの通りに進めればDocker×Laravelの環境を構築できます。
習うより慣れろで行ってみましょう!ハンズオン終了時のGitHub
https://github.com/ucan-lab/docker-laravel-handson
ハンズオンイベント
【ぺちオブ】ゆーきゃんプレゼンツ!Laravel + Docker 環境構築ハンズオン
https://phper-oop.connpass.com/event/137152
あの有名なぺちオブのイベントとしてハンズオン講師をやらせていただきました!
ありがとうございます?レビュワー
- hiroさん @hirodragon
- かひろくん @kahirokunn
- もっぷ先生 @kobayashi-m42
- こはさん @smspp
スペシャルサンクス?♂️?
前提条件
- Mac基準ですが、Windowsでも動作することを確認済み
- コマンドは
[環境] $とprefixを付けてます。- 所要時間は事前準備部分を除いて20分を目安としてます。
- エディタはファイル編集時にお好みのもので開いて作業ください?
構築環境(Dockerイメージ、各種バージョン)
- php: 7.4-fpm-buster
- composer: 1.10
- nginx: 1.18-alpine
- mysql: 8.0
- Laravel: 8.x
dockerおすすめ書籍
冒頭で習うより慣れろと言いましたが、習っておくのもそれはそれで良いことなのでオススメの書籍や記事をご紹介します。私はこの辺りの記事や本を読んで勉強したのでオススメしてます。
マンガでわかるdockerシリーズ
@llminatoll さんがDockerについてマンガで分かりやすく説明されてます。
初めての方はこちらのマンガでDockerの概念を学んでから進めると理解しやすいです。dockerおすすめ記事・サイト
公式系
記事
スライド
- Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
- Dockerfileを改善するためのBest Practice 2019年版
- Dockerfile を書くためのベストプラクティス解説編
事前準備
Windowsユーザー
Git Bashのインストールをお願いします。(Linuxコマンドをそのまま打てるようになります)
Git Bash を起動した際は下記のコマンドを実行ください。
$ exec winpty bashGitHubアカウントの作成
Gitの初期設定
user.emailとuser.nameが設定されていればok!
Gitの初期設定を行ってない場合、下記の記事を参考に初期設定をお願いします。最終的に下記のように表示されればokです。
$ git config --list | grep user user.email=xxx@yyy.com user.name=zzzGitHub SSH接続設定
$ ssh -T github.com Warning: Permanently added 'github.com,52.69.186.44' (RSA) to the list of known hosts. Hi ucan-lab! You've successfully authenticated, but GitHub does not provide shell access.
successfully authenticatedの文字が出ればok!
Warningは気にしなくてok!GitHub SSH接続設定を行ってない場合、下記の記事を参考に初期設定をお願いします。
もしくはハンズオンをHTTPSに置き換えて進めてください。docker, docker-compose のインストール
[Mac] Docker for Macをインストール
[Windows] Docker for Windowsをインストール
イントール確認
[mac] $ docker --version Docker version 19.03.12, build 48a66213fe [mac] $ docker-compose --version docker-compose version 1.26.2, build eefe0d31Docker起動確認
インストールしたDockerを起動してください。
running状態になっていればokです。
ここまでで事前準備終了です。
今回のハンズオンのゴール
- 3層アーキテクチャのコンテナの構築
- ウェブサーバー(web)
- nginxで静的コンテンツ配信サーバを構築
- アプリケーションサーバー(app)
- nginxを経由してPHPを動作させるアプリケーションサーバを構築
- PHPパッケージ管理ツールComposerのインストール
- データベースサーバー(db)
- MySQLデータベースサーバーの構築
- Laravelをインストールしてwelcome画面の表示
- LaravelとMySQLを連携し、マイグレーションを実行
- Docker環境の破棄
- Docker環境をGitHubから再構築
?【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン?
- 所要時間: 20分目安
作業ディレクトリを作成
[mac] $ mkdir docker-laravel-handson [mac] $ cd docker-laravel-handson最終的なディレクトリ構成
. ├── README.md (この名前にするとGitHubで見た時にHTMLに変換して表示してくれる) ├── infra (*1) │ ├── mysql (*1) │ │ ├── Dockerfile │ │ └── my.cnf (*1) │ ├── nginx (*1) │ │ └── default.conf (*1) │ └── php (*1) │ ├── Dockerfile (この名前にするとファイル名の指定を省略できる) │ └── php.ini (*1) ├── docker-compose.yml (この名前にするとファイル名の指定を省略できる) └── backend (*1) └── Laravelをインストールするディレクトリこのディレクトリ構成を目指します。
(*1)任意の名前に変更してもokです。リモートリポジトリを作成
リポジトリ名
docker-laravel-handsonのGitHubリモートリポジトリを作成する。リモートリポジトリへ初回コミット&プッシュ
[mac] $ echo "# docker-laravel-handson" >> README.md [mac] $ git init [mac] $ git add README.md [mac] $ git commit -m "first commit" [mac] $ git branch -M main※ 2020/10/1 からデフォルトブランチがmasterからmainに変更されました。
リモートリポジトリを登録します。
リモートリポジトリ先は適宜置き換えてください。[mac] $ git remote add origin git@github.com:ucan-lab/docker-laravel-handson.gitリモートリポジトリ先が正しく設定されていることを確認してください。
[mac] $ git remote -v origin git@github.com:ucan-lab/docker-laravel-handson.git (fetch) origin git@github.com:ucan-lab/docker-laravel-handson.git (push) # もしリモートリポジトリ先を間違えた場合は下記のコマンドから変更できます。 [mac] $ git remote set-url origin <リモートリポジトリ>リモートリポジトリ先の名前を
originと付けられていること。
リモートリポジトリ先のURL等に間違いがないことを確認する。GitHubへpushします。
[mac] $ git push -u origin main
-uオプションは--set-upstreamの省略
- ローカルリポジトリの現在のブランチ(main)をリモートリポジトリ(origin)のmainにpush先を設定
- 以降は
git pushだけでgit push origin mainと同義GitHubのリモートリポジトリのmainブランチへpushされていることを確認します。
お好みのGUIエディタで開く
ymlファイルをCUIエディタで書いていくのは大変なのでお好みのGUIエディタ(今回はVSCode)で開きます。
codeコマンドがインストールされていれば、簡単にプロジェクトをVSCodeで開けます。[mac] $ code .また、
control+shift+@で統合ターミナルをVSCodeで開くことができます。
ファイルの編集もコマンドの実行もVSCodeだけで行えます。アプリケーションサーバ(app)コンテナを作る
PHPアプリケーションサーバコンテナを作成します。
公式のPHP-FPMイメージをベースイメージとしてカスタマイズします。ディレクトリ構成
. ├── infra │ └── php │ ├── Dockerfile │ └── php.ini # PHPの設定ファイル ├── backend # Laravelをインストールするディレクトリ └── docker-compose.ymldocker-compose.yml を作成する
touchコマンドで空ファイルを作成してますが、作成後はお好みのエディタで編集ください。
ファイル名のタイプミス防止のため空ファイルだけ作成してます。
当ハンズオンはこの流れで進めていきます。[mac] $ touch docker-compose.yml作成した
docker-compose.ymlを下記の通りに編集します。docker-compose.ymlversion: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。docker-compose.yml: 設定値の補足
docker-compose.ymlversion: "3.8"Docker Composeファイルのバージョンを指定しています。
https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/compose-versioning/
通常はメジャー番号とマイナー番号を両方指定します。
ちなみにマイナーバージョンを指定しない場合はデフォルト0が使用されます。docker-compose.yml# 下記は同じ指定になる version: "3" version: "3.0"docker-compose.ymlservices: app: # => サービス名は任意 build: ./infra/php volumes: - ./backend:/workサービス名に
app(アプリケーションサーバー)の名前を付けて定義しています。
サービス名は任意に決められます。
build:で指定しているのはビルドコンテキストを指定します。
ビルドコンテキストとは、docker buildを実行する際の現在の作業ディレクトリのことをビルドコンテキスト(build context)と呼びます。
Dockerfileが置かれている./infra/phpディレクトリをビルドコンテキストとして指定します。
Dockerビルドの際はDockerfileのファイルを探すので、ファイル名の指定は不要です。
volumes:ではホスト側のディレクトリや名前付きボリュームをコンテナ側へマウントしたい時に指定します。
今回はホスト側の./backendディレクトリをappサービスのコンテナ内/workへマウントしてます。./docker/php/Dockerfile を作成する
- Composerコマンドのインストール
- Laravelで必要なPHP拡張機能のインストール
- bcmath, pdo_mysql が不足しているので追加インストール
[mac] $ mkdir -p infra/php [mac] $ touch infra/php/Dockerfile下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
infra/php/DockerfileFROM php:7.4-fpm-buster SHELL ["/bin/bash", "-oeux", "pipefail", "-c"] ENV COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/composer COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer RUN apt-get update && \ apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ docker-php-ext-install intl pdo_mysql zip bcmath COPY ./php.ini /usr/local/etc/php/php.ini WORKDIR /work./docker/php/Dockerfile: 設定値の補足
Dockerfileはテキストファイルであり、Dockerイメージを作り上げるために実行する命令をこのファイルに含めることができます。
各種Docker命令を解説します。
FROM php:7.4-fpm-busterhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#from
FROM命令はイメージビルドのためのベースイメージを設定します。
FROM イメージ名:タグ名で指定します。php | Docker Hub公式イメージをベースイメージとして利用します。
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#shell
SHELL命令は何も指定しない場合は
SHELL ["/bin/sh", "-c"]がデフォルト値となります(Linuxの場合)パイプ中のあらゆる段階でエラーがあれば失敗とするため、
pipefailオプションを明示的に指定してます。
-oオプションはオプションを設定するためのオプションです。
-eオプションを定義しておくと、そのシェルスクリプト内で何らかのエラーが発生した時点で、それ以降の処理を中断できます。
-uオプションを定義しておくと、未定義の変数に対して読み込み等を行おうとした際にエラーとなります。
-xオプションを定義しておくと、実行したコマンドを全て標準エラー出力に出してくれます。おまじないと思ってもらえればokです。SHELL命令は必須ではないです。
ENV COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/composerhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#env
ENV命令はコンテナ内のサーバー環境変数を設定します。
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composerhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#copy
RUN apt-get update && \ apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ docker-php-ext-install intl pdo_mysql zip bcmathhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#run
Debian系のパッケージ管理ツールは
apt-get,aptとありますが、Dockerfile内でaptを実行するとCLIインターフェース向けではないと警告が表示されるため、apt-getを使用します。
apt-get updateインストール可能なパッケージの「一覧」を更新します。
実際のパッケージのインストール、アップグレードなどは行いません。
apt-get -y install xxxLaravelのインストールに必要なパッケージをインストールします。
下記のパッケージをインストールしておけばokだと思います。
- https://packages.debian.org/ja/sid/git
- https://packages.debian.org/ja/sid/unzip
- https://packages.debian.org/ja/sid/libzip-dev
- https://packages.debian.org/ja/sid/libicu-dev
- https://packages.debian.org/ja/sid/libonig-dev
apt-get clean && rm -rf /var/lib/apt/lists/*ここはパッケージインストールで使用したキャッシュファイルを削除しています。phpの公式Dockerイメージには、
docker-php-ext-install,docker-php-ext-enable,docker-php-ext-configureのPHP拡張ライブラリを簡単に利用するための便利コマンドが予め用意されています。
docker-php-ext-install intl pdo_mysql zip bcmathPHPの拡張ライブラリをインストールしています。
- https://www.php.net/manual/ja/book.intl.php
- https://www.php.net/manual/ja/ref.pdo-mysql.php
- https://www.php.net/manual/ja/book.zip.php
- https://www.php.net/manual/ja/book.bc.php
COPY ./php.ini /usr/local/etc/php/php.inihttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#copy
WORKDIR /workhttps://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#workdir
./docker/php/php.ini を作成する
php.ini はPHPの設定ファイル
- PHPエラーメッセージの設定
- PHPエラーログの設定
- メモリ等の設定(お好みで)
- タイムゾーン設定
- 文字コード設定
[mac] $ touch infra/php/php.iniphp.inizend.exception_ignore_args = off expose_php = on max_execution_time = 30 max_input_vars = 1000 upload_max_filesize = 64M post_max_size = 128M memory_limit = 256M error_reporting = E_ALL display_errors = on display_startup_errors = on log_errors = on error_log = /dev/stderr default_charset = UTF-8 [Date] date.timezone = Asia/Tokyo [mysqlnd] mysqlnd.collect_memory_statistics = on [Assertion] zend.assertions = 1 [mbstring] mbstring.language = Japanesebuild & up
- appコンテナの作成
- PHPのバージョン確認
- Laravelで必要なPHP拡張機能の確認
[mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。docker-compose upはdocker-compose.ymlに定義したサービスを起動します。-d「デタッチド」モードでコンテナを起動します。
- デフォルトは「アタッチド」モードで全てのコンテナログを画面上に表示
- 「デタッチド」モードではバックグラウンドで動作
--buildコンテナの開始前にイメージを構築します
- 特に変更がない場合はキャッシュが使用されます。
[mac] $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcpdocker-laravel-handson_app_1 コンテナの State が Up になっていたら正常に起動している状態です。
docker-compose psコンテナ一覧を表示します。
- Name: コンテナ名
- Command: 最後に実行されたコマンド
- State: 状態(Up)はコンテナが起動している状態
- Ports: 9000/tcp(コンテナのポート)
- 9000番のホストポートは公開されていないので、コンテナの外からはアクセスできない
appコンテナ内ミドルウェアのバージョン確認(コンテナに入ってコマンド実行)
作成したappコンテナの中に入ってPHP, Composerのバージョン、インストール済みの拡張機能を確認します。
Laravel 7.xのサーバ要件に必要な拡張機能が入っていることを確認します。[mac] $ docker-compose exec app bash # PHPのバージョン確認 [app] # php -v PHP 7.4.6 (cli) (built: May 15 2020 13:01:21) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies # Composerのバージョン確認 [app] # composer -V Composer version 1.10.10 2020-08-03 11:35:19 # インストール済みの拡張機能の一覧 [app] # php -m [PHP Modules] bcmath Core ctype curl date dom fileinfo filter ftp hash iconv intl json libxml mbstring mysqlnd openssl pcre PDO pdo_mysql pdo_sqlite Phar posix readline Reflection session SimpleXML sodium SPL sqlite3 standard tokenizer xml xmlreader xmlwriter zip zlib [Zend Modules]
exitでコンテナの外に出ます。[app] $ exit
control+dでもコンテナから出られます。補足説明
appコンテナ内に入ってphpコマンドを実行しています。
docker-compose exec実行中のコンテナ内で、コマンドを実行します。appサービス名(コンテナ名)を指定します。PHPのバージョン確認(コンテナの外からコマンド実行)
コンテナの外から
phpコマンドを実行することもできます。
docker-compose exec [サービス名] [実行したいコマンド][mac] $ docker-compose exec app php -v PHP 7.4.6 (cli) (built: May 15 2020 13:01:21) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies結果にコミット
[mac] $ git status (use "git add <file>..." to include in what will be committed) docker-compose.yml infra/php/Dockerfile infra/php/php.ini [mac] $ git add . [mac] $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: docker-compose.yml new file: infra/php/Dockerfile new file: infra/php/php.ini [mac] $ git commit -m "feat create docker app container" [main f5a637f] feat create docker app container 3 files changed, 49 insertions(+) create mode 100644 docker-compose.yml create mode 100644 infra/php/Dockerfile create mode 100644 infra/php/php.ini [mac] $ git logウェブサーバー(web)コンテナを作る
nginxウェブサーバーコンテナを作成します。
nginxのベースイメージをそのまま利用します。ディレクトリ構成
. ├── infra │ └── nginx │ └── default.conf # nginxの設定ファイル ├── backend │ └── public # 動作確認用に作成 │ ├── index.html # HTML動作確認用 │ └── phpinfo.php # PHP動作確認用 └─── docker-compose.ymldocker-compose.yml へ追記する
- ポート転送の設定(今回は10080ポートにする)
- タイムゾーンの設定
version: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work # 追記 web: image: nginx:1.18-alpine ports: - 10080:80 volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。appコンテナの設定と同じインデントレベルで貼り付けます。docker-compose.yml: 設定値の解説
image: nginx:1.18-alpinehttps://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#image
コンテナを起動させるイメージを指定します。
nginx | Docker Hubを指定してます。
今回は公式のnginxイメージをそのまま利用しています。(Dockerfileは不要)ちなみにnginxは
1.10,1.12等の偶数のバージョンが安定バージョンになります。
特に理由がなければ偶数バージョンをご利用ください。ports: - 10080:80https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#ports
nginxへ外(ホスト側)からコンテナ内へアクセスさせるため公開用のポートを設定します。
ホスト側:コンテナ側と設定します。volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.confhttps://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#volumes
ホスト側にあるディレクトリ、ファイルをコンテナ内へマウントさせています。
docker/nginx/default.conf を作成する
[mac] $ mkdir infra/nginx [mac] $ touch infra/nginx/default.confLaravel公式にnginxの設定例が用意されているので、こちらを流用します。
https://readouble.com/laravel/7.x/ja/deployment.htmldefault.confserver { listen 80; server_name example.com; root /work/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass app:9000; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } }
root,fastcgi_passドキュメントルート設定を書き換えてます。
nginxの設定を詳しく知りたい方は下記の記事がオススメです。build & up
[mac] $ docker-compose down [mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。[mac] $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcp docker-laravel-handson_web_1 nginx -g daemon off; Up 0.0.0.0:10080->80/tcpdocker-laravel-handson_web_1 コンテナの State が Up になっていたら正常に起動している状態です。
また、
Portsの項目が、appコンテナは9000/tcpでwebコンテナは0.0.0.0:10080->80/tcpと表示形式が異なってます。
これはホスト上の10080番ポートをコンテナの80番ポートへ割り当てています。nginxのバージョン確認
[mac] $ docker-compose exec web nginx -v nginx version: nginx/1.18.0webコンテナの確認
- webコンテナの動作確認
- HTMLとPHPが表示されるか
[mac] $ mkdir backend/public [mac] $ echo "Hello World" > backend/public/index.html [mac] $ echo "<?php phpinfo();" > backend/public/phpinfo.phphttp://127.0.0.1:10080/index.html
「Hello World」が表示されることを確認する。
http://127.0.0.1:10080/phpinfo.php
phpinfoの情報が表示されることを確認する。
[mac] $ rm -rf backend/*確認用に作成したHTML, PHPファイルは不要なので削除します。
結果にコミット
[mac] $ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: docker-compose.yml modified: infra/php/Dockerfile Untracked files: (use "git add <file>..." to include in what will be committed) infra/nginx/default.conf [mac] $ git add . [mac] $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: docker-compose.yml new file: infra/nginx/default.conf modified: infra/php/Dockerfile [mac] $ git commit -m "feat create docker web container" [main 5e278c1] feat create docker web container 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 infra/nginx/default.conf [mac] $ git logLaravelをインストールする
- app コンテナに入り、Laravelをインストール
- welcomeページが表示されるか
[mac] $ docker-compose exec app bash [app] $ composer create-project --prefer-dist "laravel/laravel=8.*" . [app] $ php artisan -V Laravel Framework 8.0.0 [app] $ exit
composer create-project --prefer-dist
--prefer-distzipでダウンロードするため高速- composer の–prefer-distってよく使うけど何してる?
composer create-projectコマンドでメモリ足りない問題の対策artisanについて
- Laravelが用意しているコマンドラインインターフェイス
- https://readouble.com/laravel/7.0/ja/artisan.html
php artisan list使用可能なコマンド一覧を表示Laravel ウェルカム画面の表示
LaravelのWelcome画面が表示されることを確認する。
結果にコミット
[mac] $ git status [mac] $ git add . [mac] $ git status [mac] $ git commit -m "feat laravel install" [mac] $ git logデータベース(db)コンテナを作る
MySQLデータベースコンテナを作成します。
MySQLのベースイメージをそのまま利用します。参考記事
ディレクトリ構成
. ├── infra │ └── mysql │ ├── Dockerfile │ └── my.cnf # MySQLの設定ファイル └── docker-compose.ymldocker-compose.yml へ追記する
- データベース名やユーザー名等の接続情報とタイムゾーンの設定は環境変数で渡す
- トップレベルvolumeを使用してデータの永続化
version: "3.8" services: app: build: ./infra/php volumes: - ./backend:/work web: image: nginx:1.18-alpine ports: - 10080:80 volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work # 追記 db: build: ./infra/mysql volumes: - db-store:/var/lib/mysql volumes: db-store:
docker-compose.ymlファイルはインデント(半角スペース)が意味を持ちます。注意してコピペしてください。webコンテナの設定と同じインデントレベルでdbコンテナの設定を貼り付けます。volumesはトップレベル(servicesと同じレベル)に貼り付けます。- DockerのMySQLイメージ起動時に渡す環境変数
- Docker for Macのmount遅い問題まとめ
- Performance tuning for volume mounts (shared filesystems)
./docker/mysql/Dockerfile を作成する
[mac] $ mkdir infra/mysql [mac] $ touch infra/mysql/Dockerfile下記のコードを丸ごとコピーして Dockerfile へ貼り付けてください。
infra/mysql/DockerfileFROM mysql:8.0 ENV MYSQL_DATABASE=laravel_local \ MYSQL_USER=phper \ MYSQL_PASSWORD=secret \ MYSQL_ROOT_PASSWORD=secret \ TZ=Asia/Tokyo COPY ./my.cnf /etc/mysql/conf.d/my.cnf RUN chmod 644 /etc/mysql/conf.d/my.cnfコメントでいただいたのですが、Windows環境でボリュームマウントを行うと、ファイルパーミッションが777となるようです。
my.cnfに書き込み権限が付いてるとMySQLの起動時にエラーが発生します。
その対策としてボリュームマウントではなくDockerfileを作成してmy.cnfファイルコピー、読み取り専用に権限変更してます。docker/mysql/my.cnf を作成する
- 文字コードの設定
- タイムゾーンの設定
- ログ設定
[mac] $ mkdir infra/mysql [mac] $ touch infra/mysql/my.cnfmy.cnf[mysqld] # character set / collation character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # timezone default-time-zone = SYSTEM log_timestamps = SYSTEM # Error Log log-error = mysql-error.log # Slow Query Log slow_query_log = 1 slow_query_log_file = mysql-slow.log long_query_time = 1.0 log_queries_not_using_indexes = 0 # General Log general_log = 1 general_log_file = mysql-general.log [mysql] default-character-set = utf8mb4 [client] default-character-set = utf8mb4build & up
[mac] $ docker-compose down [mac] $ docker-compose up -d --build
docker-composeコマンドはdocker-compose.ymlがあるディレクトリで実行します。[mac] $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- docker-laravel-handson_app_1 docker-php-entrypoint php-fpm Up 9000/tcp docker-laravel-handson_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp docker-laravel-handson_web_1 nginx -g daemon off; Up 0.0.0.0:10080->80/tcpdocker-laravel-handson_db_1 コンテナの State が Up になっていたら正常に起動している状態です。
[mac] $ docker-compose exec db mysql -V mysql Ver 8.0.20 for Linux on x86_64 (MySQL Community Server - GPL)マイグレーション実行(エラーが発生します)
[mac] $ docker-compose exec app bash [app] $ php artisan migrate Illuminate\Database\QueryException SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = laravel and table_name = migrations and table_type = 'BASE TABLE') at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671 667▕ // If an exception occurs when attempting to run a query, we'll format the error 668▕ // message to include the bindings with SQL, which will make this exception a 669▕ // lot more helpful to the developer instead of just the database's errors. 670▕ catch (Exception $e) { ➜ 671▕ throw new QueryException( 672▕ $query, $this->prepareBindings($bindings), $e 673▕ ); 674▕ } 675▕ +37 vendor frames 38 artisan:37 Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) [app] $ exit
SQLSTATE[HY000] [2002] Connection refusedこのエラーはよく見るMySQLのエラーです。
MySQLに接続拒否されたエラーなので、この場合は大体MySQLへの接続設定に誤りがあります。
backend/.envのDB接続設定を修正する。[mac] $ vim backend/.env DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel_local DB_USERNAME=phper DB_PASSWORD=secret
backend/.env.exampleも同様にDB接続設定を修正しておきます。[mac] $ vim backend/.env.example DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel_local DB_USERNAME=phper DB_PASSWORD=secretマイグレーション実行(再実行)
[mac] $ docker-compose exec app bash [app] $ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.07 seconds) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (0.04 seconds) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds)試しにデータを作ってみる
[mac] $ docker-compose exec app bash [app] php artisan tinker $user = new App\Models\User(); $user->name = 'phper'; $user->email = 'phper@example.com'; $user->password = Hash::make('secret'); $user->save();[mac] $ docker-compose exec db bash [db] $ mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE [mysql] > SELECT * FROM users; +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ | id | name | email | email_verified_at | password | remember_token | created_at | updated_at | +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ | 1 | phper | phper@example.com | NULL | $2y$10$/cmcAIdF7twOntcazkn4/e61YAM2F99hCrOo.qRUz7cUa/ZydrbVS | NULL | 2020-08-24 17:09:53 | 2020-08-24 17:09:53 | +----+--------------------+-----------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ 1 rows in set (0.00 sec)結果にコミット
[mac] $ git status [mac] $ git add . [mac] $ git status [mac] $ git commit -m "feat create docker db container" [mac] $ git logGitHubにpush
[mac] $ git pushリモートリポジトリへpushされていることを確認します。
Docker環境の再構築
Docker環境の破棄
コンテナの停止、ネットワーク・名前付きボリューム・コンテナイメージ、未定義コンテナを削除
[mac] $ docker-compose down --rmi all --volumes --remove-orphans作業ディレクトリの削除
プロジェクトを削除するので、GUIエディタは閉じておきましょう。
[mac] $ cd .. [mac] $ rm -rf docker-laravel-handsonVisual Studio Code等のGUIエディタで開いている場合は、一度エディタを終了しましょう。
環境の再構築
GitHubからリポジトリをクローン
** 自身のリポジトリ先に適宜変更してください **
[mac] $ git clone git@github.com:ucan-lab/docker-laravel-handson.git [mac] $ cd docker-laravel-handson [mac] $ docker-compose up -d --build
/work/public/../vendor/autoload.phpを開くのに失敗してエラーになっていることを確認します。
git cloneが終わった状態ではappコンテナ内に/work/vendorディレクトリが存在しないためです。Laravelインストール
app コンテナに入ります。
[mac] $ docker-compose exec app bash
vendorディレクトリへライブラリ群をインストールします。
composer.lockファイルを参照します。[app] $ composer install
composer install時は.env環境変数ファイルは作成されないので、.env.exampleを元にコピーして作成します。[app] $ cp .env.example .env
.envにAPP_KEY=の値がないとこのエラーが発生します。
このコマンドでアプリケーションキーを生成できます。[app] $ php artisan key:generateWelcome画面が表示されることを確認します。
[app] $ php artisan migrate最後にマイグレーションを実行して成功すればハンズオン終了です。
お疲れ様でした?? コンテナを停止して終了してください。[app] $ exit [mac] $ docker-compose downオマケ
MySQLに接続したい
[mac] $ docker-compose exec db bash -c 'mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE}' mysql> show tables; +---------------------+ | Tables_in_homestead | +---------------------+ | migrations | | password_resets | | users | +---------------------+ 3 rows in set (0.00 sec) mysql> desc users; +-------------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | email | varchar(255) | NO | UNI | NULL | | | email_verified_at | timestamp | YES | | NULL | | | password | varchar(255) | NO | | NULL | | | remember_token | varchar(100) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +-------------------+---------------------+------+-----+---------+----------------+ 8 rows in set (0.01 sec)マイグレーションが実行されるとLaravel側で最初から用意されている
users,password_resetsのテーブルが生成されています。Laravelのログをコンテナログに表示する
backend/.envを修正する。LOG_CHANNEL=stderr
backend/routes/web.phpRoute::get('/', function () { logger('welcome route.'); return view('welcome'); });$ docker-compose logs # -f でログウォッチ $ docker-compose logs -f # サービス名を指定してログを表示 $ docker-compose logs -f appMySQLクライアントツールで接続したい
docker-compose.ymlのdbサービスに下記設定を追記して、コンテナを再起動して設定を反映してください。ports: - 33060:3306dbコンテナへのポート転送設定がないとホストからアクセスが行えません。
MySQLクライアントツールは「Sequel Ace」がオススメです。Windowsエラーメモ
Error response from daemon
$ docker-compose up -d --build docker: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).解決策をいくつか
- Docker For Windowsを再起動する
- Dockerでpullやrunを行ったときイメージダウンロードができなくなった問題の解決法
- Proxy環境下のDockerトラブルシューティング
- さわって理解する Docker 入門
マイグレーションエラーの補足
Host '172.27.0.2' is not allowed to connect to this MySQL server
$ php artisan migrate Illuminate\Database\QueryException : SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server (SQL: select * from information_schema.tables where table_schema = homestead and table_name = migrations and table_type = 'BASE TABLE') at /work/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("SQLSTATE[HY000] [1130] Host '172.27.0.2' is not allowed to connect to this MySQL server") /work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=db;port=3306;dbname=homestead", "homestead", "secret", []) /work/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details.このエラーが発生した場合は
my.cnfを作成する前にdocker-compose up -dでビルドしてしまった可能性が高いです。$ docker-compose down --volumes --rmi all $ docker-compose up -d --build設定ファイルがない状態でMySQLの初期化が行われたでデータが永続化されてしまってるので一度ボリューム毎削除してビルドし直せばokです。
docker volumes の共有で Permission denied (SELinux) 問題
CentOS 等の場合 volumes でホストと共有したファイル(ディレクトリ)にアクセスできないことがあります。
書き込み権限を与える
$ chmod -R 777 logs $ chmod -R 777 src/storage/logsログディレクトリはホスト側からもコンテナ側からも書き込みするので、書き込み権限を付与しておきましょう。
応用
より実用的な構成のdocker構成の記事を書いてますので、よかったらこちらの記事も読んでもらえると嬉しいです。
- 投稿日:2020-12-06T21:13:34+09:00
[Laravel][HTML] 戻るボタンを作って動かすとバリデーションが働いてしまう場合の対処
状況
Laravelに限った話ではないがLaravelを利用しているときに発生したことなので一応。
●こんな感じで書くと、普通のメニュー画面みたいなところでは問題なく動くが、anime-suko.php<a href="/menu"><button>戻る</button></a>これをフォームの中に書くと、今の画面に移動するわけではないのに何も入力しなくても勝手にバリデーションが発生してしまって移動してくれない。
解決方法
buttonタグのtypeをbuttonにする。こうしないと、HTMLの仕様的にこうしないと、type="submit"の扱いになるHTMLの仕様らしい。
anime-suko.php<a href="/menu"><button type="button">戻る</button></a>
- 投稿日:2020-12-06T20:29:28+09:00
LaravelでRequestクラスを作成したら、403エラー
概要
Requestクラスを作成し、バリデーション用コード書き、コントローラーに適応させた!!
さて、ブラウザでフォーム入力行いボタンを、ポチー!!
「ファーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーwww」
ここまでが、僕のリクエストクラス作成のルーティンです。(毎回やっている。。。)戒めも込めて記事にします。
結論
falseをtureに書き換える!!これだけ!!
class TestRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; ←ここがデフォルトではfalseになっている。 }これからは絶対、忘れないようにしよう!絶対に!
- 投稿日:2020-12-06T20:10:52+09:00
お前らのLike検索は間違っている (QueryBuilder/Eloquent)
煽りタイトルすみません。
Laravelタグをつけてはいますが、特定の言語/フレームワークに限らない、Like検索時のエスケープの注意点についてお話します。
(@wand_ta 指摘ありがとうございます! 修正しました)本記事の対象読者
- 安易に以下のようなコードを書いてしまう方
$query->where('name', 'like', "%{$keyword}%")addcslashes? ナニソレ? 旨いの? って方- 1つのバックスラッシュを含むデータを検索するためにコードでは8個のバックスラッシュを書かなければならない場合があることに驚きを隠せない方
DB::select("SELECT * FROM items WHERE name LIKE '%\\\\\\\\%'")よくある間違い
商品テーブルに「100%りんご」という名前のデータが登録されているとします。
あなたの実装した部分一致検索APIは「%」を検索キーワードに指定して正しく検索できるでしょうか?
また、「 _ 」や「 \ 」を指定して正しく検索できるでしょうか?間違った実装
商品テーブルに対し、指定されたキーワードをSQLのLIKEを使って部分一致で検索するAPIを作るものとします。
とても素朴に、'%' . $keyword . '%'のような感じでリクエストパラメタから得たキーワードの両端に%をつけてLIKE検索した実装をしました。app/Http/Controllers/ItemController.phpclass ItemController extends Controller { public function index(Request $request): JsonResponse { $keyword = $request->input('keyword') ?? ''; // キーワードで商品名を検索 ***間違った実装*** $rows = Item::where('name', 'like', '%' . $keyword . '%')->get(); return response()->json($rows); } }何がまずいか?
テーブル
まず、こんな商品テーブルがあるとします。
商品名として「%」や「_」や「\」といった文字が含まれています。itemsテーブルmysql> SELECT * FROM items; +----+---------------+ | id | name | +----+---------------+ | 1 | 100%りんご | | 2 | abc_コーラ | | 3 | CC\1000 | +----+---------------+ 3 rows in set (0.00 sec)curlで確認 (意図した挙動をしない)
先程のLaravel APIにキーワードとして「100%」を指定して検索してみましょう。
「100%りんご」がヒットすることを期待しますが、
実際には「CC\1000」という商品もヒットしてしまいました。
※curlコマンドでは、キーワード「100%」をURLエンコードした?keyword=100%25というクエリ文字列が指定されています。# 「100%」を含む商品を探したいが。。。 % curl -s 'http://localhost/api/items?keyword=100%25' | jq . [ { "id": 1, "name": "100%りんご" }, { "id": 3, "name": "CC\\1000" # ※「CC\1000」のエスケープ文字列表現 } ]また、今度はキーワード「\」を含む商品を探したいとき、「CC\1000」がヒットすることを期待しますが、実際には何もヒットしません。
※キーワード「\」をURLエンコードした?keyword=%5cというクエリ文字列が指定されています。# 「\」を含む商品を探したいが。。。 % curl -s 'http://localhost/api/items?keyword=%5c' | jq . []解説
前置き
- 本記事は主にLIKE演算子のワイルドカードのエスケープについて説明します
- 照合順序、Locale等については言及しません
- 大文字小文字、バイナリ比較、ロケールに応じた照合、など
- いわゆる「全文検索」機能、形態素解析等については言及しません
- いわゆるSQL組み立てにおけるエスケープ、SQLインジェクションといったセキュリティについては直接言及しません。
- 本記事は以下の環境で検証しています。
- Laravel 7.4
- MySQL 8.0
- PostgreSQL 11.5
- 他のバージョン、他のDB(Oracle, SQL Serverなど)、他の言語/フレームワークでも基本的な考え方は同じですが、具体的な差異については言及しません。
SQLで検索するとき
1. 単純なLIKE検索
単純に「100」という文字列を検索したい場合は、ワイルドカード
%で挟んで検索パターン文字列を作り、LIKE演算子で比較します。単純なLIKE検索-- 「100」を含むものを探す mysql> SELECT * FROM items WHERE name LIKE '%100%';結果+----+---------------+ | id | name | +----+---------------+ | 1 | 100%りんご | | 3 | CC\1000 | +----+---------------+ 2 rows in set (0.00 sec)2. ワイルドカード文字そのものを検索したいとき (要エスケープ)
文字としての
%を検索したいときは\でエスケープして、\%としてパターン文字列を指定します。
部分一致で探したいので、前後を%で挟むと%\%%というパターン文字列になります。
このパターン文字列をLIKE演算子の右辺で指定するときに、シングルクォートで囲んであげましょう。
この際、クォート文字列のリテラル表現のために、さらに\をエスケープして\\とする必要があります。
結果として、クォート文字列表現としては'%\\%%'となります。エスケープしてLIKE検索-- 「%」を含むものを探す: 間違った例 mysql> SELECT * FROM items WHERE name LIKE '%%%'; -- 「%」を含むものを探す: 正しい例 (\でエスケープする) mysql> SELECT * FROM items WHERE name LIKE '%\\%%';結果+----+---------------+ | id | name | +----+---------------+ | 1 | 100%りんご | +----+---------------+ 1 row in set (0.00 sec)同様に、文字としての「_」や「\」を探したいときもエスケープが必要です。
※「\」が増殖しているところに注目-- 「_」を含むものを探す: 正しい例 (\でエスケープする) mysql> SELECT * FROM items WHERE name LIKE '%\\_%'; -- 「\」を含むものを探す: 正しい例 (\でエスケープする) mysql> SELECT * FROM items WHERE name LIKE '%\\\\%';LIKE検索パターンのエスケープまとめ
- LIKE検索パターン文字列中では、
%や_は ワイルドカード と呼ばれる特殊な役割を持ち、これらの文字列そのものを示したい場合はエスケープする必要があります- エスケープ文字は LIKE演算子の
ESCAPE句で指定可能ですが、多くのDBでは省略した場合はデフォルトは\です。- また、
\文字そのものを示したい場合も同様にエスケープする必要があります
文字 検索パターン内での役割 エスケープ後文字列 % (パーセント) 任意の長さの文字列(空文字列を含む) \% _ (アンダースコア) 任意の1文字 \_ \ (バックスラッシュ) エスケープ文字 \\ MS SQL Server のLIKE検索パターンのエスケープ
Microsoft SQL Serverは少し特殊で、文字クラス
[]を利用してメタ文字そのものを表現します。
文字 検索パターン内での役割 MS SQL Serverでのエスケープ後文字列 % (パーセント) 任意の長さの文字列(空文字列を含む) [%] _ (アンダースコア) 任意の1文字 [_] [ 文字クラスの開始 [[] SQLのシングルクォート リテラル表現でのメタ文字のエスケープまとめ
SQL中で文字列をリテラル表現する場合はシングルクォートで囲む必要があります。
例: 文字列abc=> クォート表現'abc'この際に シングルクォートそのものなどのメタ文字を含む場合はエスケープ文字として
\でエスケープする必要があります。
また、エスケープ文字\を含む場合も同様にエスケープします。
例: 文字列'=> クォート文字列表現'\'
例: 文字列\=> クォート文字列表現'\\'
文字 SQL文での役割 エスケープしたクォート文字列表現 ' (シングルクォート) 文字列の開始/終了 '\'' \ (バックスラッシュ) エスケープ文字 '\\' 正しいLike検索の実装
エスケープを意識して検索APIの実装を見直し
API実装
LIKE検索パターンのためにワイルドカード文字、及びエスケープ文字をそれぞれエスケープしてあげます。
phpの addcslashes 関数で指定文字をバックスラッシュでエスケープできます。
(THX @wand_ta)app/Http/Controllers/ItemController.phpclass ItemController extends Controller { public function index(Request $request): JsonResponse { $keyword = $request->input('keyword') ?? ''; // キーワードのメタ文字をエスケープして商品名を検索 $pat = '%' . addcslashes($keyword, '%_\\') . '%'; $rows = Item::where('name', 'LIKE', $pat)->get(); return response()->json($rows); } }※↑キーワードが空だったらエラーにする、結果を返さない、(Like条件で絞り込みせずに)全件を返す、 など実際には仕様に応じて実装すると思いますが、単純化のためここでは簡易な実装にしています。
なお、MS SQL Server の場合は以下のようにする必要があるかもしれません。(未検証)
また、preg_replace()の第1引数は正規表現を指定しますので、ここでもさらにエスケープしています。
もちろん、シングルクォート内の「\」をエスケープしています。SQLServerの場合$pat = '%' . preg_replace('/([\\[_%])/', '[$1]', $keyword) . '%';curlで確認
こんどは意図した検索ができるようになりましたね!
# 「100%」を含む商品を探して「100%りんご」がヒットする % curl -s 'http://localhost/api/items?keyword=100%25' | jq . [ { "id": 1, "name": "100%りんご" } ] # 「\」を含む商品を探して「CC\1000」がヒットする % curl -s 'http://localhost/api/items?keyword=%5c' | jq . [ { "id": 3, "name": "CC\\1000" } ]リファクタリング: likeスコープを提供するTrait
コントローラに複雑な処理を書きたくないので、Model側に処理を移しましょう。
Trait実装
以下のようなscopeを提供するトレイトを作成し、
app/Models/Traits/WhereLike.phpnamespace App\Models\Traits; trait WhereLike { // 部分一致検索 public function scopeWhereLike($query, string $column, string $keyword) { return $query->where($column, 'like', '%' . addcslashes($keyword, '%_\\') . '%'); } // 前方一致検索 public function scopeWhereLikeForward($query, string $column, string $keyword) { return $query->where($column, 'like', addcslashes($keyword, '%_\\') . '%'); } }ModelでTrait利用
商品モデルで先程のトレイトをuseします。
app/Models/Item.phpnamespace App\Models; use App\Models\Traits\WhereLike; use Illuminate\Database\Eloquent\Model; class Item extends Model { use WhereLike; }tinkerで確認
これで、Itemモデルに部分一致検索
whereLike()や前方一致検索whereLikeForward()といったクエリビルダ用メソッドが追加されました。tinker# 名前に「%」を含むものを検索 >>> Item::whereLike('name', '%')->get() => Illuminate\Database\Eloquent\Collection {#4324 all: [ App\Models\Item {#4317 id: 1, name: "100%りんご", }, ], } # 名前に「100」を含むものを検索 >>> Item::whereLike('name', '100')->get() => Illuminate\Database\Eloquent\Collection {#4326 all: [ App\Models\Item {#4318 id: 1, name: "100%りんご", }, App\Models\Item {#4328 id: 3, name: "CC\1000", }, ], } # 名前を「100」で前方一致検索 >>> Item::whereLikeForward('name', '100')->get() => Illuminate\Database\Eloquent\Collection {#4312 all: [ App\Models\Item {#4316 id: 1, name: "100%りんご", }, ], }おまけ: プログラム内で生SQLを利用するときのエスケープ
クエリビルダ/ORマッパーを利用せずに、生SQLを利用したい場合が稀にあります。
※もちろんそのような場合でも、バインド値・プレースホルダなどの安全に利用できる仕組みを極力活用すべきです。
- 再帰クエリ、JSON系の演算子、その他特殊な構文などDB固有の特殊な機能を使いたい場合
- DB固有のDDLを実行したい場合
- PostgreSQLの継承テーブルなどの都合で動的にテーブル名を導出したい場合
- 複雑なサブクエリで、クエリビルダ/ORマッパーでの組み立てがやりづらい場合
- 開発、検証中の一時的な仮実装
生SQLではLIKE文に限らずセキュリティの観点からエスケープを特に注意ください。
以下は、生SQLでLIKE文を書いた時のエスケープの例です。
※呆れるほど「\」が増殖しているところに注目tinker# 名前に「\」を含むものを検索 >>> DB::select("SELECT * FROM items WHERE name LIKE '%\\\\\\\\%'"); => [ {#4334 +"id": 3, +"name": "CC\1000", }, ]
- 検索対象としての「\」
- → LIKE検索パターンにするためにエスケープ → 「\\」 2倍!
- → SQLのクォート文字列表現のためのエスケープ → 「\\\\」 4倍!!
- → phpのクォート文字列表現のためのエスケープ → 「\\\\\\\\」 8倍!!!!
そうです、見ての通り バックスラッシュは増えるんです。
※ちなみに、Qiitaのこの記事を書くときに Qiita Markdownのためにエスケープするので、私は↑で16個の連続したバックスラッシュを書いています。※もちろん、Productionコードで↑のような実装をしてはいけませんし、このような実装が必要な場面は考えにくいですが、あえて想像するなら、seederの実装内でシードデータを別テーブルの情報から何かしら探して使うとき、複雑なサブクエリが必要で、SQLターミナルで検証したクエリをそのまま流用して一時的にコードで使う、というケースでしょうか。。。
最後に
いっぱいバックスラッシュを書いた。
それはともかく良い年末を!
- 投稿日:2020-12-06T16:19:06+09:00
Vueソーシャルシェアリングのバージョン指定
- 投稿日:2020-12-06T14:08:17+09:00
[2020年12月]Herokuにlaravelアプリケーションをデプロイする方法
herokuにLaravelアプリをデプロイする方法について解説します!
前提
①アカウント登録ができていること
②heroku CLIをインストール済であること
公式のガイドを元にherokuのコマンドラインを打てるようにしておきましょう。②2020年12月時点での情報
過去のherokuデプロイ系の記事を見つつ実際にデプロイしたところ、細かい手順で変更があるのを確認しました。
今後も変更があるかもしれないので、うまくいかないことがあったら
公式リファレンスを確認しましょう。リファレンスページ上部の最終更新履歴(↓画像の赤枠部)が新しくなっていたら、リファレンスの方法に沿ってすすめるのがイイかと!
ざっくり手順を解説
- laravelのプロジェクト作成
- gitリポジトリを作成
- ProcFile作成
- herokuアプリケーションとして登録
- herokuへpushする
- config(設定)を行う
- DB設定をする
- マイグレーション実行
既にアプリをgithubにpushして開発している方は、手順3ProcFile作成から参照してください。
では、やっていきましょう!方法
手順1 laravelプロジェクトの作成
composerコマンドでlaravelのプロジェクトを作成します。
自分の場合はver8のLaravelとlivewireというライブラリを触りたかったのでlivewire-applicationという名前にしました。composer create-project laravel/laravel livewire-application --prefer-dist "8.0.*"手順2 gitリポジトリを作る
herokuにはgithubへpushしたコードを毎日自動デプロイしてくれるなど、便利な機能があります。
逆にいえば、herokuにデプロイするにはgitを使う必要があるということ。なので、Laravelのプロジェクトを自分のリポジトリにする必要があります。
githubのアカウントページで新しいリポジトリを作成し、pushしましょう。
cd livewire-application/src git init git add . git remote add origin https://github.comユーザー名/リポジトリ名 git commit -m "new livewire project"これで自分のgithubリポジトリにlaravelプロジェクトが作成されているはず!
手順3 ProcFile作成
Procfileは「あなたのアプリがデプロイされているherokuサーバにアクセスがあった時に、どこを参照するか?」を設定するファイルです。これをLaravel用に設定しましょう。
echo "web: vendor/bin/heroku-php-apache2 public/" > Procfile git add . git commit -m "Procfile for Heroku"上記の通り、Procfileは公開しても問題ないのでgitコミットしてます。
設定について解説すると、
Laravelリファレンス/設定/Publicディレクトリより
Laravelをインストールできたら、Webサーバのドキュメント/Webルートがpublicディレクトリになるように設定してください。
と書かれている通り、Laravelのアプリは
publicディレクトリがアクセスのエンドポイントになります。
heroku側の初期設定がpublicになってないので、設定を変更しているワケですね。手順4 herokuアプリケーションとして登録
Procfileを作成した場所で、そのままheroku createコマンドを打ちます。hirog:livewire-application hirog_engineer$ heroku create Creating app... done, ⬢ shallow-waters-42026 https://Protected-waters-42026.herokuapp.com/ | https://git.heroku.com/shallow-waters-42026.gitざっくりいうと、これはgitコマンドの
git initと同じです。
「この場所を起点に、これからherokuのアプリを新規登録しますよ。」と宣言しています。また、ここでheroku側でのアプリ名も決定されます(名前はランダム)。
今回はProtected-waters-42026という名前になりました。手順5 herokuへpushする
git push heroku masterというコマンドを打ちます。hirog:livewire-application hirog_engineer$ git push heroku master Enumerating objects: 123, done. Counting objects: 100% (123/123), done. Delta compression using up to 4 threads Compressing objects: 100% (101/101), done. Writing objects: 100% (123/123), 68.26 KiB | 1.95 MiB/s, done. Total 123 (delta 9), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source:このコマンドを打つことで、
gitを元にアプリのmasterブランチにあるコードがherokuにpush(送信)されます。ちなみに、この時点でProcfileを作成し忘れていると、以下のようなメッセージが途中に出てきます。
「Procfileがないから、アクセスがあった時はrootディレクトリを参照されるようにしたよ」という意味です。
ただ、先ほども書いた通り、Laravelのアプリはpublicをエンドポイントにしなければいけません。手順を戻って
masterブランチにcheckoutしてProcfileを作成しましょう。手順6 config(設定)を行う
herokuにpushする時にも
.gitignoreは有効になっているので、ここまでの手順で環境変数.envはheroku上にありません。。
.envに書くような情報はheroku用のコマンドを使って設定する必要があります。
最低限、設定しておく項目は、、、
KEY VALUE 役割 APP_NAME 任意(アプリケーション名) ブラウザタブのタイトルなど、アプリ名の表示に使用 APP_KEY key:generateコマンドで発行 認証情報の暗号化 SESSION_DRIVER database/fileなど(認証方法による)セッション管理の方法 APP_DEBUG true/false デバッグモードON/OFF ※DBの接続情報、外部API連携キー、メールサーバ接続情報なんかも同じ要領で設定します。
※
SESSION_DRIVERはデフォルト値がfileですが、認証に使っているライブラリによっても変わります。.envに書かれている値をそのまま入れます。※最初にデプロイする時は
APP_DEBUGをtrueに設定することをオススメします。デプロイ初期にエラーが起こっている時、デバッグモードじゃないと原因の把握がしづらいため。認証周りを含めデプロイがうまくいったところでfalseに変更するといいかと。config設定の方法はCUI(コマンドライン)とGUI(herokuのアプリページ内)の2種類。書き換え・確認のしやすさからGUIでやるのがオススメです。
CUIでconfig設定する
srcディレクトリ内(Procfileがある場所)から
heroku config:setコマンドで設定できます。// 例 // 暗号化キー heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show). # APP_KEY: base64:.... みたいな表示がされる // アプリ名 heroku config:set APP_NAME=livewire-project // アプリのベースURL (git push heroku master で表示されたURLを入力) heroku config:set APP_URL=https://sutara79-laravel.herokuapp.com // セッションドライバ heroku config:set SESSION_DRIVER=database // デバッグモード heroku config:set APP_DEBUG=true // 設定の確認コマンド heroku configGUIでconfig設定する
②アプリページ内のタブ一覧から
Settingsを選択、Config Vars欄に、
KEY-VALUEの形式で値を入力するだけ!
手順7 DB設定をする
DBの設定は、それだけでひと仕事になるので、別記事を用意しました。
長文ですが、初めてデプロイする方でも分かりやすいよう書いたので、
よかったら参照してください。手順8 マイグレーション実行
herokuにpushした場合、
composer installは自動でしてくれているので、
他の初期設定やマイグレーションをやっていきましょう。
heroku runに続けてartisanコマンドを打ちこみます。heroku run php artisan migrateproduction環境では以下のような表示があるかもですが、気にせずyes
マイグレーションが終わったら、アプリケーションのURLにアクセス!
エラーなく画面が表示されたら
APP_DEBUGをfalseにするのを忘れずに。
お疲れさまでした!
その他やっておくと便利な設定・コマンド
個人的な目線でコレしたいな、というものを数点紹介。
今後、自分で実践したら記事にしたいと思います!・ログを確認する
heroku logsコマンドで確認(参考資料)。注意書き
herokuの記事はたくさんの方が投稿して下さっているのですが、過去の記事に書かれていた設定手順が、今となっては不要になっている、というトコロも数点ありました。
・PHPの国際化用拡張モジュール(intl)を使う
↓のようなエラーが出て実行できなかったため、断念
↑の通り、
git push heroku masterした時にheroku側がpush内容から自動でBuildPackを設定してくれているっぽいです。
GUI(herokuのアプリ管理画面)のSettingsタブにもPHPが設定されているのが確認するだけでOKかと。あくまで私自身がやってみて不要だった、というだけなので、場合によってはこれらの設定も必要になるかもしれません。
参考資料
・公式リファレンス
・Laravel5.7: Herokuにデプロイする
・【Heroku】LaravelとMySQLでデプロイする
・(youtube動画)How to Deploy Laravel Project with Database on Heroku for Free | Step by Step Tutorial by Code Band
・Herokuでpg:psqlを実行しようとしたらThe local psql command could not be located
・HerokuのPostgreSQLのdbをみる方法
- 投稿日:2020-12-06T13:14:54+09:00
【Portfolio】laravelでサイトを構築(設計→実装→デプロイ)
成果物
★こちらクリック! → Go To "tiny-happiness"★
★こちらクリック! → Go To GitHub★実装環境
インフラ:AWS
サーバー側:PHP、Laravel 5.8
ソート管理:Git
フロント側:Html,CSS,JavaScript,jQuery機能一覧
■記事一覧表示
■記事詳細表示
■記事投稿/コメントをつける
■ユーザー新規登録
■ユーザーログイン/ログアウト
■DB操作:Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)
■DBテーブルのリレーション操作
■権限分け(RBAC|ロールベースアクセス制御)
■Ajax機能(jQuery)
■画像アップロード機能の詳細
■記事一覧表示
・Laravelのページネーションを使い、configの中にページ数を定義することで、一括で修正することは可能
■記事詳細表示
・登録済みのユーザーが記事の中でコメントできる
・記事と記事のコメントをすべて表示する
■記事投稿/コメントをつける
・ユーザーのログイン状態を確認してから、登録済みのユーザーがコメントできる
・画像アップロードできる(下記の【画像アップロード】にご参照)
■ユーザー新規登録
・新規登録するとき、提出前に、ユーザー名の使用状況を確認(下記の【Ajax機能(jQuery)】にご参照)
■ユーザーログイン/ログアウト
・ログインするとき、ユーザー名とパスワードのバリデーションを検証する
・ログアウトしない限り、ログイン情報がSessionに保存し、2時間以内再度ログインする必要がない
■DB操作:Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)
・Create(生成)とき、正規表現とバリデーションでデータの有効性を確認
・Read(読み取り)、キーワードによりの検索
・Update(更新)、登録済みのデータが表示されたうえで更新する
・Delete(削除)、論理削除と物理削除を分ける
■DBテーブルのリレーション操作
・ユーザーのIDから役割を取得(1対多の逆関係 → belongsTo)
・役割から権限を取得(多対多 → belongsToMany)
・記事からすべてのコメントを取得(1対多 → hasMany)
・記事のIDから記事のユーザーを取得(1対多の逆関係 → belongsTo)
・コメントのIDからコメントのユーザーを取得(1対多の逆関係 → belongsTo)
■権限分けで管理操作(RBAC|ロールベースアクセス制御)
・Middlewareを設定して、ログインしていないユーザーは管理画面へアクセスできない
・管理者:すべての権限を持つ
・スタッフ:管理者から付与した権限範囲内で操作
・一般ユーザー:自分のデータしか操作できない
・権限以外の画面が表示しない
■Ajax機能(jQuery)
・新規登録するとき、ユーザー名が存在するかどうか
・crud操作する時、Ajaxで送信
■画像アップロード
・API(Webupload)を使って、画像アップロードする
・画像がアップロードしないとき、ディフォルトの画像を使う
■単体テスト紹介動画(作成中)
完全版:
分割版(一本/1min以内)
- 投稿日:2020-12-06T12:30:14+09:00
【サルが書く】Lazy Eager loadingでもキャストしたい
わきゃ!!
GIGアドベントカレンダー9日目になります!
GIGメンバーの投稿はこちら? から見てみてね!
https://qiita.com/organizations/gig-inc$test = Test::where(['test' => 'test'])->get(); $test->load(['table_name' => function ($query) { $query->orderByRaw('CAST(table_name.column_name AS signed) ASC'); }]);これでいけると思います。
- 投稿日:2020-12-06T12:17:21+09:00
PHPの無名関数を使ってみた
無名関数とは?
公式ドキュメントの引用です。
無名関数はクロージャとも呼ばれ、 関数名を指定せずに関数を作成できるようにするものです。 callable パラメータとして使う際に便利ですが、用途はそれにとどまりません。
https://www.php.net/manual/ja/functions.anonymous.php
callableパラメータ
callableパラメータとしての使い方を見てみます。
<?php // 使用方法1 // クロージャ $double = function($number) { return $number * 2; }; $numbers = [1, 2, 3, 4]; // 各値を2倍にしていく $new_numbers = array_map($double, $numbers); // 結果 [2, 4, 6, 8] // 使用方法2 $numbers = [1, 2, 3, 4]; $new_numbers = array_map(function($number) { return $number * 2; }, $numbers); // 結果 [2, 4, 6, 8]変数の使用
次に、外部から変数を使用する場合の使い方を見てみます。
エラー例
<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する。 $magnification = 3; $new_numbers = array_map(function($number) { return $number * $magnification; }, $numbers); // 結果 PHP Notice: Undefined variable: magnification in......そのまま、変数を使用しようとするとエラーとなります。
そこで、useを使用します。<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する $magnification = 3; $new_numbers = array_map(function($number) use ($magnification) { return $number * $magnification; }, $numbers); // 結果 [3, 6, 9, 12]もちろん参照渡しにすると、変数の値を変更できます。
<?php $numbers = [1, 2, 3, 4]; // 倍率を指定する $magnification = 3; $new_numbers = array_map(function($number) use (&$magnification) { // loopごとに倍率に1を足す $magnification++; return $number * $magnification; }, $numbers); // 結果 [4, 10, 18, 28]型指定
型を指定することができます。
指定していないからといってエラーにはならないですが、安全性、保守性の向上を考えると指定しておいたほうが良いです。
例としてLaravelのEloquentsを使用します。<?php $persons = App\Person::all(); array_filter($persons, function(Person $person) { // IDEによってはgenderに補完が効き、定義元へジャンプできるようになる。 return $person->gender == '男子'; });Laravel使用例
最後にLaravelのCollectionでよく使うので紹介します。
collect(['太郎', null, '次郎']) ->reject(function ($name) { // 名前がない要素を除外 return empty($name); }) ->map(function ($name) { // 末尾に「さん」をつける return $name . 'さん'; }) ->values();※
values()を使うことでキーをリセットしてくれます。公式ドキュメントのweb内検索で
function()と検索するとどのような場面でfunction()を使用しているかわかるので参考になります。
https://readouble.com/laravel/5.8/ja/collections.html
- 投稿日:2020-12-06T10:59:31+09:00
LaravelでRailsみたいなデバックがしたい。(スクール卒業生向け)
概要
巷にあふれる某スクール卒業生。
彼らがスクールで学ぶのはRuby on Railsなのだが他人との差別化を図る上でLaravelを用いてポートフォリオを作成する方々を散見する。彼らがスクールで学んだ、デバック方法は「binding.pry」ほぼ一択。
Laraveでよく見かけるデバック方法は,var_dump(); や dd(); でちょっとモヤモヤしてる方向けの記事になります。
結論
「binding.pry」みたいなことはLaravelでも出来る!!
eval(\Psy\sh());処理を止めたいところに「binding.pry」のように書いて、ターミナルで確認するだけです。
恐らく、この説明でスクール卒業生は使いこなせると思います。
「大金払ってRuby on Railsを学び、実務で使うのはLaravelなんですか?」
という意見もあると思うのですが、個人的には自分が卒業生でlaravelエンジニアというのもあり応援したい気持ちです。
- 投稿日:2020-12-06T10:46:31+09:00
laravel リレーション関係
データベーステーブルの関係は大体下記のように分けられます。
・1対1
・1対多
・多対多1対1
例:userとphoneの関係、基本一人には一つの番号しか持ってないですね。
userでphoneを検索したいとき、二つの関係の定義:hasOne
phoneでuserを検索したいとき、二つは逆関係の定義:belongsTo1対多
例:articleとcommentの関係、一つの記事に複数のcommentが存在しますね。
articleでcommentを検索したいとき、二つの関係の定義:hasMany
commentでarticleを検索したいとき、二つは逆関係の定義:belongsTo多対多
例:userとroleの関係、userが多くのroleを持ち、rolesも大勢のuserを持っています。
二つの表の関係を別の表user_roleで結びつけます。userでroleを検索したいとき、二つの関係の定義:belongsToMany
ModelsClass User extends Model { public function roles() { return this->belongsToMany('App\Role','role_user','user_id','role_id'); } }roleでuserを検索したいとき、二つは逆関係の定義:belongsToMany
ModelsClass Role extends Model { public function users() { return this->belongsToMany('App\User','role_user','role_id','user_id'); } }
- 投稿日:2020-12-06T10:03:49+09:00
Laravel6 チェックボックスの値を受け取り保存する
目的
- Laravel6でチェックボックスの実装とtrue falseを受け取りそれに応じてデータベースのカラムに0か1を格納する
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.8 Homebrewを用いてこちらの方法で導入→Mac HomebrewでPHPをインストールする Laravel バージョン 6.X commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提条件
- 実施環境に近い環境が構築されていること。(筆者は直接Macに環境を構築しているがLaravel6の動作環境なら基本本方法で確認可能である。)
前提情報
- Authを用いた認証機能があるLaravel6アプリがあること。
- usersテーブルに1つ以上のデータレコードが存在すること。
- Larvel6でチェックボックスを実装する。
- そのチェックボックスがチェックされているかいないかによってDBのusersテーブルのcheck_flagカラム」に格納する値を変更する。
- 「check_flagカラム」はチェックされていないと0、チェックボックスにチェックされていると1が入る。
- 「check_flagカラム」に1が入っていた時にチェックボックス入力ページのチェックボックスは最初からチェックがついた状態にする。
概要
- カラム作成
- ルーティング情報の記載
- コントローラファイルの作成と記載
- ビューファイルの作成と記載
- 確認
詳細
カラム作成
アプリ名ディレクトリで下記コマンドを実行してマイグレーションファイルを作成する。
$ php artisan make:migrate add_check_flag_columns_to_users_table --table=users作成されたマイグレーションファイルを開き下記のように記載する。下記にはマイグレーションファイルの全内容を記載する。
アプリ名ディレクトリ/database/migrations/YYYY_MM_DD_HHMMSS_add_check_flag_columns_to_users_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AddCheckFlagColumnsToUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { // $table->tinyInteger('check_flag')->default(0)->after('password'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { // $table->dropColumn('check_flag'); }); } }ルーティング情報の記載
ルーティングファイルを開き、下記の3行を追記する。
アプリ名ディレクトリ/routes/web.phpRoute::get('/input', 'TestController@input')->name('input'); Route::post('/check_register', 'TestController@register')->name('check.register'); Route::get('/output', 'TestController@output')->name('output');コントローラファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行してコントローラファイルを作成する。
$ php artisan make:controller TestController作成されたファイルに下記の内容を記載する。下記にはコントローラファイルの全内容を記載する。
アプリ名ディレクトリ/app/Http/Controllers/TestController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\User; class TestController extends Controller { public function input() { $user_infos = User::select('*')->get(); return view('tests.input', ['user_infos' => $user_infos]); } public function register(Request $request) { $input_data = $request->all(); foreach ($input_data['user_id'] as $key => $value) { $user_info = User::select('*')->find($key); $user_info->check_flag = $value; $user_info->save(); } Log::info($request); return redirect(route('output')); } public function output() { $user_infos = User::select('*')->get(); return view('tests.output', ['user_infos' => $user_infos]); } }ビューファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行してビューファイル格納用のディレクトリを作成する。
$ mkdir resources/views/testsinput.blade.phpファイルとoutput.blade.phpファイルを
アプリ名ディレクトリ/resources/views/tests直下に作成する。先に作成した2つのビューファイルに下記の内容を記載する。
input.blade.php
input <form action="{{ route('check.register') }}" method="post"> @csrf <table border="1"> <tr> <th>チェックボックス</th> <th>id</th> <th>name</th> <th>email</th> </tr> @foreach ($user_infos as $user_info) <tr> <input type="hidden" name="user_id[{{ $user_info['id'] }}]" value="0"> @if ($user_info['check_flag'] === 1) <td><input type="checkbox" checked="checked" name="user_id[]" value="{{ $user_info['id'] }}"></td> @else <td><input type="checkbox" name="user_id[]" value="{{ $user_info['id'] }}"></td> @endif <td>{{ $user_info['id'] }}</td> <td>{{ $user_info['name'] }}</td> <td>{{ $user_info['email'] }}</td> </tr> @endforeach </table> <button type="submit">保存</button> </form>output.blade.php
output <table border="1"> <tr> <th>チェックボックス</th> <th>id</th> <th>name</th> <th>email</th> </tr> @foreach ($user_infos as $user_info) <tr> @if ($user_info['check_flag'] === 1) <td><input type="checkbox" checked="checked" name="user_id[]" value="{{ $user_info['id'] }}"></td> @else <td><input type="checkbox" name="user_id[]" value="{{ $user_info['id'] }}"></td> @endif <td>{{ $user_info['id'] }}</td> <td>{{ $user_info['name'] }}</td> <td>{{ $user_info['email'] }}</td> </tr> @endforeach </table>確認
- ローカルサーバを起動し下記にアクセスする。
テーブル状にusersテーブルのデータが表示されるので任意のレコードのチェックボックスにチェックをいれて「保存」をクリックする。
outputの画面に移動する。
再度下記にアクセスする。
先程チェックを入れて保存したカラムのチェックボックスはデフォルトでチェックが入った状態になっている。
- 投稿日:2020-12-06T07:16:38+09:00
LaravelのBladeテンプレートにCSSやJavascriptを書かない理由はこれかな?
はじめに
この記事は、私個人の意見を文章にしているだけの記事です。
そのため、コードサンプルや、技術の説明のような情報はありません。
もし、コードサンプルや技術説明をお探しであれば、別の方の記事を参考にしてください。Bladeテンプレートでの問題点
以前、次のような記事を書きました。
LaravelのBladeテンプレートをVue.jsっぽく書いてみた
この手法だと、部品毎で分割しやすく、コードも見やすくなると思っていたんですよね。
しかし、実際に開発していて大きな問題が1つ発生しました。
それは、JavascriptのUnitテストができないことです。解決方法の検討
次のように解決方法を検討しました。
1. Javascriptを外部のJSファイルに記述する
これは、Vue.jsやReact等の、フロントエンドのフレームワークを利用しない場合、当たり前の記述方法だと思います。
Laravelでは、Bladeテンプレートファイルの中でCSSやJavascriptを使用することは推奨されていません。
しかし、明確に理由まで記載してある情報ソースを見つけることができなかったので、自分は「盲目的にルールを守ること」はしませんでした。
その結果、問題点が発覚したので、反省が必要ですね。
とはいえ、Bladeテンプレート内に書くJavascriptのコードはほとんどがDOM操作です。
なので、外部ファイルにして単体テストをしたところで、どこまで信頼できるものかわかりませんね。
※DOM操作は他のUIに依存するものが発生しやすいので、結局は判定ロジックや、算出ロジックだけのテストになるのかと。。2. フロントエンドフレームワークを利用する
これは、ある程度大きなプロダクトでは、当たり前の話です。
しかし、技術選定時は、「できるだけ多くの人が参画しやすいようにする」をテーマに、Laravelを選定しました。
というのも、私が参画させていただいている案件は、企業したてのスタートアップ企業様の案件です。
そのため、スペシャリストよりも、フルスタックな人が必要だったんですよね。経営を考慮しての技術選定だったので、現段階でLaravelを選定したことについては仕方なかったと思っています。
しかし、Unitテストができないというのは、品質を担保する指標が1つなくなること。
なので、後々はフロントエンドのスペシャリストに参画していただき、Vue.jsやReactを採用してUnitテストができる状態も必要なのかなとも思います。
その時は、売り上げが拡大されてきた時ですかね。
※余談ですが、VueやReactを使えばSPAも簡単に実現できますし、付加価値は付けやすいでしょう。3. Integrationテストの仕組み構築に尽力する
現段階では、これが一番現実的なのではないかと考えています。
ここまでは、Unitテストのことしか考えていませんでした。
しかし、Integrationテストでしっかりテストをする仕組みがあり、かつ、デバッグがしやすいのであれば問題ないかと思っています。
というのも、Integrationテストでバグが発生したらUnitテストからやり直しになることも多々あります。
また、網羅率100%のUnitテスト用コードを目指すと、テストをすることが目的になる可能性もあるんですよ。
そうなると、無駄なことに工数をかけてしまい、ビジネス的にはよろしくないですよね。
なので、今回はこの方向性が一番有効かと思います。最後に
この記事では、設計による問題の解決手法を検討した内容を記載しました。
今回の場合だと、予算が潤沢にある企業様だとしても「技術選定からやり直し」や、「Unitテストのためだけの設計変更」はするべきではないと思います。
というのも、出戻りばかりしていたら、リリースまでたどり着けないからです。
スタートアップの企業様ですと収益化できない期間が長くなるのは、より苦しいですよね。
そのため、今回は、「出戻りはしない」かつ、「品質は落とさない」のどちらにも配慮して検討してみました。
もし、本当に設計変更やフレームワークの導入をするのであれば、スケールして、予算に余裕が出てきた時で良いと思います。最後まで読んでいただき、ありがとうございます
私は、参画させていただいている企業様に心より感謝し、プロジェクトの成功に尽力致します。そして、自分のスキルアップに尽力することが、お世話になる企業の関係者様の利益になると信じています。
- 投稿日:2020-12-06T00:40:18+09:00
Laravel SubstituteBindingsミドルウェア
Laravel SubstituteBindingsミドルウェアについて調べてみた。
SubstituteBindingsミドルウェアは、Kernel.phpでwebとapiグループのミドルウェアとして指定されている。
Substitute = 代替(代わりに使う)。Binding = 紐づけるKernel.phpprotected $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' => [ 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];SubstituteBindingsは
ルートモデルバインディング用の機能。
https://laravel.com/docs/6.x/routing#route-model-bindingrouteにfunctionを入れてモデルを引数にすると、pathの値を元に自動でfindOrFailして引数として渡してくれる。
{user} pathでUserが取得できない場合は404エラーが返される。api.phpRoute::get('/user/{user}', function (\App\Models\User $user) { return $user; });curl http://127.0.0.1:8000/api/user/1 {"id":1,"name":"aaa","email":"hoge@co.jp","email_verified_at":null,"api_token":"WzLJB6Djg7HS9vDp8Ss8RqcIKYPYvs2pJBUl8CYMwsnVCHXK3FeIEdLBFq8Q","created_at":"2020-12-04T14:46:11.000000Z","updated_at":"2020-12-04T14:46:11.000000Z"}%SubstituteBindingsをミドルウェアから外した状態で再度アクセスするとLaravelコンテナによってUserモデルがインスタンス化された状態で引数に追加される。($userはnullではないが$user->idとかはnullになる)
SubstituteBindings読む
SubstituteBindingsのコンストラクタでRegisterを引数でもらっている。
SubstituteBindings.phppublic function __construct(Registrar $router) { $this->router = $router; }handle関数では、bindingを行っている。
binding処理はコンストラクタで受け取ったRegisterクラスで行っている。public function handle($request, Closure $next) { $this->router->substituteBindings($route = $request->route()); $this->router->substituteImplicitBindings($route); return $next($request); }Register
Illuminate\Foundation\Application.phpのregisterCoreContainerAliases関数でRegisterのaliasが設定されている。
Registerクラスは\Illuminate\Routing\Router::classの型を使ってDIされる。Application.php'router' => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],substituteBindings
substituteBindingsは明示的なpathとModelの結合の処理に使用される。
substituteBindingsの関数を見ると、routeのparameterを元にパラメータを設定している。
Routerにはpath名と型を紐づけるbinderを管理するbidnersプロパティがあり、そこに該当するbinderが設定されているとperformBinding関数を使ってパラメータを取得し、setParameterでセットしている。
binderはbind関数で追加されるが、これはmodel関数で使用されている。
model関数はRoute::model('user', \App\Models\User::class);のように使われる。Router.phppublic function substituteBindings($route) { foreach ($route->parameters() as $key => $value) { if (isset($this->binders[$key])) { $route->setParameter($key, $this->performBinding($key, $value, $route)); } } return $route; }↓routeのパラメータはpathを分解した時のpath名をkeyに値をvalueにした配列になっている。
Route::get('hoge/{hogeId}', [\App\Http\Controllers\HogeController::class, 'hoge']); ↓route->parameters()の値 {"hogeId":"11"}substituteImplicitBindings
substituteImplicitBindingsは暗黙的なpathとモデルの結合に使用される。
Implicit = 暗黙的






















