- 投稿日:2019-02-15T21:43:10+09:00
【Laravel5.7】カラム名でBETWEENしたい
SELECT * FROM table WHERE id BETWEEN 1 AND 10
と書きたい場合は普通に書けます。$response = Table::whereBetween('id', [1, 10])->get();こうすると
`id` BETWEEN ? AND ?
というSQLが発行され、正しく想定通りの結果を得られます。ところで、たとえば表示開始時刻から表示終了時刻までは表示する、といった仕組みを作りたい場合はどうすればいいか。
つまりSELECT * FROM table WHERE NOW() BETWEEN display_start AND display_end
ということです。これを調べてみたのだけど、意外と見当たらなかった。
$response = Table::whereBetween('NOW()', ['display_start', 'display_end'])->get();こう書くと、
`NOW()` BETWEEN ? AND ?
とかいうSQLが発行されてしまいます。
当然NOW()
なんてカラムは無いのでエラーです。仕方ないので試しにやってみたらこれで動いた。
$response = Table::whereBetween(DB::raw('NOW()'), [DB::raw('display_start'), DB::raw('display_end')])->get();いや、うん、想定通りのSQLが発行されてるし、結果も正しいけど、なんというかこれでいいのか?
- 投稿日:2019-02-15T19:00:17+09:00
Laravelの開発環境をDockerを使って構築する
何回Laravelの開発環境を構築するねん...ってツッコミたくなりますが、4回目の記事です?
概要
docker(docker-compose)でLEMP環境(PHP/nginx/MySQL)を構築し、Laravelの新規プロジェクト作成まで行います。
使用するコマンドについても軽く説明していきます。リポジトリ
https://github.com/ucan-lab/docker-laravel5
お急ぎの方は README 参照
対象読者
- Laravelが動く環境をdockerで作りたい人
Docker とは
Dockerとは、一台のマシン上に複数の隔離された環境を構築する軽量なコンテナ型の仮想化技術です。
特定のプロセスの実行環境を
Dockerイメージ
という単位で管理できます。Dockerのメリットとしては、
仮想マシン(VirtualBox)と違ってハードウェア(CPU, メモリ)をシミュレーションする必要がないためとても軽快です。補足
コンテナに出たり入ったりすることが多いので下記のようにします。
[mac] $ Macで実行するコマンド [web] $ dockerの web コンテナ内で実行するコマンド [app] $ dockerの app コンテナ内で実行するコマンド [db] $ dockerの db コンテナ内で実行するコマンド [node] $ dockerの node コンテナ内で実行するコマンド [redis] $ dockerの redis コンテナ内で実行するコマンド前提
- docker
- docker compose
- git(Macは標準でインストールされている)
上記のツールが入っていることが前提となってます。
docker インストール
docker for macをインストールして起動します。
[mac] $ brew cask install docker [mac] $ open /Applications/Docker.appdockerが入っていることを確認する
[mac] $ docker --version Docker version 18.09.1, build 4c52b90 [mac] $ docker-machine --version docker-machine version 0.16.1, build cce350d7 [mac] $ docker-compose --version docker-compose version 1.23.2, build 1110ad01git
gitが入っていること、sshできることを確認する
[mac] $ git --version git version 2.17.2 (Apple Git-113 [mac] $ ssh -T github.com Hi ucan-lab! You've successfully authenticated, but GitHub does not provide shell access.もしsshできなかった場合は、
過去記事にssh接続の設定手順の記事を書いてるので良かったら参考にしてください。推奨アプリ
- Kitematic
- コンテナをGUIで管理するツール
- dockerを触るなら必須
- TablePlus
- MySQLをGUIで管理するツール
- MySQLを触るなら必須
- MySQL8.0に対応
- Visual Studio Code
- Microsoft製のテキストエディタ
- こだわりがない人は入れておこう
- PhpStorm
- JetBrains製の統合開発環境(IDE)
- お金がある人は入れておこう
環境
Mac
- MacOS Mojave 10.14.2
Docker
最終的に次の環境を構築します!!
$ docker-compose run web nginx -V nginx version: nginx/1.15.7 built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) built with OpenSSL 1.1.0f 25 May 2017 TLS SNI support enabled [mac] $ docker-compose run app php -v PHP 7.3.2 (cli) (built: Feb 9 2019 02:11:33) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.2, Copyright (c) 1998-2018 Zend Technologies [mac] $ docker-compose run app composer -V Composer version 1.8.4 2019-02-11 10:52:10 [mac] $ docker-compose run db mysql -V mysql Ver 8.0.15 for Linux on x86_64 (MySQL Community Server - GPL) [mac] $ docker-compose run redis redis-cli --version redis-cli 5.0.3 [mac] $ docker-compose run node node -v v11.8.0 [mac] $ docker-compose run node npm -v 6.5.0 [mac] $ docker-compose run node yarn --version 1.13.0※バージョン固定してないものもあり、構築するタイミングによってバージョン差異はあります。
ディレクトリ構成
[mac] $ tree . . ├── README.md ├── docker => 各コンテナの設定ファイル │ ├── mysql │ │ ├── Dockerfile │ │ ├── data │ │ ├── logs => MySQLのログファイルはここ │ │ └── my.cnf │ ├── nginx │ │ ├── default.conf │ │ └── logs => nginxのログファイルはここ │ └── php │ ├── Dockerfile │ └── php.ini ├── docker-compose.yml └── src => Laravelプロジェクトをインストールするディレクトリディレクトリ構成はとても悩みましたが、この形で行きます。
Docker環境を構築する
今回は
handson
という作業ディレクトリを作ります。[mac] $ mkdir ~/handson [mac] $ cd ~/handsonGitHubリポジトリからDockerテンプレートをダウンロード
[mac] $ git clone git@github.com:ucan-lab/docker-laravel5.git [mac] $ cd docker-laravel5環境設定ファイルの確認
dockerの.env
の中身を確認します。$ less .env MYSQL_DATABASE=homestead MYSQL_USER=homestead MYSQL_PASSWORD=secretデータベースの接続情報を記述している箇所があります。
ここはLaravelの.env
の初期設定に合わせてます。
必要に応じて 環境設定ファイルや各コンテナの設定ファイルを変更してください。Dockerのビルド、起動
[mac] $ docker-compose build [mac] $ docker-compose up -d
build
コマンドで サービスの構築 を行うup
コマンドで コンテナを作成してサービスを開始 します-d
オプションで デタッチド・モードで起動(デフォルトはアタッチド・モード) します。
- これにより通常はコンテナを実行するとすぐに終了しますが、バックグラウンドで動作するようになります。
[mac] $ docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------------------- myproject_app_1 docker-php-entrypoint php-fpm Up 0.0.0.0:3501->8000/tcp, 9000/tcp myproject_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3502->3306/tcp, 33060/tcp myproject_node_1 node Exit 0 myproject_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp myproject_web_1 nginx -g daemon off; Up 0.0.0.0:3500->80/tcp
ps
コマンドで起動しているコンテナの一覧を表示します。上記のようにコンテナが表示されていればokです。
Laravelプロジェクトを新規に作成する場合
[mac] $ docker-compose exec app ash [app] $ composer create-project laravel/laravel --prefer-dist .
exec
コマンドで指定した起動しているコンテナのプロセスを実行できます。ash
を指定することでコンテナの中に入って任意のコマンドを実行できます。composer create-project laravel/laravel
新しいLaravelプロジェクトを作成するコマンド
--prefer-dist
はzipでダウンロードする(高速)、デフォルトはgit cloneする.
カレントディレクトリにインストールする上記のアドレスにアクセスしてLaravelのウェルカムページが表示されればokです。
ポート番号は他の環境と被らないようあえて
3500
番号に設定してます。LaravelのDocker環境構築は以上で完了です!!
お疲れ様です???必要に応じて...
Laravelのコマンドを実行したい場合
php
,composer
,artisan
コマンドはapp
コンテナ内で実行します。[mac] $ docker-compose exec app ash [app] $ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table
migrate
するとusers
とpassword_resets
テーブルが生成される。MySQLに接続したい場合
MySQLはdbコンテナに入ってMySQLコマンドを実行します。
環境変数に接続情報が入っているのでこのままコピペでログインできます。[mac] $ docker-compose exec db bash [db] $ mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE}上記のコマンドでMySQLにログインし、MySQLコマンドを実行できます。
[db] mysql> show variables like '%time_zone%'; +------------------+------------+ | Variable_name | Value | +------------------+------------+ | system_time_zone | UTC | | time_zone | Asia/Tokyo | +------------------+------------+ [db] mysql> show variables like '%char%'; +--------------------------+--------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql-8.0/charsets/ | +--------------------------+--------------------------------+[db] mysql> show databases; +--------------------+ | Database | +--------------------+ | homestead | | information_schema | +--------------------+ [db] mysql> show tables; +---------------------+ | Tables_in_homestead | +---------------------+ | migrations | | password_resets | | users | +---------------------+ [db] mysql> select * from migrations; +----+------------------------------------------------+-------+ | id | migration | batch | +----+------------------------------------------------+-------+ | 1 | 2014_10_12_000000_create_users_table | 1 | | 2 | 2014_10_12_100000_create_password_resets_table | 1 | +----+------------------------------------------------+-------+
- タイムゾーン:
Asia/Tokyo
- 文字コード:
utf8mb4
上述の
php artisan migrate
を実行していればmigrations
テーブルに2件データが入ってるはず。MySQL管理ツールから接続したい場合
オススメのMySQL管理ツールはTablePlusです。理由はMySQL8.0に対応してるから?
設定例
変更箇所
Name: docker-laravel5(任意) Host: 127.0.0.1 Port: 3502 User: homestead Password: secret Database: homesteadTablePlusの画面
Node(npm, yarn)を実行したい場合
Nodeのコンテナは起動させてないので
run
コマンドで起動させてコンテナ内に入ります。[mac] $ docker-compose run node ash [node] $ npm install # OR yarn install [node] $ npm run dev # OR yarn run dev
run
コマンドで指定したコンテナを作成して起動し、プロセスを実行します。Redisを使いたい場合
[mac] $ docker-compose exec redis ash [redis] $ redis-cli 127.0.0.1:6379> SET hoge bar OK 127.0.0.1:6379> GET hoge "bar"RedisをLaravelで使いたい場合
[mac] $ docker-compose exec app ash [app] $ composer require predis/predis [app] $ sed -i -e 's/CACHE_DRIVER=file/CACHE_DRIVER=redis/' .env [app] $ sed -i -e 's/SESSION_DRIVER=file/SESSION_DRIVER=redis/' .env参考: https://readouble.com/laravel/5.7/ja/redis.html
Laravelのビルドインサーバを使いたい場合
[mac] $ docker-compose exec app ash [app] $ php artisan serve --host 0.0.0.0
serve
Laravelのビルトインサーバを起動します。コンソール上にログが表示されます。--host
外部からのアクセスを許可する指定をします。データベースを初期化する
データベースを真っさらな状態に戻したい場合。
まずは、定義されているデータベースのボリュームを探します。$ docker volume ls | grep db-data local docker-laravel5_db-data
docker-laravel5_db-data
これです。$ docker-compose down $ docker volume rm docker-laravel5_db-data $ docker-compose up -d使用中のボリュームは削除できないので、コンテナを停止してボリュームを削除し、コンテナを再び起動するとデータベースが初期化された状態になります。
既存のLaravelプロジェクトの実行環境を構築する
例えば、このLaravelプロジェクトを動かしてみたいなぁという時。
https://github.com/ucan-lab/laravel-microposts
[mac] $ mkdir ~/handson [mac] $ cd ~/handson[mac] $ git clone git@github.com:ucan-lab/docker-laravel5.git [mac] $ git clone git@github.com:ucan-lab/laravel-microposts.git[mac] $ cd docker-laravel5
.env
を書き換える#PROJECT_PATH=./src PROJECT_PATH=../laravel-micropostsあとは環境構築して動作確認します?
[mac] $ docker-compose build [mac] $ docker-compose up -d [mac] $ docker-compose exec app ash [app] $ composer install [app] $ cp .env.example .env [app] $ php artisan migrate今回Dockerを勉強して知ったこと
上記の方々にレビューいただき、今回改めて知ったことを共有します。
コンテナ名の衝突を避ける
コンテナを起動するとフォルダ名が初期値としてプロジェクト名となってしまい、フォルダ名を変えた場合に、プロジェクト名が勝手に変わってしまう問題があります。
コンテナ名だけでなく、イメージ名、ネットワーク名、ボリューム名の衝突にも気を付ける必要があります。
COMPOSE_PROJECT_NAME
COMPOSE_PROJECT_NAME
を使えば、container_name
も指定する必要なく、「COMPOSE_PROJECT_NAME 変数 + _ + サービス名 + _ + 連番」という名前になります?RUN set -eux
RUN を実行する際にはじめにシェルオプションを設定すると
buildを実行する際にコマンドが表示されるので、処理が途中で失敗した場合などに原因調査しやすいというメリットがあります。
-e
オプションによって実行したコマンドが1つでもエラーになれば直ちに終了するようにし、-u
オプションで未定義の変数などを使おうとすればエラーにするようにしている。-x
オプションでは実行コマンドとその引数をトレースとして出力するようにしている。複数行でRUNの実行
Dockerは文を実行するたびにイメージのレイヤーを作成されてしまう。
RUN コマンドは繋げてワンライナーにする方が良い。
コマンドが長くなる場合は\
で改行して見やすくする。# NG RUN apk update RUN apk add git # GOOD! RUN apk update && \ apk add git一時的な変数は ENV ではなく ARG を使う
ARG
は Dockerfile 内で使用可能な変数です。ARG foo=bar RUN echo $fooADD より COPY
ファイルをコピーする意図であれば、
ADD
よりCOPY
を使った方がより直感的で分かりやすいADD ./my.cnf /etc/mysql/my.cnf # NG COPY ./my.cnf /etc/mysql/my.cnf # GOOD!トップレベルボリュームを使用する
トップレベルボリュームを使用するとデータの永続化が可能になります。名前付きvolume とも呼ぶそうです。
また、ボリュームが隠蔽化されるため誤って削除する心配が少なくなり開発しやすくなります。テスト用に使うデータベースを用意する
Laravelの場合、テスト用のデータベースを作成することが多いので、
テスト用のDBも合わせて作成してあると開発環境としてはさらに良い。
/docker-entrypoint-initdb.d
のディレクトリ配下に.sql
や.sh
ファイルを配置すれば初回起動時に実行してくれます。ベースイメージはコンテナの軽さを重視するならAlpine
Alpine Linux(アルパイン・リナックス)とは、
muslとBusyBoxをベースとしたLinuxディストリビューションです。
(用語を説明すると新たな用語の説明をしなければならなくなる説)セキュリティ・シンプルさ・リソース効率を重視したOSです。
特定のOSのシェルを実行するようなアプリでなければ、Alpineはとても軽量です。
ただ、徹底的に軽量化されているため、AWSやGCPにデプロイした際に苦戦しそうです?
Alpine以外のDockerイメージとしては、UbuntuやDebianのイメージが良く使われる傾向にあるみたいです。個人歴にはCentOSとかRedHat系のOSが好きです?
関連記事
参考にさせていただいた記事
- Dockerでサクっとローカル開発環境(LAMP + Laravel)を構築する
- Dockerfileを書くときに気をつけていること10選
- VagrantとDockerについて名前しか知らなかったので試した
- Dockerfileを書くときに気をつけていること10選
- Dockerfile ARG入門
さいごに
初めてdocker環境の構築に挑戦しましたが、個人的にはイイ感じに作れたかなと満足してます?
もっとこうした方がイイよ!っていうアドバイスがあったらコメントいただけると嬉しいです!
- 投稿日:2019-02-15T17:27:07+09:00
nuxt.js(フロント)とLaravel(API)を同一リポジトリ/サーバで動かす時
web server
- nuxt.jsは
yarn start
でデフォルトの3000番でうける- APIは
php artisan serve
でデフォルトの8000番でうける- nginxでプロキシする
nginx.confserver { index index.html; server_name sample.com; location /api { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_b }Laravelで作るAPIは、デフォルト
/api
のprefixがつく。
/api
だったら8000番へ。それ以外 3000番へ@nuxtjs/axiosによるAPIコール
apiComponent.vueconst response = this.$axios.$get('/api/users')この時、@nuxtjs/axiosは自動的にURLを作成して、コールしようとする。
デフォルトは、http://[HOST]:[PORT][PREFIX]
cf: https://axios.nuxtjs.org/options > baseURL
nginxが
localhost:3000
にproxyしているので、このままだとブラウザからhttp://localhost:3000/api/users
がコールされエラーとなる。@nuxtjs/proxy と axiosの設定
@nuxtjs/proxyを使って、特定のURLパスのときにAPIをプロキシ(実際はプロキシじゃなくてURLの書き換え?)する。
add_package$ yarn add @nuxtjs/proxy
nuxt.config.jsmodules: [ '@nuxtjs/axios', '@nuxtjs/proxy', ], axios: { baseURL: '/', }, proxy: { '/api': 'http://sample.com/', },proxyの設定で
/api
だったらサーバにリクエストが飛ぶようにする。
サーバサイドではnginxが/api
のパスを見てlocalhost:8000
にプロキシする。また、@nuxtjs/proxyにおける
/api
はパスを見てくれるのではなく、どうやらaxiosのbaseURLを見ている様子だった。
axiosの設定でbaseURL: '/'
の指定も必須っぽい?
- 投稿日:2019-02-15T16:37:56+09:00
【Laravel】ユーザー認証を、他テーブルのIDとパスワードで認証する
環境
- Laravel5.6
対象ケース
該当のEloquent(Users等)で認証したいが、IDとパスワードは別のテーブルのカラムを使用したい
例えば、認証機能を複数用意しており、それぞれEloquentモデルを分けているが、IDとパスワードは1箇所のテーブルにまとまっているなど。概要
Laravelでは認証機能が標準で用意されており、
php aritsan make:auth
するとデフォルトのセットが追加されます。
通常、config/auth.php
の内容に沿って処理が展開される流れとなります。config/auth.php'defaults' => [ 'guard' => 'app', 'passwords' => 'users', ], 'guards' => [ 'app' => [ 'driver' => 'session', 'provider' => 'users', ], // 追加 'admin' => [ 'driver' => 'session', 'provider' => 'admin', ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], // 追加 'admin' => [ 'driver' => 'eloquent', 'model' => App\Models\Admins::class, ], ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, ], ],
Admins
のモデルを使いたいが、IDとパスワードはUsersに存在すると仮定します。config/auth.php'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], // 追加 'admin' => [ 'driver' => 'custom_eloquent', 'model' => App\Models\Admins::class, ], ],ポイントは、↑の
driver
を独自で作ってあげることです。app/Providers/AuthServiceProvider.phppublic function register() { Auth::provider('custom_eloquent', function($app) { return new CustomEloquentProvider($app['hash'], config('auth.providers.admin.model')); }); }app/Drivers/CustomEloquentProvider.phpuse Illuminate\Support\Str; use Illuminate\Auth\EloquentUserProvider; class CustomEloquentProvider extends EloquentUserProvider { public function retrieveByCredentials(array $credentials) { if (empty($credentials) || (count($credentials) === 1 && array_key_exists('password', $credentials))) { return; } // クエリを変更 $query = $this->createModel()->query() ->select(['admin.*', 'users.username', 'users.password']) ->join('users', 'users.id', '=', 'admin.user_id'); foreach ($credentials as $key => $value) { if (! Str::contains($key, 'password')) { $query->where($key, $value); } } return $query->first(); } }
Illuminate\Auth\EloquentUserProvider
の中身を追っていくと分かりますが、
実際に認証クエリを発行しているメソッドはretrieveByCredentials
であることが分かります。
retrieveByCredentials
をオーバーライドしてあげれば自由に処理を変更することができます。
- 投稿日:2019-02-15T15:27:56+09:00
laravelでmysqlのjson型の扱いかた
json型の扱いについてまとまったページがあまりなかったので書くことにしました。
・・・いまだに苦しめられています。環境
larave5.7
Mysql5.71.データベースにjson型を含むテーブルをmigrationで作成するとき
public function up() { Schema::create('members', function (Blueprint $table) { $table->uuid('id'); $table->string('name', 100); $table->json('data')->nullable(); // ココ $table->timestamps(); $table->primary('uuid'); }); }※注意
なんと、Mysql5.7のJson型は初期値を設定できない仕様になっています・・・!(MariaDBは初期値OK)
$table->json('data')->default('{"title":1,"permission":[1,2]}');
というかんじに書いてたらmigrate実行時にエラーが出ました。
MariaDBで開発してるけどリリース先の環境はMysqlって場合は特に気をつけてください。(もちろんDB揃えられるなら揃えてください!)2.データベースにデータを入れるとき
こちらにすでに書かれてましたhttps://qiita.com/kamikosi/items/7d4135ce74de8b91e721
$data = [ [ 'title' => 1, 'permission' => [ 1, 2, ], ], [ 'title' => 2, 'permission' => [ 1, 2, ], ] ]; $member = new Member; $member->data = $data; // ココ $member->save();※注意
$member->data = json_encode($data);
と書くとstringとしてデータが入ってしまう。
↓がそのjson_encodeしてしまったデータ。
"{\"permission_list\":[{\"title\":\"2\",\"permission\":[\"1\",\"3\"]}],\"admin\":false}"
JSON_UNESCAPED_UNICODEオプションを付けても両端の"
はのこってしまうので
json型として取得できなくなってしまう。。。3.データベースからデータを取得するとき
参考:https://qiita.com/pinekta/items/93c9c427ccc22df578a5
まず、モデル側に$castsを設定する。
class Member extends Eloquent { protected $casts = [ 'json_column' => 'json', // ココ ]; }$castsについてはjson型だけでなく、データをDBから取得するときの型を設定できるものなので
知っておくと応用がききそうです。つぎにそのモデルを使ってデータを取得します。
$member = Member::find($id); // $member->dataにはjsonが連想配列として保存されている $data_permission = isset($member->data['permission']) ? $member->data['permission'] : null ;$castsによってjson型データが連想配列として取得できるようになっているので
$member->data['permission']
のように取得できる。
json objectとしてデータベースに入れていても、取得すると連想配列になっているのでネスト地獄に注意。。。(絶賛苦戦中)また1.の注意欄でも書いた通り、初期値設定ができないので
特定のキーが存在するかどうかのチェック(こちらではissetを使用)が必要です。
まだまだlaravel, MysqlのJson型については勉強中の身ですので
「こうすれば{もっとかんたんだよ|できるよ}」などのご意見をいただけましたら幸いです。