- 投稿日:2020-01-19T23:59:37+09:00
LaradockでLaravelの環境構築するもMysqlが立ち上がらない!
開発環境
- macOS Mojave 10.14.4
- Laravel6.(php7.3以上)
- Mysql8.
環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)
初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中
docker-compose up -d nginx mysql phpmyadmin
mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!エラー内容
Recreating laradock_mysql_1 ... error ERROR: for mysql Cannot start service mysql: driver failed programming external connectivity on endpoint laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use3306が既に使われているってことか?
ググる...
Laradock公式にこんな記述が
docker-compose.ymlの中のports欄を消せばよいとのお告げ
docker-compose.yml### MySQL ################################################ mysql: build: context: ./mysql args: - MYSQL_VERSION=${MYSQL_VERSION} environment: - MYSQL_DATABASE=${MYSQL_DATABASE} - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - TZ=${WORKSPACE_TIMEZONE} volumes: - ${DATA_PATH_HOST}/mysql:/var/lib/mysql - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d ports: - "${MYSQL_PORT}:3306" networks: - backendports: - "${MYSQL_PORT}:3306"
これを削除してみる...再度
docker-compose up -d nginx mysql phpmyadmin
できた!ありがとうLaradock公式!!
- 投稿日:2020-01-19T23:59:37+09:00
悲劇!LaradockでLaravelの環境構築するもMysqlが立ち上がらない!
開発環境
- macOS Mojave 10.14.4
- Laravel6.(php7.3以上)
- Mysql8.
環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)
初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中
docker-compose up -d nginx mysql phpmyadmin
mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!エラー内容
Recreating laradock_mysql_1 ... error ERROR: for mysql Cannot start service mysql: driver failed programming external connectivity on endpoint laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use3306が既に使われているってことか?
ググる...
Laradock公式にこんな記述が
docker-compose.ymlの中のports欄を消せばよいとのお告げ
docker-compose.yml### MySQL ################################################ mysql: build: context: ./mysql args: - MYSQL_VERSION=${MYSQL_VERSION} environment: - MYSQL_DATABASE=${MYSQL_DATABASE} - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - TZ=${WORKSPACE_TIMEZONE} volumes: - ${DATA_PATH_HOST}/mysql:/var/lib/mysql - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d ports: - "${MYSQL_PORT}:3306" networks: - backendports: - "${MYSQL_PORT}:3306"
これを削除してみる...再度
docker-compose up -d nginx mysql phpmyadmin
できた!ありがとうLaradock公式!!
- 投稿日:2020-01-19T20:11:24+09:00
laravelで作った画像も表示できるブックレビューの機能について ※ポートフォリオの解説です
こんなアプリを作りました
http://book-review123456.herokuapp.com/articles
書籍のカバー画像も表示できる掲示板アプリです。画像はGoogleBooksAPIsを利用して取得します。
APIを利用した画像の取得方法については下記にまとめました ↓laravelでカバー画像も表示できるブックレビューアプリを作った。※自分の学習用です
デプロイ出来れば一番良かったのですが、出来なかったのでイメージ画像と言葉で補足します。
(gitはしばらくの間はprivateにしておこうと思います。個人情報的に。特定の相手にだけ共有しようかと)機能はこんな感じです。
- 画像も表示できるブックレビューの投稿
- ログイン/新規登録
- バリデーション(ログイン時だけレビューの投稿・編集ができる)
- お問い合わせ
- 退会処理
- TwitterOAuth
- いいね機能
TwitterOAuthは個人情報的にアレかな〜と思って、gitで共有する段階ではやはり封印しました。
反省と今後の課題
機能に関する課題は別記事に書いたので、勉強について。
しばらくはLaravelではなく、生のPHPで制作し勉強した方がいいのかなと思いました。
なので、こちらの記事を参考に勉強を進めたいです。
- 投稿日:2020-01-19T19:40:50+09:00
laravelでのSlack通知実装
laravelでお問い合わせフォームを作成し、入力された内容をSlackに通知する必要があったので、そこで学習したことを残しておきます。
WebHookURLの取得
通知したいチャンネルのwebhookURLを取得する
Incoming Webhook
ここで取得したURLを.envファイルに書き込む次に、Slack通知チャンネルをlaravelに追加。
> composer require laravel/slack-notification-channelNotificationクラスの作成
次に、Slackに通知を送るためのNotificationクラスを作成する。
> php artisan make:Notification SlackNotificationapp/Notifications/SlackNotification.phpというファイルが作られる。
app/Notifications/SlackNotification.php<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; class SlackNotification extends Notification { use Queueable; protected $message; protected $channel; protected $name; protected $icon; /** * Create a new notification instance. * * @return void */ public function __construct($message) { $this->message = $message; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['slack']; } /** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\SlackMessage */ public function toSlack($notifiable) { return (new SlackMessage) ->from($this->name) ->image($this->icon) ->to($this->channel) ->content($this->message); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return [ // ]; } }処理本体の作成
次に、通知処理本体の作成。
app/Services/Slack/SlackService.php<?php namespace App\Services\Slack; use Illuminate\Notifications\Notifiable; use App\Notifications\SlackNotification; class SlackService { use Notifiable; public function send($message = null) { $this->notify(new SlackNotification($message)); } protected function routeNotificationForSlack() { return config('app.slack_url'); } }ファサードクラスを作成
app/Services/Slack/SlackFacade.php<?php namespace App\Services\Slack; use Illuminate\Support\Facades\Facade; class SlackFacade extends Facade { protected static function getFacadeAccessor() { return 'slack'; } }
routeNotificationForSlack
で、通知するURLを見に行っている。プロバイダの作成
サービスプロバイダを作成する。
> php artisan make:provider SlackServiceProviderapp/Providers/Slack/SlackServiceProvider.phpというファイルが作成される。
app/Providers/Slack/SlackServiceProvider.php<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class SlackServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->bind( 'slack', 'App\Services\Slack\SlackService' ); } /** * Bootstrap services. * * @return void */ public function boot() { // } }サービスプロバイダ、エイリアス、envを追記する
参照するenvの値は、Notification内には書かず、
config/app.php
に追加する。
これは、config:cache
された時に、.env
ファイルがロードされなくなり、nullを返すため。app/config/app.php'slack_url' => env('SLACK_URL'), 'providers' => [ App\Providers\SlackServiceProvider::class, ], 'aliases' => [ 'Slack' => App\Services\Slack\SlackFacade::class, ],あとは、以下の記述でどこでもSlack通知が届くようになる。
\Slack::send('Hello World!')
- 投稿日:2020-01-19T16:40:49+09:00
Docker+Laravelでタスクスケジューラ
目的
通常サーバのタスクスケジューラはcronにて行うが、
- 少し面倒であること
- Laravelにタスクスケジューラの機能があること
から、画面を作成して画面経由でタスクスケジュール設定を行えるものを作成してみる。
前提
下記の環境で実装した。
- PHP7.1.x
- Laravel6.x+AdminLTE3
- PostgreSQL11.4
- Docker phpfpm
サンプル仕様
サーバのタスクスケジュールのため、画面操作するためにログインが必要とした。
タスク自体は別途実装済みであるとして、タスク名と実行スケジュールを設定する画面を
用意する。
タスクの実行はLaravelにタスクスケジューラをcron経由で実行するものとして、タスクスケジューラの設定を先の画面で設定したものを反映させるものとする。タスクの実装
詳細は省略するが、タスクの雛形をartisanで作成できる。
% php artisan make:command タスク名とりあえず、タスクが動いたか確認したいため、下記のタスクを実装した。
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; class Shout extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'command:shout'; 〜省略〜 /** * Execute the console command. * * @return mixed */ public function handle() { Log::info("start shout!!"); } }見ての通り、実行されたらログが出力されるだけのタスクである。
モデル定義
定義するモデルはScrapingCommandである。
migrateの内容を下記に示す。?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateScrapingCommandsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('scraping_commands', function (Blueprint $table) { $table->bigIncrements('id'); $table->string("name",200); $table->string("cmd",200); $table->integer("cron1")->nullable(); $table->integer("cron2")->nullable(); $table->integer("cron3")->nullable(); $table->integer("cron4")->nullable(); $table->integer("cron5")->nullable(); $table->integer("flg")->default(0); $table->softDeletes(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('scraping_commands'); } }画面実装
ログイン画面
タスク一覧画面
登録されているタスクの一覧表示している。
タスクスケジュールはcronと同じ形式である。スケジュールとは別に有効/無効の設定が可能である。
タスク設定画面
タスクの設定を行う。コマンド名はartisanで使用するコマンド名(\$signatureに設定しているもの)を設定する。
設定内容はScrapingCommandモデルに保存される。
Laravel側のスケジュール定義
LaravelのタスクスケジュールはApp\Console\Kernelのscheduleメソッドで定義するため、
同メソッドを下記のように修正した。<?php namespace App\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Support\Facades\Log; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use App\ScrapingCommand; class Kernel extends ConsoleKernel { 〜省略〜 /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $lst = ScrapingCommand::where("flg",1)->get(); foreach($lst as $rec) { Log::info("cmd:".$rec->cmd); Log::info("cron:".$rec->cronStr()); $schedule->command($rec->cmd)->cron($rec->cronStr()); } } 〜省略〜 }Cron側の設定
Cron側はLaravelのタスクスケジューラを実行するだけなので、
* * * * * /usr/local/bin/php /svr/app/artisan schedule:run >> /var/log/cron.log 2>&1 (注)/svr/appはLaravelをインストールしたディレクトリ本来はcrontab -e で上記のファイルを作成することになるが、今回はDockerなので、
rootファイルとして作成しておく。以上で、要望を実装済みであるが、今回はDockerで実装したため、コンテナ上でcronのインストールが必要になる。
そこで、cronのインストールはDockerイメージ作成時に行う。よってDockerファイルは
FROM php:fpm # For composer RUN apt-get update \ && apt-get install -y gcc make zlib1g-dev libzip-dev libpng-dev unzip libmcrypt-dev libjpeg-dev libfreetype6-dev \ && docker-php-ext-install zip \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include \ && docker-php-ext-install -j$(nproc) gd # Install composer RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ && php composer-setup.php \ && php -r "unlink('composer-setup.php');" \ && mv composer.phar /usr/local/bin/composer # Set composer environment ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin # Install laravel installer RUN composer global require "laravel/installer" # PHP's DB setting RUN apt-get update \ && apt-get install -y libpq-dev \ && docker-php-ext-install pdo_mysql pdo_pgsql # Install Node.js RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \ && apt-get update \ && apt-get install -y nodejs RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime RUN apt-get install -y cron RUN mkdir /debugbar RUN chmod go+w /debugbar WORKDIR /svrとした。
次にcrontabの登録とcron起動が必要になるが、今回は手動で行うものとした。
よって、上記で作成したイメージからコンテナを起動して、コンテナ上で% cp root /var/spool/cron/crontab % service cron startを実行する。
rootファイルはあらかじめ/svrにコピーしておくこと。考察
cronの設定って結構面倒なものである。何よりわざわざサーバにログインして作業しないと行けないのである。
この仕組みだと、画面で設定するだけなので大分楽である。タスクはLaravel上で動くため、
DB接続とかログ出力とかの考慮が必要なくなる。問題点は
- crontabの設定とcronの起動をコンテナ起動後手動で行わなければならない。
Dockerイメージでcronの起動もできるらしいが、今回は割愛した- 5分毎等のスケジュールに対応できてない
これは実装の問題であるが。。。サンプルで実装したプロジェクトはGitHubにアップしたので、必要であれば参照していただきたい。
- 投稿日:2020-01-19T16:19:10+09:00
Auth0のCustom SignUpをLaravelで実装してみた
はじめに
最近Auth0を使い始めまして、久しぶりに素晴らしいサービスに出会えたと感謝している毎日です。
その中で、Auth0を使って少々詰まったユースケースがあったのでご紹介しようと思います。実装例にはLaravelを用いていますが、ポイントはどの言語でも変わらないと思われます(たぶん)。
概要
Auth0は認証を中心としたエコシステムを提供しているマネージドサービスになります。
ユーザーの登録、ログイン、ログアウト、メール検証、二段階認証など様々なユースケースに対応しています。僕がAuth0を使って実現したかったのは、SignUp時の追加情報入力フローです。
というのも、Auth0のデフォルトのSignUpでは、Emailとパスワードか、Social Providerに登録済みの情報しか取得することができません。
アプリケーションによっては足りない情報の入力を促したり、SignUp時に必須で取得したい情報が存在すると思います。
そのようなSignUp時の追加情報入力フローをAuth0 Rulesを使って実装してみました。Custom SignUpのフロー
Auth0でCustom SignUpを実現するにはいくつか方法が存在します。
公式ドキュメントに記載されてはいる方法だと以下の3つです。
- Auth0のSignUpページをカスタマイズする
- 自分でログインページを作成しホスティングする
- 別ページにRedirectさせる
- Progressive profilingをする
各方法について見ていきます。
1. Auth0のSignUpページをカスタマイズする
1.のやり方はAuth0がホスティングしているセントラル認証サーバー上で入力する項目をカスタマイズすることができます。
Auth0の提供しているLockというライブラリを使えば簡単に項目を追加することができます。
しかし、複数アプリケーションから利用されるようなユースケースの場合だと、アプリケーション固有の項目を入れるとなると、かなり複雑な処理を書く必要が出てきます。
また、Social ProviderでSignUpされた場合には、対応することができないので注意が必要です。2. 自分でログインページを作成しホスティングする
Lockでは、求めるUI/UXを実現できない!となった場合は、自分でログインページを作成することも可能です。その場合、Auth0のAPIを用いて、認証を行うことになります。
自分でUIを作成してしまえば、複雑な認証プロセスをいくらでも実現することが可能となります。しかし、Auth0が用意している認証エコシステムが一部利用不可能になったり、実装コストがかかってしまうというデメリットも抱えています。
ちなみに、LockとCustom UIについては、公式ドキュメントで比較されているので、ご興味がある方は参照してみてください。3. 別ページにRedirectさせる
今回採用したのはこの方法です。
Auth0のホスティングするログインページでSignUp後、自分がホスティングする追加情報入力ページにリダイレクトさせ、情報を入力してもらうという方法です。
この方法を用いると、Auth0の機能をふんだんに使いつつ、自分のアプリケーション用にカスタマイズされた追加項目の入力をユーザーに求めることが可能です。
また、Social Provider経由のSignUpでもEmail/PWでのSignUpでも、同じフローで追加項目の入力を求めることが可能になります。これの実現方法についても。公式ドキュメントにかかれています。
4. Progressive profilingをする
SignUp時には最低限の情報しか求めず、次回ログイン時や、アクセス時に追加で情報を求めるというのが、Progressive profilingです。
名前もかっこいいし、登録率も増えそうな方法ですが、UX設計のハードルが少々高そうだったので、今回は見送りました。Progressive profilingの例もAuth0の公式ドキュメントで提供されています。
ご興味がある方はご参照ください。RedirectでCustom SignUp
Auth0でSignUp中にRedirectする場合には、Auth0 Rulesというサービスを使います。
Auth0 Rulesは認証トークンを発行する前に呼び出されるサーバレスな関数だと思って頂ければよいかと思います。Rulesを使ったRedirectのSignUpのフローを図にすると以下のような感じです。
図の
7.
でAuth0 Rulesを使って、Custom SignUpページにRedirectされています。
この時、認証は一度中断されるため、ここでユーザーが離脱した場合には、認証状態はなりません。
(実際には6.
の時点で、Auth0上にユーザーが作成されるようですが)認証フローを再開する場合には、
https://{YOUR_AUTH0_DOMAIN}/continue?state={STATE}
にリクエストを送る必要があります。
(state
パラメーターについては、7.
でRedirectされる際に、URLのクエリパラメーターにくっついてきます。)認証フローが再開し、その後の処理が無事終了すると、認証済みになり、トークンが発行され、ログイン状態になります。
Rulesの作成
実際にRulesを作成してみます。
既にAuth0のアカウントは作成済みという前提で進めます。
- Auth0のDashboardのサイドメニューにあるRulesをクリックします。
CREATE RULE
をクリックし、Empty rule
を選択します- Scriptに以下のコードを貼り付けます
function (user, context, callback) { const hasConsented = user.app_metadata && user.app_metadata.has_consented; if (!hasConsented && context.protocol !== 'redirect-callback') { // urlはアプリケーションのURLに差し替える context.redirect = { url: 'http://localhost:3000/signup' }; } if (!hasConsented && context.protocol === 'redirect-callback') { user.app_metadata = { has_consented: true }; user.user_metadata = context.request.body; auth0.users.updateAppMetadata(user.user_id, user.app_metadata); auth0.users.updateUserMetadata(user.user_id, user.user_metadata); } callback(null, user, context); }Saveすると準備完了です。
6行目のcontext.redirect = {url }
でRedirect先のURLを指定しています。2つ目のifの中身は
https://{YOUR_AUTH0_DOMAIN}/continue?state={STATE}
にリクエストが送られた後の処理になります。
認証プロセスが再開されると、context.protocol
にredirect-callback
という値が挿入されます。
ここで、Custom Signupが完了しているユーザーなのか判別するために、app_metadata
に{ has_consented: true }
という値を入れています。Laravel Application側
Redirectの受け先のLaravel Applicationを作成します。
既にLaravelはセットアップ済みという前提で進めます。
(セットアップがまだの方はこちらを参考にしてください。)<?php # routes/web.php Route::get('/signup', SignupController@main)->name('signup'); Route::post('/signup', SignupController@signup);<?php # app/Http/Controllers/SignupController.php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class SignupController extends Controller { public function main(Request $request) { $state = $request->input('state'); // formにstateの値をhiddenで入れる return view('signup', ['state' => $state]); } public function signup(Request $request) { $state = $request->input('state'); $config = config('laravel-auth0'); $url = 'https://'.$config['domain'].'/continue?state='.$state; return redirect($url, '307'); } }ここでの注意点は、
/continue
にRedirectさせる時に、redirect($url, '307')
とすることです。
第2引数はHttpステータスコードを指定しています。laravelの
redirect()
はデフォルトでは302
でリダイレクトされるのですが、302
だとリダイレクトする際にブラウザ側で勝手にGETリクエストに変換されてリクエストが送信されてしまうので、POSTのBodyが全てなくなってしまいます。
そこで、Httpステータスコードを307
でリダイレクトさせることで、POSTのままリダイレクト先にリクエストを送信でき、Bodyの情報も無事渡すことが可能になります。
signup()
でリダイレクトされた後は、再びAuth0のRulesが発火されるといった寸法です。ここで、無事認証が終了すると指定したコールバックURLにトークン付きでリダイレクトされ、ログイン状態になるといった寸法です。
まとめ
Laravelを用いてAuth0のCustom Signupを実装してみました。
Custom Signupを利用することで、アプリケーション固有の入力項目にも対応することができ、かつ、Redirectの場合であれば、Social ProviderでSignupしたユーザーにも対応することができるようになりました。
少しの手間で、Custom Signupを実現することができ、Auth0の凄さには驚きを隠せません。ただ、認証再開時の実装については、あまり文献がなかったので、見事にハマってしまいましたが、良さげな解決法が見つかってよかったです。
しかし、Httpステータスコードの307
に対応していないブラウザ(特にモバイルが心配)もある可能性もあり、そこについてはまだ調査していないので、実際に利用する際にはご注意ください...。
(RFCで定められているから、大丈夫だと思うんだけど...一応)。
- 投稿日:2020-01-19T15:07:39+09:00
Laravel: 複数のDBに接続
mysqlとSQLite3の同時使用例
php.iniの場所
$ php -r "echo phpinfo();" | grep "php.ini"php.ini//使用するDBをコメントアウト extension=pdo_mysql extension=sqlite3httpdの再起動
データベースの作成設定
.envDB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE_1={データベース名} DB_USERNAME=root DB_PASSWORD=null DB_CONNECTION=sqlite DB_DATABASE_2={パス/データベースファイル名}config/database.php'default' => env('DB_CONNECTION_1', 'mysql'), //略 'sqlite' => [ 'driver' => 'sqlite', 'database' => env('DB_DATABASE_2', database_path({フォルダパス})), 'prefix' => '', ], 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE_1', 'forge'), 'username' => env('DB_USERNAME_1', 'forge'), 'password' => env('DB_PASSWORD_1', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'engine' => null, ],migration//デフォルトDBにはconnectonメソッドは必要なし Schema::connection('sqlite')->create({テーブル名}, function (Blueprint $table) { }データベースを指定してマイグレーション
$ php artisan migration --database=sqliteModelでの接続設定
Modelclass Datas extends Model { protected $connection = 'sqlite'; }