■目次
概要より良い設計をするために前提知識Homesteadによる開発環境構築Laravel インストールの補足Laravel のバージョンを指定してインストールHomesteadにサブドメインと作業ディレクトリを追加Homesteadに同一LAN内からアクセスHomesteadからメール送信gitの設定アプリケーションの作成例認証マルチ認証(管理者とユーザで別々に認証)API認証(API利用時にPassportでOAuth2認証)強制ログイン更新(他の開発者の更新を取り込む手順)設計ルーティングテンプレートファイルデータベーステスト・デバッグメモトラブル対応HomesteadによるPHP5.6開発環境構築(旧メモ)Laravel 5.1 クイックスタート(旧メモ)
■概要
Laravel - Wikipedia https://ja.m.wikipedia.org/wiki/Laravel Laravelソースコードの歩き方 https://kore1server.com/326 Laravelのここがすごい - Qiita http://qiita.com/nunulk/items/78d70ac809948b470bbc ■特徴 ・Symfonyベース。他にも大量のライブラリ。重いとは言われている ・Composerでインストール&依存解決 ・ファイル構成は開発者任せ。規約で強制はされない。Ver5からMVCではない ・artisan(アルチザン / 「職人」の意味)でプログラムの雛形作成など ・DIとサービスコンテナが重要 ・Illiminateという名前空間 https://kore1server.com/326 ・ファサード(後にStatic proxyと呼び方変更)でどこからでも簡単に静的に命令を呼べる ただしファサードは強制されず他の呼び出し方もできるので、好みに合った形式を選択できる ・マイグレーションとシーダーに対応 ・データベースは代理キーでの管理が前提 ■バージョンごとの差(ググって解説を探すとき混乱しないように) ・Laravel3まではシンプルで理解しやすい、が売りだった。4からは大きいシステムの制作に耐えられるよう大幅に改良された ・4まではMVCだが、モデルの扱いが曖昧なので5ではMVCを捨てた。モデルの置き場所は開発者が決める ・5からはappはアプリケーションのロジックのみの置き場に ■どのバージョンを使うべきか 2017年9月時点で、LTS版は5.1と5.5 PHP7環境なら、5.5を使っておくといい PHP5環境なら、5.1か5.4のどちらかになりそうだが、サポート終了期間に大差ないので5.4で良さそう Laravelのリリース日と修正期間のまとめ - Qiita http://qiita.com/ykhirao/items/576a9eea735f91350072 ■軽量版 Laravelの軽量版としてLumenがある LumenとLaravelの違い - ララ帳 https://laravel10.wordpress.com/2015/04/21/lumen%E3%81%A8laravel%E3%81%AE%E9%81%95%E3%81%84/ ■2018年11月時点での良さそうな入門記事 Laravel入門情報 2018年10月版 - Qiita https://qiita.com/studio15/items/8519fed2d6d5b1a573c0 PHPフレームワークLaravel5.5チュートリアル、CRUD一回り作ってみる - 名古屋のWebシステム開発 iNet Solutions https://www.inet-solutions.jp/technology/laravel-tutorial/
■より良い設計をするために
Laravelでウェブアプリケーションをつくるときのベストプラクティスを探る - Qiita http://qiita.com/nunulk/items/b1e2da51b5dabab92da0 Laravel5のアーキテクチャから学ぶより良いクラス設計 - Qiita http://qiita.com/nunulk/items/2c637d3952096ef74677 LaravelのORMで初心者から職人へ - Qiita http://qiita.com/henriquebremenkanp/items/cd13944b0281297217a9 Laravel Recipes日本語版 http://recipes.laravel.jp/ Laravel リファレンス https://book.impress.co.jp/books/1114101107 https://github.com/laravel-jp-reference/chapter8 ■Laravel経験者に話を聞いたときのメモ(2017/09/13) ・バージョン5.4を採用した ・Homesteadはあくまでも開発環境用として使った。本番環境はEC2にPHPなどをインストールして構築した ・本番反映は「デプロイ → マイグレーション → シード」 ・基本構成は MVC + Service + Repository ・DIとEloquentには慣れが必要だった ・「php artisan make:auth」を使うとカラム名の変更などが大変なので、認証はmiddlewareを使って一から作った
■前提知識
■Composer PHPの依存性管理ツール 依存ライブラリ郡も含めて、まとめてインストールできる composer require 使いたいライブラリ もしくは composer.json に使いたいライブラリを記述して composer install とコマンドを実行するとインストールされる 自分で一つ一つ依存ライブラリをダウンロードする必要が無い ダウンロードしたファイルは vendor フォルダ内に格納される PHP開発でComposerを使わないなんてありえない!基礎編 - Qiita http://qiita.com/niisan-tokyo/items/8cccec88d45f38171c94 vendor フォルダは通常gitの管理対象外とする インストールしたライブラリの情報は composer.json と composer.lock に記録され、 「composer install」を実行すると同じバージョンのライブラリをダウンロードしてくれる つまり、他の環境でも同じ状態を再現できる ただし運用方法によっては、vendor をgit管理対象にするのも有効。メリット&デメリットを考える Laravel5のプロジェクトをGitで管理する - Qiita https://qiita.com/zaburo/items/bc448a9fbf2d35194302 php - vendorディレクトリをGit管理下に置かないのは何故? - スタック・オーバーフロー https://ja.stackoverflow.com/questions/23725/vendor%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%... ■オートローディング 名前空間に従って自動的にファイルを読み込む require_once などで明示的にファイルを読み込む必要がない Composerを使うことで簡単に導入できる PHPでクラスをspl_autoload_registerを使ってオートロードする - バカンス駆動開発 http://egapool.hatenablog.com/entry/2013/11/17/195045 PHPのオートロード(autoload) - Qiita http://qiita.com/atwata/items/5ba72d3d881a81227c2a src/Refirio/Greeting/Hello.php
<?php class Hello { function say() { echo 'Hello World!'; } }
sample.php
<?php require_once "src/Refirio/Greeting/Hello.php"; $hello = new Hello(); echo $hello->say();
↓オートロードに対応させる composer.json
{ "autoload": { "psr-4": { "myapp\\": "src/" } } }
composer install … composer.json に設定した内容に合わせて vender ディレクトリ内にプログラムが作られる 以下のようにしてオートロードを利用できる src/Refirio/Greeting/Hello.php
<?php namespace myapp\Refirio\Greeting; class Hello { function say() { echo 'Hello World!'; } }
sample.php
<?php require_once "vendor/autoload.php"; $hello = new myapp\Refirio\Greeting\Hello(); echo $hello->say();
↓名前空間のエイリアスを作成
<?php require_once "vendor/autoload.php"; use myapp\Refirio\Greeting\Hello; //use myapp\Refirio\Greeting\Hello as Hello; // クラスに別名を指定する場合 $hello = new Hello(); echo $hello->say();
■trait 限定的な多重継承を行う PHP5.4 の新機能 trait のまとめと実際の利用例 | 株式会社インフィニットループ技術ブログ https://www.infiniteloop.co.jp/blog/2014/08/php54-traits/ PHP5.4以降で導入されたトレイト(trait)というしくみ - ts0818のブログ http://ts0818.hatenablog.com/entry/2015/10/15/183555
<?php trait Facebook { function post($body) { echo 'Facebookに投稿しました:' . $body . "\n"; } } trait Twitter { function post($body) { echo 'Twitterに投稿しました:' . $body . "\n"; } } class Mailer { public function send($body) { echo 'Mailerで送信しました:' . $body . "\n"; } } class SendmailMailer extends Mailer { use Facebook; /* // 同名のメソッドがある場合 use Facebook, Twitter { Facebook::post insteadof Twitter; // instead of ... 代わりに Twitter::post as tweet; // as ... として } */ public function send($body) { echo 'Sendmailで送信しました:' . $body . "\n"; } public function getPath() { return '/usr/sbin/sendmail'; } } $mailer = new SendmailMailer(); $mailer->send('こんにちは!'); $mailer->post('こんにちは!'); //$mailer->tweet('こんにちは!');
■リフレクション クラスに関する情報を参照したり、変更したりすることができる PHPでリフレクション : アシアルブログ http://blog.asial.co.jp/751 パラメータとしてタイプヒントされたクラスを取得 http://php.net/manual/ja/reflectionparameter.getclass.php タイプヒントからクラスを取得する例
class Message { } class Hello { function say(Message $message) { } } $reflection = new ReflectionClass('Hello'); $parameters = $reflection->getMethod('say')->getParameters(); echo $parameters[0]->getClass()->getName(); // 「Message」と表示される
■デザインパターン 勉強メモ http://refirio.org/page/memo/design_pattern http://refirio.org/memos/study/20161028_design_pattern/slide/ ■DI DIとは?DIコンテナとは?試してみた(前編)[PHP][DI] - あざらし備忘録。 http://shiro-goma.hatenablog.com/entry/2014/06/22/102236 Dependency Injectionを特定のDIコンテナに頼らず実現する - Qiita http://qiita.com/Hiraku/items/48fbdfca4b63c74494e9 DI・DIコンテナ、ちゃんと理解出来てる・・? - Qiita http://qiita.com/ritukiii/items/de30b2d944109521298f DIコンテナの本当の使いどころ | 技術トピックス | ウルシステムズ株式会社 https://www.ulsystems.co.jp/topics/025 勉強メモ http://refirio.org/page/memo/di ■ドメイン駆動設計 厳密にドメイン駆動設計に沿っているわけでは無いようだが、知っておくと理解が進みそう ドメイン駆動設計 http://qiita.com/haazime/items/6119097071149a362f7f Domain駆動開発入門 | キャスレーコンサルティング 技術ブログ http://www.casleyconsulting.co.jp/blog-engineer/%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%... ■Mockery 作成前のクラスを、すでに存在するかのようにテストできる 例えば
<?php class SampleExecuter { public function doSomething($checkType, $param) { $factory = new CheckerFactory($checkType); $checker = $factory->create(); if ($checker->checkSomething($param)) { return 'trueが返ったよ'; } else { return 'falseが返ったよ'; } } }
このような SampleExecuter があった場合、このテストは CheckerFactory が完成するまで行うことができない が、モックとなる CheckerFactory を作り、テストできるようにする MockeryとPHPUnitを使う(最低限) - Qiita https://qiita.com/zaburo/items/b559782179565bb1c538 Mockery 0.8.0 日本語ドキュメント https://kore1server.com/202/Mockery+0.8.0+%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%83%89%E3%82%AD%E3%83%A5%E3%... 「Mockery」を使ってサクッとPHPのテストを書いてみる | アライドアーキテクツ エンジニアブログ http://tech.aainc.co.jp/archives/3918 composerでインストール (「--dev」をつけると通常の「composer install」ではインストールされないものにできる。「composer install --dev」とする。) cd C:\localhost\home\test\public_html\mockery composer require --dev phpunit/phpunit composer require --dev mockery/mockery tests\test1.php を作成して以下を入力
<?php require_once 'vendor/autoload.php'; class SampleTest1 extends PHPUnit_Framework_TestCase { public function testSample1() { //Mockを設定 $m = \Mockery::mock('Hoge'); $m->shouldReceive('Hello') ->with('foo') ->andReturn("Hello foo"); //Helloメソッドを実行 $m->Hello('foo'); $this->assertEquals("Hello foo", $m->Hello('foo')); } }
以下のコマンドでテストを実行 vendor\bin\phpunit tests/test1.php PHPUnit 5.7.22 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 85 ms, Memory: 3.25MB OK (1 test, 1 assertion)
■Homesteadによる開発環境構築
Homesteadをインストールするだけで Ubuntu 16 + PHP 7 + Nginx + MySQL の環境を構築できる ※Dockerによる開発環境構築は、docker.txt の「その他の環境構築例」と「Laradockによる開発環境構築」を参照 Homesteadは、1つのPCに複数インストールできない(無理矢理インストールすることは不可能では無いようだが) 案件名をもとにしたフォルダ内にHomesteadをインストールせず、「homestead」という場所にインストールして使うと良さそう 詳細は「環境構築のトラブル事例」を参照 Laravel Homestead 5.5 Laravel https://readouble.com/laravel/5.5/ja/homestead.html ↑インストール方法など詳しく書かれている 【Laravel超入門】開発環境の構築(VirtualBox + Vagrant + Homestead + Composer) - Qiita http://qiita.com/7968/items/97dd634608f37892b18a Laravel HomesteadでLaravel5.4の環境を作りながら、レンタルサーバにwebアプリケーションを公開する(追記あり) - Qiita http://qiita.com/Fendo181/items/a6b9017f6ef490995aba Windows10でLaravel Homestead環境構築 - Qiita http://qiita.com/ricoirico/items/9745160bcf9983fa30ad Laravel Homestead - Laravel - The PHP Framework For Web Artisans https://laravel.com/docs/5.5/homestead 以下、VirtualBox + Vagrant の環境は構築済みとする ■Homestead Vagrant Box のインストール >cd C:\vagrant >vagrant box add laravel/homestead ==> box: Loading metadata for box 'laravel/homestead' box: URL: https://atlas.hashicorp.com/laravel/homestead This box can work with multiple providers! The providers that it can work with are listed below. Please review the list and choose the provider you will be working with. 1) parallels 2) virtualbox 3) vmware_desktop Enter your choice: … virtualboxで使うので2を選択 ==> box: Adding box 'laravel/homestead' (v3.0.0) for provider: virtualbox box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/3.0.0/providers/virtualbox.box box: Progress: 100% (Rate: 3645k/s, Estimated time remaining: --:--:--) ==> box: Successfully added box 'laravel/homestead' (v3.0.0) for 'virtualbox'! … 5分ほどで完了 ■Homesteadのインストール ここでは C:\vagrant\homestead にインストールするものとする ※作業ファイルではなくHomesteadの仕組みそのもののインストールなので、 案件ごとの作業領域ではなく C:\vagrant\homestead などHomestead用の領域にインストールする方が良さそう >cd C:\vagrant\homestead >git clone https://github.com/laravel/homestead.git Homestead >cd Homestead >git checkout v6.2.2 … 最新の安定バージョンは https://github.com/laravel/homestead/releases で確認。v6は「v6.6.0」が最終版 >init.bat … Windowsの場合。Mac/LinuxもしくはWindowsgit bash環境なら「bash init.sh」とする インストールしたHomesteadのバージョンは、以下に記載されている C:\vagrant\homestead\Homestead\bin\homestead $app = new Symfony\Component\Console\Application('Laravel Homestead', '6.2.2'); ■鍵の作成 gitを使っているなら Git Bash で作成できる 恐らくPoderosaなどを使っても大丈夫と思われる WindowsでGitを始めたらまず確認!Git Bashの設定&ショートカット | 株式会社グランフェアズ http://www.granfairs.com/blog/staff/gitbash-setting-shortcut $ mkdir ~/.ssh $ cd ~/.ssh $ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/refirio/.ssh/id_rsa): … 空ENTER Enter passphrase (empty for no passphrase): … 空ENTER Enter same passphrase again: … 空ENTER Your identification has been saved in /c/Users/refirio/.ssh/id_rsa. Your public key has been saved in /c/Users/refirio/.ssh/id_rsa.pub. The key fingerprint is: SHA256:750v6UHSVnX/ymH37ZbFIQTM94y3Tlhad4WPXfpCIS8 refirio@DESKTOP The key's randomart image is: +---[RSA 2048]----+ | o.. .o| | o.oo.=| | oo=*+| | .E+=B*| | S . +oXoB| | . + =.**| | . ..=.=| | . .oo +.| | ..+o...| +----[SHA256]-----+ これで C:\Users\ユーザ名\.ssh 内に鍵ファイルが作成される。ここはHomesteadからみると ~/.ssh/ にあたる ■Homesteadの設定 C:\vagrant\homestead\Homestead\Homestead.yaml を編集する 鍵の設定は以下にあるが、上の手順で作成した場所がデフォルトとなっているため変更は不要 他の方法で鍵を作成した場合、必要に応じて設定を変更する
authorize: ~/.ssh/id_rsa.pub keys: - ~/.ssh/id_rsa
■hostsの設定 http://homestead.test/ でアクセスできるようにする C:\Windows\System32\drivers\etc\hosts 192.168.10.10 homestead.test ■Vagrantの起動 cd C:\vagrant\homestead\Homestead vagrant up しばらく待つ…がエラーが表示された Windows10環境での不具合で、最新版のVirtualBoxでは修正されているらしい Progress state: E_INVALIDARG VBoxManage.exe: error: Failed to create the host-only adapter VBoxManage.exe: error: Assertion failed: [!aInterfaceName.isEmpty()] at 'F:\tinderbox\win-5.1\src\VBox\Main\src-server\HostNetworkInterfaceImpl.cpp' (74) in long __cdecl HostNetworkInterface::init(class com::Bstr,class com::Bstr,class com::Guid,enum __MIDL___MIDL_itf_VirtualBox_0000_0000_0038). VBoxManage.exe: error: Please contact the product vendor! VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component HostNetworkInterfaceWrap, interface IHostNetworkInterface VBoxManage.exe: error: Context: "enum RTEXITCODE __cdecl handleCreate(struct HandlerArg *)" at line 94 of file VBoxManageHostonly.cpp Windows8.1 から Windows10 にUpgrade して発生したvagrantのエラー - Qiita https://qiita.com/joniyjoniy/items/8176ece2b57c5a40121e https://www.virtualbox.org/ticket/14040 から VBox-Win10-fix-14040.exe をダウンロードし、管理者として実行。再度起動コマンドを試すとVagrantが起動した その後 http://homestead.test/ にアクセスすると、「No input file specified.」とだけ表示される いったん成功 Vagrantを終了する場合は以下のコマンド vagrant halt ■SSHで接続 「vagrant ssh」コマンドを使うと、SSHで接続するための情報が表示される Poderosaを使って以下の情報で接続する >vagrant ssh `ssh` executable not found in any directories in the %PATH% variable. Is an SSH client installed? Try installing Cygwin, MinGW or Git, all of which contain an SSH client. Or use your favorite SSH client with the following authentication information shown below: Host: 127.0.0.1 Port: 2222 Username: vagrant Private key: C:/localhost/home/homestead/public_html/Homestead/.vagrant/machines/homestead-7/virtualbox/private_key ■プロジェクトを作成 ※最初は案件用ではなく、動作確認用のプロジェクトを作成する方がいいかも その後、別途サブドメインと作業フォルダを作る方がいいかも C:\vagrant\homestead\code を作成 C:\vagrant\homestead\Homestead\Homestead.yaml を編集(folders の map のみ)
folders: - map: C:\vagrant\homestead\code to: /home/vagrant/code
この状態でVagrantを再起動 C:\vagrant\homestead\code\public\index.html を作成すると、これが http://homestead.test/ に表示される(SSH経由で /home/vagrant/code/public/index.html を作成しても同じ) このディレクトリの内容は C:\vagrant\homestead\code と同期している ここまで来れば最低限、Homesteadを開発環境として使用できる 以降で引き続き、Laravelのインストールを試す ■Laravelインストール いったん /home/vagrant/code/public/ を削除しておく $ cd /home/vagrant/code $ sudo composer self-update $ composer create-project laravel/laravel laravel --prefer-dist … とても時間がかかる Installing laravel/laravel (v5.5.0) - Installing laravel/laravel (v5.5.0): Downloading (100%) Created project in Laravel 〜略〜 Application key [base64:WHQKmMRLnXNH9uw3CQRl+fgcwPdwhx9CftI0vBLdgFE=] set successfully. 上でインストールしたLaravelの公開ディレクトリは /home/vagrant/code/laravel/public なので変更する
sites: - map: homestead.test to: /home/vagrant/code/public ↓ sites: - map: homestead.test to: /home/vagrant/code/laravel/public
Vagrantを再起動するも、設定は反映されない Homestead.yaml の sites 設定は「初回起動時にsitesの内容をもとに、Nginxの設定ファイルを調整する」という動作みたい つまり2回目以降、上記設定は無視される Laravel : Homesteadで開発環境を構築 | DN-Web64 http://www.dn-web64.com/archives/web/homestead/ 別途Nginxの設定ファイルを編集し、Nginxを再起動する $ sudo vi /etc/nginx/sites-available/homestead.test … バージョンによっては「homestead.app」という名前になっている?
server { listen 80; listen 443 ssl http2; server_name homestead.test; #root "/home/vagrant/code/public"; root "/home/vagrant/code/laravel/public"; … 公開ディレクトリを変更
$ sudo service nginx restart 以下のURLでLaravelの画面が表示される http://homestead.test/ ■Laravel動作確認 $ cd /home/vagrant/code/laravel/ $ php artisan --version Laravel Framework 5.5.13 引き続き、Composerやデータベース接続などを試す 以下のページも参考になりそう Laravel : Homesteadで開発環境を構築 | DN-Web64 http://www.dn-web64.com/archives/web/homestead/ 実際の作業環境追加は「Homesteadにサブドメインと作業ディレクトリを追加」の項目を参照 ■環境構築のトラブル事例1 vagrant up 後「Warning: Authentication failute. Retrying...」が出続けてタイムアウトエラーが出る (vagrant 自体は起動するがなんか気持ち悪い) [原因] ssh の秘密鍵とゲストOS で設定されている公開鍵が合っていない為発生。 [解決方法] http://kiraba.jp/vagrant-error-authentication-failure-retrying/ こちらのサイトの通りにやることで Warning は出ずに起動するようになった。 ■環境構築のトラブル事例2 vagrant が起動した後、サイトにつながらない (http://homestead.test/ が見れない) [原因] 一度不完全な状態で起動した(上記の Warning など)為、nginx の設定ファイルが正常に作成されていない為。 [解決方法] vagrant halt で一旦停止後、 vagrant up --provision で起動する。 --provision を付与することで再度 Homestead.yaml に沿って設定ファイルを作成してくれる。 これで http://homestead.test/ にアクセスすると「No input file specified.」が表示されるようになった。 vagrant up --provision すれば鍵の不一致も解決するかも(?)。 試していないので断定はできないが、あとで見ると鍵がコピーされていた。 ■環境構築のトラブル事例3 複数のHomesteadを起動できない - - - - - - - - - - - - - - - - - - - - C:\localhost\home\asims2\public_html\Homestead>vagrant up Bringing machine 'homestead-7' up with 'virtualbox' provider... ==> homestead-7: Box 'laravel/homestead' could not be found. Attempting to find and install... homestead-7: Box Provider: virtualbox homestead-7: Box Version: >= 4.0.0 ==> homestead-7: Loading metadata for box 'laravel/homestead' homestead-7: URL: https://atlas.hashicorp.com/laravel/homestead ==> homestead-7: Adding box 'laravel/homestead' (v4.0.0) for provider: virtualbox homestead-7: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/4.0.0/providers/virtualbox.box ==> homestead-7: Box download is resuming from prior download progress homestead-7: Progress: 100% (Rate: 101k/s, Estimated time remaining: --:--:--) ==> homestead-7: Successfully added box 'laravel/homestead' (v4.0.0) for 'virtualbox'! ==> homestead-7: Importing base box 'laravel/homestead'... ==> homestead-7: Matching MAC address for NAT networking... ==> homestead-7: Checking if box 'laravel/homestead' is up to date... A VirtualBox machine with the name 'homestead-7' already exists. Please use another name or delete the machine with the existing - - - - - - - - - - - - - - - - - - - - 「homestead-7」はすでに存在すると言われた Oracle VM VirtualBox(GUIツール)を立ち上げて、以前作成した「homestead-7」を削除して、再度「vagrant up」を実行した Homesteadはそのままでは複数起動できないらしい box名を変更するなどの対応が必要らしい 1つのboxで複数のプロジェクトを管理する方が無難かも 同一のboxだとPHPのバージョンを変更したりが難しいが、 Homesteadは「特定の案件に合わせた環境を構築する」というより「最新版Laravelの開発環境を構築する」ものなので、 以下の方法でディレクトリを分ける程度の方がいいかも Homesteadを使って複数のLaravelプロジェクトを作成する簡単な方法 - Qiita https://qiita.com/Yorinton/items/08ec8fefcbec71513399 複数のLaravel環境をHomestead上で動かす - Qiita https://qiita.com/miutex/items/ebc13fc78da4a19a3da4 以下のような方法も紹介されているが、Homesteadがバージョンアップした場合に対応できなるなる可能性がありそう また、「何故か一部だけ動作しない」になったときに解決が難しいかも Laravel5.2のHomesteadを複数たててみた - Qiita https://qiita.com/you-me/items/b886dd0fc8e5047c4bc6 Homesteadを複数作りたい場合にやること - めものようなもの http://fumikony.hatenablog.com/entry/2015/04/22/145639 現状以下が良さそうかも ・Homestead環境は一つで、サブドメインで分岐させる ・例えば C:\localhost\home\homestead にHomesteadを配置して /home/vagrant/code と同期して、 /home/vagrant/code/laravel/public で最初のLaravelの動作確認をして、 /home/vagrant/code/test1/public /home/vagrant/code/test2/public などにプロジェクトを配置して laravel.homestead.test test1.homestead.test test2.homestead.test などでアクセスできるようにする。これなら過去案件への影響も小さい ・データベースも分ける
■Laravel インストールの補足
※以下検証したときは Laravel 5.4 だったが、特に理由がなければ今なら Laravel 5.5 を使用するといい 5.4 Laravel https://readouble.com/laravel/5.4/ja/ リリースノート 5.4 Laravel https://readouble.com/laravel/5.4/ja/releases.html Composerのcreate-projectが何をやっているのか調べてみた - Qiita http://qiita.com/DQNEO/items/74f4bb8fe447e4582a97 以下の内容も踏まえて、実際に簡易ブログを作ったときの手順を Laravel_Blog.txt に記載している また、実際にEC2上にLaravel環境を構築したときの手順を Laravel_EC2.txt に記載している ■インストール(インストーラーを使う場合) https://readouble.com/laravel/5.4/ja/installation.html 解説どおり global を付けると、Windows環境では C:/Users/XXX/AppData/Roaming/Composer に作成されるみたい 少し試すくらいなら、以下のように global なしでインストールする方が手軽かも composer require "laravel/installer" ただし基本的には、以下で紹介するようにプロジェクトの作成で良さそう ■インストール(プロジェクトを作成する場合) https://readouble.com/laravel/5.4/ja/installation.html composer create-project --prefer-dist laravel/laravel blog create-project ... プロジェクトを作成 --prefer-dist ... 安定版をダウンロード laravel/laravel ... Laravelをダウンロード blog ... 「blog」ディレクトリ内に作成 以下のようにアクセスすると、Laravelの初期画面が表示される http://localhost/~test/laravel/test/public/ Laravelのバージョンを指定してプロジェクトを作成する場合、以下のようにする composer create-project --prefer-dist laravel/laravel blog 5.3 Composerで古いバージョンのLaravelをインストールする - Qiita http://qiita.com/busyoumono99/items/c207b2bd8d388d41adad ■日本語化 リポジトリに含まれているファイルのコメントを日本語に翻訳する 最初に行っておくと良い cd blog composer require laravel-ja/comja5 vendor\bin\comja5 … コマンドの使用方法を表示 vendor\bin\comja5 -c … コメントを翻訳 vendor\bin\comja5 -f … 日本語の言語ファイルを resouces/lang/ja 内に作成 laravel-ja/comja5: コメント日本語変換 https://github.com/laravel-ja/comja5 ■設定 /config/ 内に設定ファイルがある。例えば /config/app.php に「'name' => env('APP_NAME', 'Laravel'),」のような設定があるが、これは /.env の設定が反映される /.env は環境ごとの設定をまとめておくためのファイル /.gitignore によって、はじめからgitの管理対象外にされている 初めてのLaravel 5.1 : (6) データベースの環境設定 https://laravel10.wordpress.com/2015/02/21/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3... フレームワーク本体は以下にある /vendor/laravel /vendor/symfony ■タイムゾーンとロケールを設定 /test/config/app.php
'timezone' => 'Asia/Tokyo', 'locale' => 'ja',
■データベース utf8mb4_unicode_ciでデータベースを作成しておき、 /.env に接続情報を設定する。以下は設定例
DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=test DB_USERNAME=root DB_PASSWORD=1234
■Laravel のバージョンを指定してインストール
$ composer create-project --prefer-dist laravel/laravel blog ... インストール可能な最新バージョンをインストール $ composer create-project --prefer-dist "laravel/laravel=5.5.*" blog ... バージョンを指定してインストール $ cd blog/ $ php artisan --version Laravel Framework 5.5.44 Laravel(ララベル)のバージョンを指定してプロジェクトを作成する方法 http://engineer-ez.net/create-laravel-specify-version-project/
■Homesteadにサブドメインと作業ディレクトリを追加
※動作確認用の /home/vagrant/code/laravel とは別に、案件ごとの作業領域を作る ■Homesteadの設定 C:\localhost\home\Homestead\Homestead.yaml のsite部分に以下を追記(必要に応じて、databases にもデータベース名を追加しておくといいが、別途手動で作成してもいい)
- map: blog.homestead.test to: /home/vagrant/code/blog/public
■起動 Homesteadをオプション付きで再起動(Homestead.yaml の内容が再読込される) vagrant up --provision [Laravel] Homestead環境でHomestead.yamlの設定を修正した時の反映方法 https://agatayoshimi.blogspot.com/2015/10/laravel-homesteadhomesteadyaml.html 環境によっては以下のファイルが参照されていることがあるので注意 C:\Users\[ユーザ名]\.homestead\Homestead.yaml ■nginxの設定 $ sudo su - # cd /etc/nginx/sites-available # cp xxx.homestead.test blog.homestead.test ... 既存の設定を流用 # vi blog.homestead.test
server_name blog.homestead.test; root "/home/vagrant/code/blog/public"; error_log /var/log/nginx/blog.homestead.test-error.log error; ssl_certificate /etc/nginx/ssl/blog.homestead.test.crt; ssl_certificate_key /etc/nginx/ssl/blog.homestead.test.key;
# service nginx restart ■MySQLの設定 $ mysql -u root -p secret GRANT ALL PRIVILEGES ON blog.* TO homestead@localhost IDENTIFIED BY 'secret'; $ mysql -u homestead -p secret CREATE DATABASE blog CHARACTER SET utf8mb4; ■Windows側の設定 C:\localhost\home\homestead\code\blog\public\index.php
<?php phpinfo() ?>
C:/windows/System32/drivers/etc/hosts
192.168.10.10 blog.homestead.test
■アクセス 以下でアクセスできる http://blog.homestead.test ■Laravelインストール いったん /home/vagrant/code/blog を削除してから作業開始 $ cd /home/vagrant/code $ composer create-project --prefer-dist laravel/laravel blog 以下でアクセスできる http://blog.homestead.test 引き続き以下を参考に、大抵のプロジェクトで必要になりそうな部分を作業 https://github.com/refirio/laravel-blog/commits/master $ cd blog $ composer require laravel-ja/comja5 $ vendor/bin/comja5 $ vendor/bin/comja5 -c $ vendor/bin/comja5 -f タイムゾーンとロケールを変更
'timezone' => 'Asia/Tokyo', 'locale' => 'ja',
config\database.php を編集
'charset' => 'utf8mb4', 'collation' => 'utf8mb4_general_ci',
/.env を編集(基本的に DB_DATABASE の設定のみでいい)
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=blog DB_USERNAME=homestead DB_PASSWORD=secret
Userモデルを移動(移動先は App\DataAccess\Eloquent では無く App\Entities なので注意) app\Entities\User.php
namespace App\Entities;
不要なマイグレーションを削除 database\migrations\2014_10_12_100000_create_password_resets_table.php デフォルトのマイグレーションを調整 database\migrations\2014_10_12_000000_create_users_table.php
public function up() { Schema::create('users', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('name', 85)->comment('名前'); $table->string('email', 85)->unique()->comment('メールアドレス'); $table->string('password', 60)->comment('パスワード'); $table->rememberToken(); $table->timestamps(); }); DB::statement('ALTER TABLE users COMMENT \'ユーザ\''); }
作業ディレクトリへ移動 cd /home/vagrant/code/blog マイグレーションを実行 php artisan migrate
■Homesteadに同一LAN内からアクセス
Vagrantfile に config.vm.network の設定を追加する
if defined? VagrantPlugins::HostsUpdater config.hostsupdater.aliases = settings['sites'].map { |site| site['map'] } end # ローカルからのアクセスを許可 config.vm.network "forwarded_port", guest: 80, host: 80 end
これで、自身のPCから 127.0.0.1 でHomesteadにアクセスできる この状態なら、同一LAN内からIPアドレスを指定すればアクセスできる (PCのIPアドレスはipconfigで調べることができる) XAMPPを併用している場合、以下のようにすると PCのIPアドレスにアクセスするとXAMPPが表示され、「:8080」を付けてアクセスするとHomesteadが表示され…とできる
config.vm.network "forwarded_port", guest: 80, host: 8080
バーチャルホストを複数設定している場合、IPアドレスでアクセスすると最初のバーチャルホストに対してアクセスされる この場合、いずれかのバーチャルホストに対して「default_server」を指定すると、それがIPアドレスでアクセスしたときのホストとなる
server { listen 80 default_server; listen 443 ssl http2 default_server; server_name test.homestead.test;
■Homestead.yaml での指定 以下のように Homestead.yaml で指定することもできるらしい(未検証) Laravel Homestead 5.5 Laravel https://readouble.com/laravel/5.5/ja/homestead.html#network-interfaces
■Homesteadからメール送信
Homestead 環境で、GmailのSMTPでメールを送信したときのメモ [Laravel5]gmailが送信できない? : ラブサファリ http://lovesafari.blog.jp/archives/21272100.html Laravelでメール送信機能を実装する方法【Gmailを利用】 https://manablog.org/laravel-send-email/ ■.envの設定 .env で以下のように設定する
MAIL_DRIVER=smtp MAIL_HOST=smtp.gmail.com MAIL_PORT=465 MAIL_USERNAME=Gmailアドレス MAIL_PASSWORD=アプリパスワード MAIL_ENCRYPTION=ssl
■ファイルの作成 /resources/views/emails/test.blade.php でメールの本文を定義する
<p>$Test={{ $test }}</p>
/app/Http/Controllers/Admin/HomeController.php など適当なコントローラーに送信処理を書く。ファイル冒頭でMailクラスをuseし、コントローラー内に実際の送信処理も書く
use Illuminate\Support\Facades\Mail; $data = [ 'test' => 'テスト' ]; Mail::send('emails.test', $data, function($message){ $message->to('refirio.work@gmail.com') ->subject('ここがタイトルです'); }); exit('OK');
■送信確認 認証エラーが出る場合、Googleで2段階認証とアプリパスワードを設定しているか確認する (通常のログインパスワードとは別にアプリパスワードが必要。アプリパスワードの設定には2段階認証の設定が必要) これでメールが送信されたが、メールのFromが意図したアドレスにならない メールヘッダを確認すると、以下のように X-Google-Original-From に反映されていた
Sender: "山野寛和" <refirio.work@gmail.com> From: refirio@example.com X-Google-Original-From: example@example.com
これはGmailを使う場合の仕様かも php - Laravel Sender or From not working as expected - Stack Overflow https://stackoverflow.com/questions/39362131/laravel-sender-or-from-not-working-as-expected
■gitの設定
Windowsのローカル開発環境で、SourceTreeでComposer用のファイルを除外する方法 ツール → オプション → Git → グローバル無視リスト → ファイルを編集 #Ignore composer file composer.bat composer.phar
■アプリケーションの作成例
■テーブル追加 マイグレーションを作成 php artisan make:migration create_articles_table --create=articles マイグレーションファイルに以下を追加
$table->string('subject'); $table->text('detail');
マイグレーションを実行 php artisan migrate マイグレーションを巻き戻す場合(最後に「一度に」実行したマイグレーションをまとめて巻き戻す) php artisan migrate:rollback 以下のテーブルが作成される
CREATE TABLE `articles` ( `id` int(10) UNSIGNED NOT NULL, `subject` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, `detail` text COLLATE utf8mb4_unicode_ci NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
■モデル作成 php artisan make:model Article ■初期値登録(登録する場合) シーダーを使用する。1件登録するだけなら /test/database/seeds/DatabaseSeeder.php にデータ登録コードを追加すればいい ただし以下のように、Seeder用のファイルを作成するほうがいい php artisan make:seeder ArticlesTableSeeder ArticlesTableSeeder.phpに以下を追加
$article = new App\Article; $article->subject = 'subject:' . str_random(10); $article->detail = 'detail:' . str_random(50); $article->save();
もしくは、直接insertしてもいい
use Carbon\Carbon; $now = Carbon::now(); DB::table('articles')->insert([ 'subject' => 'subject:' . str_random(10), 'detail' => 'detail:' . str_random(50), 'created_at' => $now, 'updated_at' => $now, ]);
DatabaseSeeder.phpに以下を追加して、ArticlesTableSeederを呼び出す
$this->call(ArticlesTableSeeder::class);
php artisan db:seed 以下のエラーになる。作成したクラスを参照できていない [ReflectionException] Class ArticlesTableSeeder does not exist require_once で該当クラスを読み込めば参照できるが、Laravelではオートロードの仕組みを使用する /test/vendor/composer/ 内にオートロードの設定が記載されている 以下のコマンドを実行すると設定が更新され、クラスを参照できるようになる (存在するはずのクラスを参照できない場合、この方法を試すといい) composer dump-autoload また、以下のようにすると特定のシーダーのみ実行できる php artisan db:seed --class=ArticlesTableSeeder Laravelで名前空間を指定してオートロードされなかったら見る場所。 - Qiita http://qiita.com/niiyz/items/5b83ef5255a1ec64d9d6 composer dump-autoloadが面倒くさい Laravel - Qiita http://qiita.com/ytake/items/98c438d6a006f61df54a ■テーブルの参照例 すべて取得
$articles = DB::table('articles')->get(); $articles = App\Article::all();
ビューでの表示
<ul> @foreach ($articles as $article) <li>{{ $article->subject }}</li> @endforeach </ul>
追加
$article = new App\Article; $article->subject = 'Subject'; $article->detail = 'Detail'; $article->save();
更新
$article = App\Article::find(2); $article->subject = 'Subject2'; $article->detail = 'Detail2'; $article->save();
削除
$article = App\Article::find(2); $article->delete();
※追加・更新・削除は、fillで複数代入する書き方を推奨(後述) ■モデルの位置を変更する場合 ファイルを移動 \test\app\Article.php ↓ \test\app\Models\Article.php モデルの呼び出しを変更 $articles = App\Article::all(); ↓ $articles = App\Models\Article::all(); モデル内の宣言を変更 namespace App; ↓ namespace App\Models; ■CRUD \test\routes\web.php
use Illuminate\Http\Request; /** * Show Article */ Route::get('/article', function () { return view('article', [ 'articles' => App\Article::orderBy('created_at', 'asc')->get() ]); }); /** * Add New Article */ Route::post('/article', function (Request $request) { $validator = Validator::make($request->all(), [ 'subject' => 'required|max:255', 'detail' => 'required|max:255', ]); if ($validator->fails()) { return redirect('/article') ->withInput() ->withErrors($validator); } $article = new App\Article; $article->subject = $request->subject; $article->detail = $request->detail; $article->save(); return redirect('/article'); }); /** * Delete Article */ Route::delete('/article/{id}', function ($id) { App\Article::findOrFail($id)->delete(); return redirect('/article'); });
\test\resources\views\article.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="col-sm-offset-2 col-sm-8"> <div class="panel panel-default"> <div class="panel-heading"> New Article </div> <div class="panel-body"> <!-- Display Validation Errors --> @if (count($errors) > 0) <!-- Form Error List --> <div class="alert alert-danger"> <strong>Whoops! Something went wrong!</strong> <br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <!-- New Article Form --> <form action="{{ url('article')}}" method="POST" class="form-horizontal"> {{ csrf_field() }} <!-- Article Subject --> <div class="form-group"> <label for="article-subject" class="col-sm-3 control-label">Subject</label> <div class="col-sm-6"> <input type="text" name="subject" id="article-subject" class="form-control" value="{{ old('subject') }}"> </div> </div> <!-- Article Detail --> <div class="form-group"> <label for="article-detail" class="col-sm-3 control-label">Detail</label> <div class="col-sm-6"> <input type="text" name="detail" id="article-detail" class="form-control" value="{{ old('detail') }}"> </div> </div> <!-- Add Article Button --> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" class="btn btn-default"> <i class="fa fa-btn fa-plus"></i>Add Article </button> </div> </div> </form> </div> </div> <!-- Current Articles --> @if (count($articles) > 0) <div class="panel panel-default"> <div class="panel-heading"> Current Articles </div> <div class="panel-body"> <table class="table table-striped article-table"> <thead> <th>Article</th> <th>&nbsp;</th> </thead> <tbody> @foreach ($articles as $article) <tr> <td class="table-text"><div>{{ $article->subject }}: {{ $article->detail }}</div></td> <!-- Article Delete Button --> <td> <form action="{{ url('article/'.$article->id) }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit" class="btn btn-danger"> <i class="fa fa-btn fa-trash"></i>Delete </button> </form> </td> </tr> @endforeach </tbody> </table> </div> </div> @endif </div> </div> @endsection
■CRUD:コントローラーを使用する場合
Route::get('/article', 'ArticleController@index'); Route::post('/article', 'ArticleController@store'); Route::delete('/article/{article}', 'ArticleController@destroy');
\test\app\Http\Controllers\ArticleController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Article; class ArticleController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); } /** * Display a list of all of the user's article. * * @param Request $request * @return Response */ public function index(Request $request) { return view('article', [ 'articles' => Article::orderBy('created_at', 'asc')->get() ]); } /** * Create a new article. * * @param Request $request * @return Response */ public function store(Request $request) { $validator = $this->validate($request, [ 'subject' => 'required|max:255', 'detail' => 'required|max:255', ]); $article = new Article; $article->subject = $request->subject; $article->detail = $request->detail; $article->save(); return redirect('/article'); } /** * Destroy the given article. * * @param Request $request * @param string $id * @return Response */ public function destroy(Request $request, $id) { Article::findOrFail($id)->delete(); return redirect('/article'); } }
■基本的なバリデーションについて $this->validate() を呼ぶことにより、エラーがあれば $errors にエラーメッセージが格納される Validator::make() と $this->validate() の違いについても、以下のページで触れられている 実際のアプリケーションでは、フォームリクエストの仕組みでバリデーションする方が良さそう バリデーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/validation.html Laravelバリデーション指南書:フォームリクエストを使おう - Qiita http://qiita.com/sakuraya/items/abca057a424fa9b5a187 Laravel5.0:FormRequestを使ったValidation - メドピア開発者ブログ http://tech.medpeer.co.jp/entry/2015/06/16/171115 Laravel 5.1 入門記 その14(Form Request とメッセージのカスタマイズ編) - 日記 http://tnamao.hatenablog.com/entry/2015/10/04/175345
■認証
artisanから認証の仕組みを組み込める ルーティングの設定などが書き換わるので、導入するなら初めに導入しておく方が良さそう cd test php artisan make:auth php artisan migrate 以下のエラーが表示される場合、 データベースの文字コードがutf8mb4の場合にVARCHARの最大文字数が191文字に制限されるため 詳細は「トラブル対応」を参照 [Illuminate\Database\QueryException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was t oo long; max key length is 767 bytes (SQL: alter table `users` add unique ` users_email_unique`(`email`)) [PDOException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was t oo long; max key length is 767 bytes 完了すると、「LOGIN」と「REGISTER」が追加される ユーザ登録、ログイン、パスワード再発行が実装される 認証処理のルーティング \test\routes\web.php に Auth::routes(); が追加され、認証用のルーティングが有効になる。ここから \test\vendor\laravel\framework\src\Illuminate\Support\Facades\Auth.php の routes() が呼ばれる。ここから \test\vendor\laravel\framework\src\Illuminate\Routing\Router.php の auth() が呼ばれる。ここから \test\app\Http\Controllers\Auth 内のファイルが呼ばれる…となる。多くの処理はtraitになっており \test\vendor\laravel\framework\src\Illuminate\Foundation\Auth 内のファイルが呼ばれる 認証処理の流れ(調査中) \test\vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers の login() がログイン時に呼ばれる処理 同トレイとの attemptLogin() で認証を行う。内部では guard() が呼ばれており、 Auth::guard() の attempt() が実行される 認証の判定(調査中) \test\app\Http\Kernel.php の $routeMiddleware で 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, と定義されており、コントローラなどで $this->middleware('auth'); を呼ぶことで、認証が必要になる Authenticate の内部では handle() から authenticate() が呼ばれ、$this->auth->guard($guard)->check() 部分で認証が確認される この命令は Auth クラスに実装されており、実際の処理は Illuminate\Auth\AuthManager に guard() がある そこから Illuminate\Auth の check() が呼ばれ、実際の処理は Illuminate\Auth\GuardHelpers に check() がある 内部では $this->user() に値があるか否かを調べている Laravelの標準Authentication(Auth)の動きを調べてみる http://qiita.com/zaburo/items/9fcf0f4c771e011a4d35 Laravel : デフォルト認証機能の動作確認 http://www.dn-web64.com/archives/web/laravel_auth_default/ Laravel ユーザなら知っておくべきAuthオートログインのこと - Shin x blog http://www.1x1.jp/blog/2014/04/lararavel-artisan-should-know-auto-login-by-auth.html
■マルチ認証(管理者とユーザで別々に認証)
Laravelでマルチ認証(マルチログイン)を実装する https://leben.mobi/blog/laravel5_multi_login/php/ 基本的には上記の解説を参考に作成 ただし解説どおりだと、認証エラーのメッセージが表示されなかったので以下の内容は変更している /app/Http/Controllers/Auth/AdminLoginController.php users テーブルに加えて admins テーブルを作成し、管理者情報を格納するものとする モデルは /app/Entities 内に置いているものとする ■Admin用のマイグレーションとモデルを作成 php artisan make:model Admin -m マイグレーションの内容をusersと合わせる
Schema::create('admins', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('name', 85)->comment('名前'); $table->string('email', 85)->unique()->comment('メールアドレス'); $table->string('password', 60)->comment('パスワード'); $table->rememberToken(); $table->timestamps(); }); DB::statement('ALTER TABLE admins COMMENT \'管理者\'');
モデルの内容をusersと合わせる
<?php namespace App\Entities; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class Admin extends Authenticatable { use Notifiable; /** * 複数代入を行う属性 * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * 配列には含めない属性 * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; }
■マイグレーションを実行 php artisan migrate ■認証設定ファイルにAdmin用の設定を追加 /config/auth.php
<?php return [ 'defaults' => [ 'guard' => 'user', // web から user に変更 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', ], // ここから追加 'user' => [ 'driver' => 'session', 'provider' => 'users', ], 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'admin-api' => [ 'driver' => 'token', 'provider' => 'admins', ], // ここまで追加 ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Entities\User::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], // ここから追加 'admins' => [ 'driver' => 'eloquent', 'model' => App\Entities\Admin::class, ], // ここまで追加 ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, ], // ここから追加 'admins' => [ 'provider' => 'admins', 'table' => 'password_resets', 'expire' => 15, ], // ここまで追加 ], ];
■認証失敗処理を追加 /app/Exceptions/Handler.php
use Illuminate\Auth\AuthenticationException; 〜中略〜 /** * 認証エラーがあればリダイレクト * * @param \Illuminate\Http\Request $request * @param \Illuminate\Auth\AuthenticationException $exception * @return \Illuminate\Http\Response */ protected function unauthenticated($request, AuthenticationException $exception) { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); } $guard = array_get($exception->guards(), 0); switch ($guard) { case 'admin': $login = 'admin.login'; break; default: $login = 'login'; break; } return redirect()->guest(route($login)); }
■認証成功処理を追加 /app/Http/Middleware/RedirectIfAuthenticated.php
public function handle($request, Closure $next, $guard = null) { switch ($guard) { case 'admin': if (Auth::guard($guard)->check()) { return redirect()->route('admin.dashboard'); } break; default: if (Auth::guard($guard)->check()) { return redirect('/home'); } break; } return $next($request); }
■Admin用のコントローラを作成 /app/Http/Controllers/AdminController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class AdminController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth:admin'); } /** * Show the application dashboard. * * @return \Illuminate\Http\Response */ public function index() { return view('admin'); } }
/app/Http/Controllers/Auth/AdminLoginController.php を作成
<?php namespace App\Http\Controllers\Auth; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Auth; use Illuminate\Validation\ValidationException; class AdminLoginController extends Controller { public function __construct() { $this->middleware('guest:admin'); } public function showLoginForm() { return view('auth.admin-login'); } public function login(Request $request) { // Validate the form data $this->validate($request, [ 'email' => 'required|email', 'password' => 'required|min:6' ]); // Attempt to log the user in if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->remember)) { // if successful, then redirect to their intended location return redirect()->intended(route('admin.dashboard')); } return $this->sendFailedLoginResponse($request); } /** * Get the failed login response instance. * * @param \Illuminate\Http\Request $request * @return \Symfony\Component\HttpFoundation\Response * * @throws ValidationException */ protected function sendFailedLoginResponse(Request $request) { throw ValidationException::withMessages([ 'email' => [trans('auth.failed')], ]); } }
■Admin用のルーティングを追加 /routes/web.php
Route::prefix('admin')->group(function() { Route::get('/login', 'Auth\AdminLoginController@showLoginForm')->name('admin.login'); Route::post('/login', 'Auth\AdminLoginController@login')->name('admin.login.submit'); Route::get('/', 'AdminController@index')->name('admin.dashboard'); });
■Admin用のビューを作成 resources/views/home.blade.php を複製して resources/views/admin.blade.php を作成 resources/views/auth/login.blade.php を複製して resources/views/auth/admin-login.blade.php を作成 admin-login.blade.php
<div class="panel-heading">ログイン</div> <div class="panel-body"> <form class="form-horizontal" method="POST" action="{{ route('login') }}"> 以下のように変更 <div class="panel-heading">Adminログイン</div> <div class="panel-body"> <form class="form-horizontal" method="POST" action="{{ route('admin.login.submit') }}">
■動作確認 ユーザ情報の登録は作成していないので、usersテーブルの内容をもとに手動でadminsテーブルにユーザを登録する http://blog.homestead.test/ http://blog.homestead.test/admin/ 以下は要検証 ・AdminLoginController.php の内容を再度考えたい ・web.php の「->name('admin.login.submit');」は、ユーザ側には無いので省略するか ・マイグレーションのファイル名は、最初の構築以降は日付を含めるか
■API認証(API利用時にPassportでOAuth2認証)
API認証(Passport) 5.5 Laravel https://readouble.com/laravel/5.5/ja/passport.html Laravel5.5でAPI認証のパッケージ(Laravel Passport)を利用する - Qiita https://qiita.com/niiyz/items/fffff94acb6061ecc9d4 Laravel5.6 PassportでAPI認証を実装 - Crunchtimer - Medium https://medium.com/crunchtimer/laravel5-6-passport%E3%81%A7api%E8%AA%8D%E8%A8%BC%E3%82%92%E5%AE%9F%E... Laravel PassportでWeb APIの認証を実装する【初期設定編】 | 大阪のシステム開発なら 株式会社ウィズテクノロジー https://www.whizz-tech.co.jp/1442/ Laravel PassportでWeb APIの認証を実装する【実装編】 | 大阪のシステム開発なら 株式会社ウィズテクノロジー https://www.whizz-tech.co.jp/1453/ ■インストール $ composer require laravel/passport - Installation request for laravel/framework (locked at v5.5.44, required as 5.5.*) -> satisfiable by laravel/framework[v5.5.44]. - Installation request for laravel/passport ^7.0 -> satisfiable by laravel/passport[v7.0.0, v7.0.1, v7.0.2, v7.0.3]. インストールできない バージョンを指定してインストール $ composer require laravel/passport=~4.0 - Installation request for paragonie/random_compat (locked at v9.99.99) -> satisfiable by paragonie/random_compat[v9.99.99]. インストールできない Your requirements could not be resolved to an installable set of packages - Issue #774 - laravel/passport https://github.com/laravel/passport/issues/774 composer - Laravel5.5のクライアント認証のためのパッケージがインストールできない|teratail https://teratail.com/questions/115287 を参考にrandom_compatとpassportのバージョンを指定してインストール $ composer require paragonie/random_compat:2.* $ composer require laravel/passport=~4.0 インストールできた (passportのバージョン指定が無いと、相変わらずエラーになった) データベースにテーブルを作成(マイグレーションは /vendor/laravel/passport/database/migrations 内のファイルが実行されるみたい) $ php artisan migrate Migrating: 2016_06_01_000001_create_oauth_auth_codes_table Migrated: 2016_06_01_000001_create_oauth_auth_codes_table Migrating: 2016_06_01_000002_create_oauth_access_tokens_table Migrated: 2016_06_01_000002_create_oauth_access_tokens_table Migrating: 2016_06_01_000003_create_oauth_refresh_tokens_table Migrated: 2016_06_01_000003_create_oauth_refresh_tokens_table Migrating: 2016_06_01_000004_create_oauth_clients_table Migrated: 2016_06_01_000004_create_oauth_clients_table Migrating: 2016_06_01_000005_create_oauth_personal_access_clients_table Migrated: 2016_06_01_000005_create_oauth_personal_access_clients_table データベース管理ツールなどで、テーブルが作成されたことを確認しておく ■アクセストークンを作成 この作業により暗号キーが生成され、 oauth_clients テーブルに「パーソナルアクセス」クライアントと「パスワードグラント」クライアントも作成される $ php artisan passport:install Encryption keys generated successfully. Personal access client created successfully. Client ID: 1 Client Secret: Dww67qb3VFgBf5eXasC5weRSR1nBN9wAZ5xbhGLB Password grant client created successfully. Client ID: 2 Client Secret: BGp9oHDnNRD3Q45rd8Zd4VBKJbCYuhOChjiolQld ■認証ユーザを作成 この作業により、oauth_clients テーブルに対象ユーザ用の認証情報が登録される $ php artisan passport:client Which user ID should the client be assigned to?: > 1 What should we name the client?: > OAuth Test Where should we redirect the request after authorization? [http://localhost/auth/callback]: > http://localhost/~test/oauth/callback.php New client created successfully. Client ID: 3 Client secret: syHivsywmRTVTf0Cbh72u14ICDdntiDBzFHiLChc ■プログラムの修正 UserモデルにHasApiTokensトレイトを追加
<?php namespace App; use Laravel\Passport\HasApiTokens; // 追加 use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasApiTokens, Notifiable; // HasApiTokensを追加 }
AuthServiceProviderに Passport::routes を追加
<?php namespace App\Providers; use Laravel\Passport\Passport; // 追加 use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * アプリケーションのポリシーのマップ * * @var array */ protected $policies = [ 'App\Model' => 'App\Policies\ModelPolicy', ]; /** * 全認証/認可サービスの登録 * * @return void */ public function boot() { $this->registerPolicies(); Passport::routes(); // 追加 } }
config/auth.php の設定を変更
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', // tokenをpassportに変更 'provider' => 'users', ],
routes/api.php にテスト用APIを追加
Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); // テスト用API Route::middleware('auth:api')->get('/test', function (Request $request) { return json_encode(array_merge(['status' => 'OK', 'message' => 'test!'])); });
■認証クライアントを作成 「認証ユーザを作成」で作成したユーザで認証するプログラムを作成してみる 認証を開始するプログラム。ブラウザから直接アクセスする想定 http://localhost/~test/oauth/start.php
<?php $queries = array( 'client_id=3', 'redirect_uri=http://localhost/~test/oauth/callback.php', 'response_type=code', 'scope=', ); header('Location: http://blog.homestead.test/oauth/authorize?' . implode('&', $queries)); exit;
認証すると、以下にリダイレクトされる(実際はURLの後ろに「?code=XXXXX」のような値が付く) http://localhost/~test/oauth/callback.php
<?php session_start(); if (empty($_GET['code'])) { exit('Code is empty.'); } $result = file_get_contents( 'http://blog.homestead.test/oauth/token', false, stream_context_create( array( 'http' => array( 'method' => 'POST', 'header' => implode( "\r\n", array( 'Content-Type: application/x-www-form-urlencoded' ) ), 'content' => http_build_query( array( 'grant_type' => 'authorization_code', 'client_id' => '3', 'client_secret' => 'syHivsywmRTVTf0Cbh72u14ICDdntiDBzFHiLChc', 'redirect_uri' => 'http://localhost/~test/oauth/callback.php', 'code' => $_GET['code'], ) ) ) ) ) ); $json = json_decode($result, true); $_SESSION['access_token'] = $json['access_token']; header('Location: ./request.php'); exit;
アクセストークンを取得後、以下にリダイレクトされる http://localhost/~test/oauth/callback.php
<?php session_start(); if (empty($_SESSION['access_token'])) { exit('Access Token is empty.'); } $result = file_get_contents( 'http://blog.homestead.test/api/test', // テスト用API //'http://blog.homestead.test/api/user', // ユーザ情報取得API false, stream_context_create( array( 'http' => array( 'method' => 'GET', 'header' => "Accept: application/json\r\n" . "Authorization: Bearer " . $_SESSION['access_token'] . "\r\n" ) ) ) ); echo '<pre>' . $result . '</pre>'; exit;
以下のようにJSONが表示されれば成功(/api/user を呼び出した場合は、JSONでユーザ情報が表示される)
{"status":"OK","message":"test!"}
アクセストークンは oauth_refresh_tokens テーブルに access_token_id として記録される アクセストークンでAPIを呼び出すときは以下の箇所で認証情報を送信している
Authorization: Bearer " . $_SESSION['access_token'] . "
以下のようにBase64デコードを行うと、送信しているデータの内容を確認できる
echo base64_decode($_SESSION['access_token']); exit;
以下のような情報を確認できるが、このうち「2c7896d9f0809d4b77a531bd7562c9ce9146c9d9ed10e1c70fb88aaf9bd69f05c38ec60739e29d57」が access_token_id と一致する
{"typ":"JWT","alg":"RS256","jti":"2c7896d9f0809d4b77a531bd7562c9ce9146c9d9ed10e1c70fb88aaf9bd69f05c38ec60739e29d57"}以下略
Laravel における Bearer 認証 - chatbox blog https://www.chatbox.blog/2018/05/29/laravel-%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B-bearer-%E8%AA%8D%E8... Authorization Bearer ヘッダを用いた認証 API の実装 - げっとシステムログ https://www.getto.systems/entry/2017/10/19/004734 ■認証クライアントを作成 取得したアクセストークンをファイル内に直接書いておけば、任意のタイミングでAPIを呼び出せるようになる
<?php define('ACCESS_TOKEN', 'アクセストークンの内容'); $result = file_get_contents( 'http://terasapo.homestead.test/api/test', false, stream_context_create( array( 'http' => array( 'method' => 'GET', 'header' => "Accept: application/json\r\n" . "Authorization: Bearer " . ACCESS_TOKEN . "\r\n" ) ) ) ); echo '<pre>' . $result . '</pre>'; exit;
■トークン持続時間 「デフォルトでPassportは、再生成する必要のない、長期間持続するアクセストークンを発行します。」とある 短くしたい場合はコードで指定する API認証(Passport) 5.5 Laravel https://readouble.com/laravel/5.5/ja/passport.html#configuration ■以下デバッグ時のメモ app/Http/Kernel.php に以下のコードは手動で追加した?不要かも?
'client' => CheckClientCredentials::class,
Laravel 5.5 API User Authentication with Passport Package https://www.itechempires.com/2017/09/laravel-5-5-api-user-authentication-passport-package/ Laravel 5.4でWeb APIを作る - アシアルブログ http://blog.asial.co.jp/1498 API Authentication Error: {"error":"invalid_client","message":"Client authentication failed"} - Issue #221 - laravel/passport https://github.com/laravel/passport/issues/221 PHP - Laravelで認証画面が表示されない|teratail https://teratail.com/questions/98712 Laravel5.5でAPI認証のパッケージ(Laravel Passport)を利用する - Qiita https://qiita.com/niiyz/items/fffff94acb6061ecc9d4
■強制ログイン
Laravel Recipes日本語版 | ユーザーのIDを利用してログインする http://recipes.laravel.jp/recipe/77 authentication - Laravel - Auth multi guard loginUsingId is not working as expexted - Stack Overflow https://stackoverflow.com/questions/52154633/laravel-auth-multi-guard-loginusingid-is-not-working-as... /config/auth.php で以下のように設定されているものとする (標準のユーザとして user があり、追加のユーザとして developer があるものとする)
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], 'user' => [ 'driver' => 'session', 'provider' => 'users', ], 'developer' => [ 'driver' => 'session', 'provider' => 'developers', ], 'developer-api' => [ 'driver' => 'token', 'provider' => 'developers', ], ],
■userで強制ログイン users テーブルに taro@example.com ユーザがいる場合、以下のコードで強制的に認証してログイン済みにできる Auth::attempt() でユーザ名とパスワードが正しいか確認できる $id には users テーブルの代理キーが入り、その代理キーを Auth::loginUsingId() に渡すとログイン状態になる
/* * 強制ログインのテスト */ Route::get('/enter', function () { $id = Auth::attempt(['email' => 'taro@example.com', 'password' => 'abcd1234']); if ($id) { Auth::loginUsingId($id); return 'OK'; } else { return 'NG'; } });
■developerで強制ログイン developers テーブルに developer@example.com ユーザがいる場合、以下のコードで強制的に認証してログイン済みにできる $id には developers テーブルの代理キーが入る
/* * 強制ログインのテスト */ Route::get('/enter', function () { $id = Auth::guard('developer')->attempt(['email' => 'developer@example.com', 'password' => 'abcd1234']); if ($id) { Auth::guard('developer')->loginUsingId($id); return 'OK'; } else { return 'NG'; } });
■更新(他の開発者の更新を取り込む手順)
gitからプルした後、SSHから以下を実行する (作業用ディレクトリに移動し、Composer・マイグレーション・シーダー・キャッシュクリアを実行) $ cd /home/vagrant/code/test $ composer install $ composer dump-autoload ... 後から追加した $ php artisan migrate $ php artisan db:seed $ php artisan config:clear $ php artisan view:clear
■設計
■選択項目の管理 項目自体は設定ファイルで管理する config\option.php
'administrator' => [ // 利用状況 'status' => [ 'operation' => '運用中', 'stop' => '運用一時停止中', ], ],
ビューに表示する際はモデルを使用する app\DataAccess\Eloquent\Administrator.php
public function getStatusLabelAttribute() { return config('option.administrator.status')[$this->attributes['status']]; }
■ミドルウェア リクエストのフィルタリングとレスポンスの変更を行う Laravel5.1以前ではコントローラでbeforeフィルターを設定するような仕様だったらしい 現在は非推奨となり、代わりにミドルウェアが推奨されている ミドルウェア 5.4 Laravel https://readouble.com/laravel/5.4/ja/middleware.html Laravelでアクション実行前・後などで共通の処理をさせたいあなたに!コントローラーフィルターまとめ - Qiita http://qiita.com/kenguy/items/321faa1bd2570a39ea35 php artisan make:middleware CheckCount \test\app\Http\Middleware\CheckCount.php セッションの値によって、ページの表示を制御するものとする
public function handle($request, Closure $next) { $count = session('count', 0); if ($count < 10) { return redirect('/home'); } return $next($request); }
\test\app\Http\Kernel.php ミドルウェアを登録する
protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 〜略〜 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'count' => \App\Http\Middleware\CheckCount::class, // ←追加 ];
\test\routes\web.php ルーティングでミドルウェアを呼び出す
// ルートプレフィックス Route::prefix('/admin')->group(function () { Route::get('/session', function () { $count = session('count', 0); return 'Admin count=' . $count; })->middleware('count'); });
Kernel.php の $routeMiddleware に登録すると、ルーティングから必要に応じてミドルウェアを呼び出せる Kernel.php の $middleware に登録すると、すべてのルーティングに対して自動的に実行される ■サービスコンテナ Laravelでは以下のようなコードが頻繁に登場する これだけでEntryServiceクラスのオブジェクトが $entry に入る。newを書かなくてもいい
public function __construct(EntryService $entry, CommentService $comment) { $this->entry = $entry; $this->comment = $comment; }
大まかな仕組みとしては、リフレクションを使用して自動でクラスを判断し、展開したものを自動で渡すようになっているみたい 以下では、個別にサービスコンテナを設定する方法などをまとめる サービスコンテナ - ララ帳 https://laravel10.wordpress.com/tag/%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%B3%E3%83%B3%E3%83%86%... サービスコンテナ https://readouble.com/laravel/5.4/ja/container.html LaravelのDIコンテナはどう使われているのか - 新人Webエンジニアの記録。 http://blog.fagai.net/2016/09/17/laravel-dependency-injection/ \test\routes\web.php
interface SenderInterface { public function send($message); } class MailSender implements SenderInterface { public function send($message) { return "メールで $message を送りました。"; } } class BikeSender implements SenderInterface { public function send($message) { return "バイク便で $message を届けました。"; } } class Messenger { protected $sender; public function __construct(SenderInterface $sender) { $this->sender = $sender; } public function send($message) { return $this->sender->send($message); } } //App::bind('sender', 'MailSender'); // DIコンテナにバインド //App::singleton('sender', 'MailSender'); // シングルトンとしてDIコンテナにバインド //$instance = new MailSender(); //App::instance('sender', $instance); // インスタンスをバインド App::bind('SenderInterface', 'MailSender'); Route::get('send/{message?}', function(Messenger $sender, $message = '合格通知') { //$sender = new BikeSender(); // 通常のクラス作成 //$sender = App::make('sender'); // DIコンテナから取得 //$sender = App::make('MailSender'); // DIコンテナから取得 //$sender = App::make('Messenger'); return $sender->send($message); });
■DIとサービスコンテナの使い分け Dependency Injection と Service Container (Service Locator) の使い分け https://qiita.com/nunulk/items/2c637d3952096ef74677#2-dependency-injection-%E3%81%A8-service-contain... 2.3. 使い分け コンストラクタインジェクションやメソッドインジェクションを使えば、 依存オブジェクトを引数として表現できるので、できるだけこちらを使った方がいいと思います。 サービスコンテナは便利な反面、どこからでもアクセスできてしまうので、 気をつけないと依存オブジェクトがどんどん増えてしまう恐れがあります。 サービスコンテナを使う場合でも、依存オブジェクトが多くなってきたな、と思ったら、 集約 (Aggregate) パターンを新たに作るなどして、依存関係を減らすように努めると、設計も洗練されて行くんじゃないかと思います。 ■サービスプロバイダ 要勉強 サービスコンテナへのバインド処理で利用する機能をサービスプロバイダと呼ぶ コントローラなどでは処理をクラス名で呼び出さずにコントラクト(インターフェース)をもとに呼び出し、サービスプロバイダ内で実際のクラスメイトコントラクトを紐付ける これにより、サービスプロバイダ内の処理を変更するだけで処理の差し替えができる サービスの初期処理(命令の初期起動処理)を行う サービスプロバイダーとは - ララ帳 https://laravel10.wordpress.com/2015/04/22/%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%83%97%E3%83%AD%E3... サービスプロバイダー 5.4 Laravel https://readouble.com/laravel/5.4/ja/providers.html Laravel5でカスタムライブラリ使いたい - Qiita http://qiita.com/zaburo/items/e8e786624f88c253c87d ■イベント 要勉強 Laravelが様々な処理実行時にイベントを発行するため、 必要に応じてフックして処理を差し込むことができる 例えば illuminate.query はデータベースにクエリを発行したあとに発生するイベント イベント 5.4 Laravel https://readouble.com/laravel/5.4/ja/events.html ■ブロードキャスト 要勉強 websocketを利用して、ブラウザにイベントを通知するなど、リアルタイム性のあるアプリケーションを作れる サーバサイドだけで完結せず、JavaScriptも関連する ブロードキャスト 5.4 Laravel https://readouble.com/laravel/5.4/ja/broadcasting.html ■フォームリクエストによるバリデーション フォームリクエストを作成 php artisan make:request StoreArticlePost \test\app\Http\Requests\StoreArticlePost.php
public function authorize() { return true; } public function rules() { return [ 'subject' => 'required|max:255', 'detail' => 'required|max:255', ]; }
\test\app\Http\Controllers\ArticleController.php
use App\Http\Requests\StoreArticlePost; public function store(StoreArticlePost $request) { /* リクエストが正しければここが実行される */ }
バリデーションと専用の項目名は、フォームリクエストで定義する \test\app\Http\Requests\StoreArticlePost.php 汎用的に使われる項目名は、以下のファイル内にある「attributes」で設定する モデルごとの特別な項目名は、各フォームリクエストで設定や上書きをする \test\resources\lang\ja\validation.php
■ルーティング
■ルーティングの設定と確認 ルーティング 5.4 Laravel https://readouble.com/laravel/5.4/ja/routing.html コントローラ 5.4 Laravel https://readouble.com/laravel/5.4/ja/controllers.html Laravel 5.4から可能になった新しいRouteの書き方 | cupOF Interests http://co.bsnws.net/article/194
// 通常のルーティング Route::get('/hello', function () { return 'Hello World'; }); // パラメータを取得 Route::get('/user/{id?}', function ($id = null) { return 'User ' . $id; }); // ルートプレフィックス Route::prefix('/admin')->group(function () { Route::get('/user/{id?}', function ($id = null) { return 'Admin User ' . $id; }); });
以下のコマンドでルーティングを確認できる php artisan route:list ■Resourcefulにルーティング Laravelのルーティング書き方まとめ - Qiita http://qiita.com/michiomochi@github/items/de19c560bc1dc19d698c Route::resource() で典型的なCRUDルートを設定できる
Route::resource('photos', 'PhotoController');
only で特定のメソッドのみ有効にできる
Route::resource('photos', 'PhotoController', ['only' => ['store']]);
以下、CRUDの具体例 \test\routes\web.php
// Resourcefulルーティング Route::resource('/restful', 'RestfulController');
\test\app\Http\Controllers\RestfulController.php
<?php namespace App\Http\Controllers; class RestfulController extends Controller { // getでrestful/にアクセスされた場合 public function index() { return 'RestfulController@index()'; } // getでrestful/createにアクセスされた場合 public function create() { return 'RestfulController@create()'; } // postでrestful/にアクセスされた場合 public function store() { return 'RestfulController@store()'; } // getでrestful/messageにアクセスされた場合 public function show($message) { return 'RestfulController@show()'; } // getでrestful/message/editにアクセスされた場合 public function edit($message) { return 'RestfulController@edit()'; } // putまたはpatchでrestful/messageにアクセスされた場合 public function update($message) { return 'RestfulController@update()'; } // deleteでrestful/messageにアクセスされた場合 public function destroy($message) { return 'RestfulController@destroy()'; } }
■外部から直接POSTする 普通にPOSTでリクエストすると、VerifyCsrfTokenによってアクセスが拒否される VerifyCsrfToken の $except に対象URLを記載することにより、VerifyCsrfTokenの対象から除外できる 以下は「/enter」を除外する例 /app/Http/Middleware/VerifyCsrfToken.php
<?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { /** * CSRFの確認を除外するURI * * @var array */ protected $except = [ // '/enter', ]; }
■テンプレート
■Bladeテンプレート /resources/views/test.php
test: <?php echo date('Y-m-d H:i:s') ?>
※テンプレートは通常のPHP /resources/views/test.blade.php
test: <?php echo date('Y-m-d H:i:s') ?> <hr> csrf: {{ csrf_field() }}
※ファイル名を変えるとbladeの記法が使える 「test.php」と「test.blade.php」の両方が存在する場合、「test.blade.php」が使われる
@extends('layouts.app') @section('content') test: <?php echo date('Y-m-d H:i:s') ?> <hr> csrf: {{ csrf_field() }} @endsection
※このようにするとレイアウトファイルが使われ、コンテンツが「layouts/app.blade.php」の@yield('content') 部分に埋め込まれる ※yield=譲渡する・委ねる
$articles = Article::orderBy('created_at', 'asc')->get(); return view('test', [ 'articles' => $articles ]);
このように割り当てて、テンプレート側では以下のように参照する
@foreach ($articles as $article) {{ $article->subject }} : {{ $article->detail }}<br> @endforeach
■エスケープ {{ 〜 }} でHTMLがエスケープされる {!! 〜 !!} でHTMLがそのまま表示される。必要に応じて自前でエスケープする ただし Htmlable を実装したクラスのインスタンスの場合はエスケープされない 具体的には {{ csrf_field() }} のようなコードが該当する http://www.msng.info/archives/2016/01/laravel-blade-braces-dont-always-escape.php Laravel4では {{{ 〜 }}} でHTMLがエスケープされる {{ 〜 }} でHTMLがそのまま表示される。必要に応じて自前でエスケープする という挙動だった ■文章を改行して表示
{!! nl2br(e($body)) !!}
もしくは /app/Providers/ViewServiceProvider.php の boot() メソッド内で以下を定義する
\Blade::setEchoFormat('nl2br(e(%s))');
これで {{ 〜 }} でHTMLがエスケープされる。改行は改行タグが追加される {{{ 〜 }}} でHTMLがエスケープされるのみ {!! 〜 !!} でHTMLがそのまま表示される。必要に応じて自前でエスケープする となる How do I use nl2br() in Laravel 5 Blade https://stackoverflow.com/questions/28569955/how-do-i-use-nl2br-in-laravel-5-blade
■ファイル
■ファイルの保存(ローカル) ファイルストレージ 5.5 Laravel https://readouble.com/laravel/5.5/ja/filesystem.html コントローラなどで以下を読み込む use Illuminate\Support\Facades\Storage; 以下のコードを実行すると Storage::put('test/1.txt', 'test1'); 以下にファイルが保存される。ファイル内には「test1」と書き込まれる storage\app\test\1.txt ■ファイルの保存(S3) Laravel 5.4でS3ファイルアップロード - Qiita https://qiita.com/zz22394/items/cd960124c3aec164a24d ファイルのアップロード - ララジャパン http://www.larajapan.com/tag/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%A2%E3%83%83%E3%83%9... あらかじめAWSのコンソールで、S3のバケットを作成しておく ドライバをインストール composer require league/flysystem-aws-s3-v3 S3を使うために、.envに設定を追加する
AWS_KEY=XXXXX AWS_SECRET=YYYYY AWS_REGION=ap-northeast-1 AWS_BUCKET=laravel-test
コントローラなどで以下を読み込む use Illuminate\Support\Facades\Storage; 以下のコードを実行するとアップロードされる Storage::disk('s3')->put('test/1.txt', 'test1', 'public'); AWSのコンソールで、ファイルが作成されていることを確認する ■ファイルアップロード 未検証 Laravelで画像ファイルアップロードをする簡単なサンプル - Qiita http://qiita.com/makies/items/0684dad04a6008891d0d Laravel上画像アップロード - Qiita http://qiita.com/samudra/items/3cb97bd6f1750c3f8134 ■CSSファイルやJSファイルのパスに、自動でファイルのタイムスタンプを追加する LaravelでBladeを拡張する(@asset( )) - Qiita https://qiita.com/reflet/items/a24b3c8e809f75fdbc19 実際は、以下のようにして組み込んだ app\Providers\BladeServiceProvider.php を作成
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Blade; class BladeServiceProvider extends ServiceProvider { /** * Bladeサービスの初期化処理 * * @return void */ public function boot() { /** * CSSファイルやJSファイルのパスに、ファイルのタイムスタンプを追加 * * @param string $file * @return string */ Blade::directive('asset', function ($file) { $file = str_replace(["'", '"'], '', $file); $path = public_path() . '/' . $file; try { $timestamp = "?<?php echo \File::lastModified('{$path}') ?>"; } catch (\Exception $e) { $timestamp = ''; } return asset($file) . $timestamp; }); } /** * アプリケーションサービスの登録 * * @return void */ public function register() { // } }
config\app.php を編集
'providers' => [ 〜略〜 App\Providers\BladeServiceProvider::class,
ビュー内で以下のように asset を使用すると、ファイル名の最後に「?1541565515」のような値が追加される
<script src="@asset('js/app.js')"></script> <script src="@asset('js/plugins/datetimepicker/jquery.datetimepicker.full.min.js')"></script>
■データベース
■マイグレーションとモデルの作成 php artisan make:migration create_articles_table ... 「create_articles_table」というマイグレーションクラスを作る php artisan make:migration create_articles_table --table=articles ... 「articles」テーブルを作成するカラのコードを追加する php artisan make:migration create_articles_table --create=articles ... 「articles」テーブルを作成・削除するコードを追加する php artisan make:model Article ... 「Article」というモデルを作成 php artisan make:model Person -m ... Personモデルとpeopleテーブルのマイグレーションをセットで作成。Person は people になる Model created successfully. Created Migration: 2016_12_05_151532_create_people_table ※コマンドラインで操作 ※artisan=職人 ■マイグレーションでエンジンを設定 $table->engine = 'InnoDB'; ■マイグレーションで真偽値を設定 available TINYINT(1) NOT NULL COMMENT '有効', MySQLでこのような定義を行いたい場合、以下のように指定する $table->boolean('available')->comment('有効'); ■マイグレーションでコメントを設定 [Laravel]マイグレーションでテーブルにコメントを入れる|atuweb - ヒーローになるIT人材育成研究室 https://ameblo.jp/atuweb/entry-12204289098.html Laravel - Laravel のマイグレーションでテーブルカラムにコメントを設定する(91732)|teratail https://teratail.com/questions/91732 ■既存のテーブル定義からマイグレーションを作成 Laravelで既存のDBからmigrationsファイルを作成する「migrations-generator」 - Qiita https://qiita.com/busyoumono99/items/a7173ad6b9b041da09dd ■基本的なリレーション モデルで以下のようなメソッドを定義
class User extends Authenticatable { public function entries() { return $this->hasMany('App\Entry'); }
コントローラで以下のようにして取得
$users = App\User::get(); foreach ($users as $user) { $result .= $user->id . ' | ' . $user->name . ' | ' . $user->email . '<br>'; foreach ($user->entries as $entry) { $result .= $entry->id . ' | ' . $entry->title . '<br>'; } $result .= '<br>'; }
Eloquent:リレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent-relationships.html 親子関係のテーブルでのクエリーの作成(Eloquent編) - ララジャパン https://www.larajapan.com/2016/07/09/%E8%A6%AA%E5%AD%90%E9%96%A2%E4%BF%82%E3%81%AE%E3%83%86%E3%83%BC... ■N+1問題 以下のように with を使うと、あらかじめ関連するデータを取得する
$users = App\User::get(); ↓ $users = App\User::with('entries')->get();
Eloquent:リレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent-relationships.html 「Eagerローディング」の項目を参照 width で読み込んだデータを対象に、whereで絞り込みを行うこともできる 「Eagerロードへの制約」の項目を参照 Laravel|STEP UP BLOG http://capella.3rin.net/Category/2/ ■記事とカテゴリ、記事とタグなど多対多のリレーション 「(33) 多対多のリレーション モデル/DB編」の解説によると、 ・「記事が複数のタグを持てる」のようなリレーションを行う場合は中間テーブルを使う ・中間テーブルは、テーブル名を関連するモデル名をアルファベット順で並べた名前にする(デフォルトの規約) ・中間テーブルのタイムスタンプを更新したい場合、withTimestamps() を指定する ・中間テーブルにオートインクリメントな主キーは、あっても無くてもいい みたい Eloquent:リレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent-relationships.html#many-to-many 初めてのLaravel 5.1 : (33) 多対多のリレーション モデル/DB編 - ララ帳 https://laravel10.wordpress.com/2015/03/30/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AElaravel-5-33-%E5%A4%9... 初めてのLaravel 5.1 : (34) 多対多のリレーション UI編 - ララ帳 https://laravel10.wordpress.com/2015/03/31/%e5%88%9d%e3%82%81%e3%81%a6%e3%81%aelaravel-5-34-%e5%a4%9... Laravel5.4で基本的なリレーションを学んでみる | webOpixel http://www.webopixel.net/php/1261.html Laravel5.2で多対多(BelongsToMany)のリレーション | will STYLE Inc.|神戸にあるウェブ制作会社 https://www.willstyle.co.jp/blog/353/ ■外部キー制約 要検証 以下のようにすれば、entriesテーブルやtagsテーブルのデータが削除された時、自動でentry_tagのデータも削除される が、代理キーで管理するなら原則として削除されないことになる 「想定外のidが入らないように」という点では有効だが、外部キー制約を使うか否かは要検討 また、外部キー制約を付けるとしても、マイグレーションでは行わずにシーダーで行うほうがいい?
Schema::create('entry_tag', function(Blueprint $table) { $table->integer('entry_id')->unsigned()->index(); $table->integer('tag_id')->unsigned()->index(); $table->foreign('entry_id')->references('id')->on('entries')->onDelete('cascade'); $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); });
■子モデル更新時に親モデルのタイムスタンプを更新 未検証 子モデル側の $touches プロパティにリレーション先を設定する
protected $touches = ['post'];
Eloquent:リレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent-relationships.html ■主キーをUUIDにする LaravelでUUIDを使う際の設定 - Qiita https://qiita.com/NewBieChan/items/3d0f8025accd770bd6d3 UUID - Qiita https://qiita.com/hththt/items/801bf4664332a0346650 UUIDはなぜ、分散環境で好き勝手に生成しても衝突しないのか。RFC4122規格とUUIDの性質。 | 三度の飯とエレクトロン http://blog.katty.in/5124 ■主キーを複合主キーにする 代理キーで管理するのが前提となっているみたい。 複合主キーに対応できないわけではないみたいだが、代理キーを使っておくのが無難 他の主要フレームワークも代理キー前提が基本なので、 「複合主キーを使いたいからLaravelは採用しない」とはならなさそう 複合主キーにしてリレーションに問題が起きないかは要検証 [Laravel]Eloquent ORMで複合主キーはオススメしない http://atuweb.net/201610_laravel-eloquent-orm-id-agreement/ PHP - Laravelで複合主キーを使ったモデルを更新できない(36627)|teratail https://teratail.com/questions/36627 Laravel5で複合キーを扱う - KaokenとLaravel https://laravel.cg0.xyz/handle-compound-keys-with-laravel-5/ LaravelのEloquentを使って複合主キーを使用したテーブルを操作する方法 - Qiita http://qiita.com/wrbss/items/7245103a5fef88cbdde9 Eloquentで多対多のリレーション - Laravel Room https://laravel-room.com/many-to-many-relationshop ■ユニークな複合キー
$table->unique(['first', 'last']);
データベース:マイグレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/migrations.html ■データを検索して一覧表示 検索機能の作成 - Laravel学習帳 http://laraweb.net/tutorial/607/ ■操作したユーザの自動記録 Laravelのeloquentのeventでcreated_byとかupdated_byとか更新するobserverとtrait - Qiita https://qiita.com/maimai-swap/items/6597c04721adbc48fec2 Soft delete: "deleting" event does not automatically update the model before it is soft deleted - Issue #4990 - laravel/framework https://github.com/laravel/framework/issues/4990 Eloquent: Getting Started - Laravel - The PHP Framework For Web Artisans https://laravel.com/docs/5.3/eloquent#events Eloquent:利用の開始 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent.html#events 各テーブルで created_by_user_id, updated_by_user_id, deleted_by_user_id これらの値を更新する必要があるが、都度コントローラから指定すると更新し忘れが発生する 自動で更新する仕組みを導入するといい 各モデルで必要に応じて以下のtraitを宣言すれば、自動で更新されるようにしている use RegisterAuthor; ■トランザクション 以下のように記述すると、ロールバックやコミットを自動で制御してくれる
DB::transaction(function () { DB::table('users')->update(['votes' => 1]); DB::table('posts')->delete(); });
もしくは以下を個別に呼び出す
DB::beginTransaction(); DB::rollBack(); DB::commit();
データベース:利用開始 5.4 Laravel https://readouble.com/laravel/5.4/ja/database.html ■生SQLの指定 Laravel5のアーキテクチャから学ぶより良いクラス設計 - Qiita http://qiita.com/nunulk/items/2c637d3952096ef74677
$users = DB::table('users') ->join('contracts', 'contracts.user_id', '=', 'users.id') ->select('users.id', DB::raw('count(*) as contract_count')) ->orderBy('users.id') ->groupBy('users.id') ->having(DB::raw('count(*)'), '>', $count) ->get() ; ↓ $sql =<<<EOQ; SELECT u.id, count(*) as contract_count FROM users INNER JOIN contracts c ON c.user_id = u.id GROUP BY u.id HAVING count(*) > ? ORDER BY u.id EOQ; $users = DB::select($sql, [$count]);
■ソフトデリート マイグレーションを作成 php artisan make:migration alter_articles_table マイグレーションを修正
public function up() { Schema::table('articles', function (Blueprint $table) { $table->softDeletes(); //$table->timestamp('deleted_at')->nullable()->after('updated_at'); }); } public function down() { Schema::table('articles', function (Blueprint $table) { $table->dropColumn('deleted_at'); }); }
マイグレーションを実行 php artisan migrate モデルを修正 C:\localhost\home\test\public_html\laravel\test\app\Article.php
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Article extends Model { use SoftDeletes; /** * 日付へキャストする属性 * * @var array */ protected $dates = ['created_at', 'updated_at', 'deleted_at']; }
データベース:マイグレーション 5.4 Laravel https://readouble.com/laravel/5.4/ja/migrations.html Eloquent:利用の開始 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent.html 削除処理の流れ データ削除時、通常は \test\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php 内の delete() が呼ばれる その中で performDeleteOnModel() が呼ばれており、これにより削除処理が走る SoftDeletesトレイトでこのメソッドを上書きしているので、これにより削除時の挙動が変わる 取得処理の流れ Laravelの機能で、モデルは「boot + トレイト名」を最初に実行する よって bootSoftDeletes() が最初に実行される このメソッド内でスコープを渡し、データ取得時はそれによって構築されるクエリが変化する http://qiita.com/niisan-tokyo/items/d3be588b53df8fa0278c ■複数代入 \test\app\Article.php
/** * 複数代入する属性 * * @var array */ protected $fillable = ['subject', 'detail'];
\test\app\Http\Controllers\ArticleController.php
// 通常の代入 $article = new Article; $article->subject = $request->subject; $article->detail = $request->detail; $article->save(); // 複数代入 $article = new Article; $article->fill($request->all())->save(); // このようにも書ける Article::create($request->all());
複数代入を使用する場合、意図しない代入を防ぐためあらかじめモデルで $fillable で代入を許可する項目を指定するか $guarded で代入を許可しない項目を指定するか を定義しておく必要がある。原則として $fillable での指定で良さそう
// 編集ならこのように書けば良さそう Article::find(15)->update($request->all()); // 削除ならこのように書けば良さそう Article::find(15)->delete(); Article::findOrFail(15)->delete();
【Laravel:Eloquentクラス】fillableとguardedの指定はどちらかだけでいい - Qiita http://qiita.com/kk_take/items/3e0639ed605f74c34619 Article::findOrFail(15) の書き方については、以下の「Not Found例外」を参照 Eloquent:利用の開始 5.4 Laravel https://readouble.com/laravel/5.4/ja/eloquent.html ■モデルの型キャスト 【Laravel】DBから取得した値を$castsで型変換する【Attribute Casting】 - Qiita http://qiita.com/amymd/items/7cba844b03754f9c849b
■テスト・デバッグ
■デバッグ デバッグモード .env APP_DEBUG=true デバッグバー composer require barryvdh/laravel-debugbar config/app.php
'providers' => [ Barryvdh\Debugbar\ServiceProvider::class, 'aliases' => [ 'Debugbar' => Barryvdh\Debugbar\Facade::class,
アプリケーションログ /storage/logs Laravel5.5: Laravel Debugbarを使う - Qiita https://qiita.com/sutara79/items/9fd442a81001842aeba1 config/app.php で設定しているドメインと異なるドメインでアクセスすると、デバッグバーは表示されないようなので注意 (正しいドメイン、もしくはIPアドレスでのアクセスなら表示されるみたい) Laravel5.0でlaravel-debugbarを使う - Qiita https://qiita.com/naga1460/items/4a5a5ede493ef008fe90 ■単体テスト テスト: テストの準備 5.4 Laravel https://readouble.com/laravel/5.4/ja/testing.html テストを実行 cd C:\localhost\home\test\public_html\laravel\blog phpunit 実行すると以下のエラーになる PHP Fatal error: Call to undefined method PHPUnit_Util_Configuration::getTestdoxGroupConfiguration() in C:\localhost\home\test\public_html\laravel\test\vendor\phpunit\phpunit\src\TextUI\TestRunner.php on line 1066 LaravelプロジェクトとJenkinsの連携 - ハマログ https://blog.e2info.co.jp/2017/05/10/laravel_and_jenkins/ PHPUnitのバージョンが問題みたいだが、解説のようにcomposerを実行しても解決せず C:\localhost\home\test\public_html\laravel\test\vendor\phpunit\phpunit\src\TextUI\TestRunner.php //$testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration(); 1066行目をコメントアウトしたら一応動く。ただし何らかの機能が削られていると思われる テストを実行 phpunit 以下のテストがはじめから作成されているので、これが実行される HTTPテストやChromeドライバーによるテストもできるみたい \tests\Feature\ExampleTest.php \tests\Unit\ExampleTest.php 設定ファイルを指定してテストを実行 phpunit --configuration phpunit.xml 特定のテストのみ実行 phpunit tests/Feature/RootTest.php --configuration phpunit.xml テストを作成 php artisan make:test ArticleTest --unit \test\tests\Unit\ArticleTest.php にテストが作成される 具体的なテストの作成については、以下が参考になる chapter8/app at master - laravel-jp-reference/chapter8 https://github.com/laravel-jp-reference/chapter8/tree/master/app ■データベースを利用する単体テスト \config\database.php の「connections」に以下の設定を追加(データベースにテスト時の値が登録されないように、SQLiteのインメモリ機能を利用する)
'testing' => [ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', 'option' => [ PDO::ATTR_PERSISTENT => true, ], ],
\phpunit.xml テストの際に上記設定を使用する
<env name="DB_CONNECTION" value="testing"/>
テスト実行時 「PHP Fatal error: Class 'Doctrine\DBAL\Driver\PDOSqlite\Driver' not found」 と表示されるようなら、doctrine/dbal をインストールする composer require doctrine/dbal ■ブラウザテスト(Selenium) $ composer require --dev laravel/dusk … とても時間がかかった。PHP5環境ではインストールできない? Using version ^2.0 for laravel/dusk ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 2 installs, 0 updates, 0 removals - Installing facebook/webdriver (1.4.1): Downloading (100%) - Installing laravel/dusk (v2.0.7): Downloading (100%) Writing lock file Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: laravel/dusk Package manifest generated successfully. app\Providers\AppServiceProvider.php に登録 php artisan dusk:install を実行すると、テストの雛形が作成される php artisan dusk でテストを実行できる。「--configuration phpunit.xml」のオプションは使えないみたい? php artisan dusk tests/Browser/AdminLoginTest.php で特定のテストのみを実行できる 通常アクセスかテストかを判定するなら、 $_SERVER['HTTP_USER_AGENT'] の値を調べるか ブラウザテスト(Laravel Dusk) 5.4 Laravel https://readouble.com/laravel/5.4/ja/dusk.html Laravel 5.4 で手軽にテストを書こう! | 株式会社インフィニットループ技術ブログ https://www.infiniteloop.co.jp/blog/2017/05/laravel-5-testing/ Laravel 5.4で導入されたLaravel Duskをテスト後にDBリセットさせるようにして試してみた - Qiita https://qiita.com/n_mogi/items/b96ccc1df31aa5fb859a Laravel5でPC/SPを振り分ける方法 - Qiita https://qiita.com/qwe001/items/0064adc3c893f603fe21 【Laravel 5.4】Laravel Duskによるブラウザテストの作成方法 - Qiita https://qiita.com/amymd/items/0a5f2705e29972d0d22e ブラウザテスト(Laravel Dusk) 5.4 Laravel https://readouble.com/laravel/5.4/ja/dusk.html なお 「Your requirements could not be resolved to an installable set of packages.」 のエラーになってインストールできない場合、 以下でインストールできることがある $ composer require --dev laravel/dusk:"^2.0" ブラウザテスト(Laravel Dusk) 5.5 Laravel https://readouble.com/laravel/5.5/ja/dusk.html また 「Laravel Facebook\WebDriver\Exception\SessionNotCreatedException: session not created exception: Chrome version must be >= 62.0.3202.0」 のようなエラーが表示される場合、Chromeのバージョンが低い可能性がある Homestead内でupdateを実行する $ sudo apt-get update $ sudo apt-get upgrade $ sudo apt-get dist-upgrade Laravel-dusk does not work - Stack Overflow https://stackoverflow.com/questions/48471515/laravel-dusk-does-not-work ■アプリケーションログ アプリケーションにログを記録する際、ログレベルを意識しておくと対応の切り分けなどに役立つ ログレベルは PSR-3 で定義されている 【PHP】PSR-3 Logger Interface(ロガーインタフェース) https://www.ritolab.com/entry/95 どのログレベルを使用するかなど、以下が参考になる ログレベルちゃんと使い分けてますか? - OTOBANK Engineering Blog https://engineering.otobank.co.jp/entry/2016/09/20/181756 Laravelでログを使う場合、以下などを参考にする エラーとログ 5.5 Laravel https://readouble.com/laravel/5.5/ja/errors.html Laravel5.6 での ログ設定について - Qiita https://qiita.com/hrdaya/items/b01d5621937a0710ca64
■メモ
■APP_KEYの設定 他環境で実行する場合、GitからPULLしたあとに以下の操作が必要かも? もしくは、キーを .env.example に含めて大丈夫か [Laravel] アプリケーションキー(APP_KEY)を設定する - 端くれプログラマの備忘録 http://www.84kure.com/blog/2015/11/17/laravel-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7... 以下を読む限り「キーがなければ生成する」くらいのもので、.env.example に含めておけば良さそう 初めてのフレームワーク-Laravel5-4エラー時の対策メモ - Qiita https://qiita.com/k-okada/items/0fa94f076d08584ee314 ■クラスの省略呼び出し use App\Article; と宣言しておくと、以下のように省略して呼び出せる
$article = new App\Article; ↓ $article = new Article;
■enumを扱う Laravel-enumをインストールすれば使える Laravel-enumの使い方 - Qiita https://qiita.com/sayama0402/items/4e8a885fed367090de12 ただし定番の方法は言いづらい 以下などを参考に自作する方が無難かも Laravelでつくる、深くて表現豊かなモデル - Qiita https://qiita.com/nunulk/items/0820fd19690066d092d5 ■セッション
// セッション Route::get('/session', function () { $count = session('count', 0) + 1; session(['count' => $count]); return 'count=' . $count; }); // 以下のような書き方もできる $request->session()->get('key');
複数台構成でセッションをデータベースに保存する場合、以下が参考になる HTTPセッション 5.5 Laravel https://readouble.com/laravel/5.5/ja/session.html ■メールアドレスとパスワードではなく、ユーザ名とパスワードで認証する 未検証 LoginController で username() を定義する
public function username() { return 'username'; }
認証 5.4 Laravel https://readouble.com/laravel/5.4/ja/authentication.html 「ユーザー名のカスタマイズ」を参照 Laravelでユーザー名またはメールアドレスでログインする - むらさきラボ http://y6rasaki.hatenablog.com/entry/2016/11/17/222713 ■キャッシュ 未検証 CacheManagerをメソッドインジェクションなどから使う方法、Cache::get() を使う方法、 Eloquentの remember() を使う方法、などがあるみたい キャシュ 5.4 Laravel https://readouble.com/laravel/5.4/ja/cache.html Laravel Recipes日本語版 | クエリー結果をキャッシュする http://recipes.laravel.jp/recipe/748 ■Queue 未検証 キューが正しく動作しなくなったら、以下で再起動すると正常になる…ことが多いらしい。要検証 php artisan queue:restart Laravel の Queue で非同期処理を実装する(beanstalkd / IronMQ / SQS) - Shin x blog http://www.1x1.jp/blog/2014/08/laravel-queue-guide.html Laravel5でAWSのSQSを使う 複数のキューを処理する方法 - ECサイト運営開発記 http://nohohox.hateblo.jp/entry/2016/02/26/030616 キュー 5.4 Laravel https://readouble.com/laravel/5.4/ja/queues.html キュー - ララ帳 https://laravel10.wordpress.com/2015/05/07/%E3%82%AD%E3%83%A5%E3%83%BC/ ■APIの作成 APIアクセスの例 /api/example routes\api.php にAPI用のルーティングを設定し、 app\Http\Controllers\Api\ExampleController.php で処理を行う ■コマンドの作成 Artisanコンソール 5.5 Laravel https://readouble.com/laravel/5.5/ja/artisan.html タスクスケジュール 5.5 Laravel https://readouble.com/laravel/5.5/ja/scheduling.html laravelでバッチ作ってcronで動かしてみた - Qiita https://qiita.com/ritukiii/items/a70d89fa988b2d9afbc4 php artisan make:command Example を実行すると app\Console\Commands\Example.php が作成されるので、これを編集する php artisan list コマンドの登録を確認 php artisan example コマンドを実行 /etc/crontab にCronを記載する場合、
* * * * * php /var/www/vhosts/asims2/artisan schedule:run >> /dev/null 2>&1
ではなく、以下のように実行ユーザとパスも指定する必要があるので注意
* * * * * nginx /usr/bin/php /var/www/vhosts/asims2/artisan schedule:run >> /dev/null 2>&1
重複起動はwithoutOverlappingで防止できるが、複数台サーバだと防止にならないので注意 Laravel Scheduler のコードリーディング - Qiita https://qiita.com/mikakane/items/e9d2a70174e8af584db0 Laravelでタスクをスケジュール実行する | scratchpad https://takayukii.me/20160824836 ■関数やクラスのパスを取得 exit(ArticlesTableSeeder::class); exit(event::class); ■動作環境の確認 php artisan env Current application environment: local .envの以下の部分で設定される APP_ENV=local production(実働環境)とtesting(ユニットテスト環境)は標準で予約されている それ以外、local(開発環境)やstaging(デプロイ候補環境)など自由に設定できる production に設定されている場合、 マイグレーションやシーディングの実行時に確認が求められる ■バージョンの確認 php artisan --version Laravel Framework 5.4.34 Artisanコマンドライン 5.dev Laravel https://readouble.com/laravel/5.dev/ja/artisan.html \test\vendor\laravel\framework\src\Illuminate\Foundation\Application.php コマンドで確認できるが、このファイルにある
const VERSION = '5.4.34';
この部分の数値が表示されている ■SSLの強制 /routes/web.php の最後に以下を追加する
// 本番環境ではSSLを強制 if (app()->environment('production')) { URL::forceScheme('https'); }
productionでhttpsにしたい - ド忘れ防止雑記帳 http://dolphin.hatenablog.jp/entry/2015/04/27/010635 ■万年カレンダーを表示する Laravel+Carbonでカレンダーの作り方 - Crieit https://crieit.net/posts/Laravel-Carbon /routes/web.php
use Carbon\Carbon; Route::get('/calendar', function () { $year = date('Y'); $month = date('m'); $dateStr = sprintf('%04d-%02d-01', $year, $month); $date = new Carbon($dateStr); // カレンダーを四角形にするため、前月となる左上の隙間用のデータを入れるためずらす $date->subDay($date->dayOfWeek); // 同上。右下の隙間のための計算 $count = 31 + $date->dayOfWeek; $count = ceil($count / 7) * 7; $dates = []; for ($i = 0; $i < $count; $i++, $date->addDay()) { // copyしないと全部同じオブジェクトを入れてしまうことになる $dates[] = $date->copy(); } return view('calendar', [ 'dates' => $dates, 'year' => $year, 'month' => $month, ]); });
/resources/views/calendar.blade.php
<!doctype html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>カレンダー</title> <style> table tr th, table tr td { border: 1px solid #666; } table tr td.secondary { color: #aaa; } </style> </head> <body> <h1>カレンダー</h1> <table class="table table-bordered"> <thead> <tr> @foreach (['日', '月', '火', '水', '木', '金', '土'] as $dayOfWeek) <th>{{ $dayOfWeek }}</th> @endforeach </tr> </thead> <tbody> @foreach ($dates as $date) @if ($date->dayOfWeek == 0) <tr> @endif <td @if ($date->month != $month) class="secondary" @endif > {{ $date->day }} </td> @if ($date->dayOfWeek == 6) </tr> @endif @endforeach </tbody> </table> </body> </html>
■確認画面のある問い合わせフォームを作る マイグレーションを作成 php artisan make:migration create_contacts_table --create=contacts マイグレーション内容を修正
Schema::create('contacts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email'); $table->string('subject'); $table->text('body'); $table->timestamps(); $table->softDeletes(); });
マイグレーションを実行 php artisan migrate モデルを作成 php artisan make:model Contact モデルを修正
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Contact extends Model { use SoftDeletes; /** * 日付へキャストする属性 * * @var array */ protected $dates = ['created_at', 'updated_at', 'deleted_at']; /** * 複数代入する属性 * * @var array */ protected $fillable = ['name', 'email', 'subject', 'body']; }
フォームリクエストを作成 php artisan make:request StoreContactPost フォームリクエストを修正
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreContactPost extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|max:255', 'email' => 'required|max:255', 'subject' => 'required|max:255', 'body' => 'required|max:255', ]; } }
ルーティングを追加
Route::prefix('/contact')->group(function () { Route::get('/', 'ContactController@index'); Route::post('/confirm', 'ContactController@confirm'); Route::post('/send', 'ContactController@store'); Route::get('/complete', 'ContactController@complete'); });
コントローラを作成
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Requests\StoreContactPost; use App\Http\Controllers\Controller; use App\Contact; class ContactController extends Controller { /** * Display a input form. * * @param Request $request * @return Response */ public function index(Request $request) { return view('contact.index'); } /** * Confirm a new contact. * * @param Request $request * @return Response */ public function confirm(StoreContactPost $request) { $data = $request->all(); $request->session()->put($data); return view('contact.confirm', compact('data')); } /** * Create a new contact. * * @param Request $request * @return Response */ public function store(Request $request) { if ($request->get('action') === 'back') { return redirect('/contact')->withInput($request->session()->all()); } Contact::create($request->session()->all()); $request->session()->flush(); return redirect('/contact/complete'); } /** * Display a complete. * * @return Response */ public function complete() { return view('contact.complete'); } }
ビューを作成: contact/index.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="col-sm-offset-2 col-sm-8"> <div class="panel panel-default"> <div class="panel-heading"> Contact </div> <div class="panel-body"> <!-- Display Validation Errors --> @if (count($errors) > 0) <!-- Form Error List --> <div class="alert alert-danger"> <strong>Whoops! Something went wrong!</strong> <br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <!-- Contact Form --> <form action="{{ url('contact/confirm')}}" method="POST" class="form-horizontal"> {{ csrf_field() }} <!-- Contact Name --> <div class="form-group"> <label for="contact-name" class="col-sm-3 control-label">Name</label> <div class="col-sm-6"> <input type="text" name="name" id="contact-name" class="form-control" value="{{ old('name') }}"> </div> </div> <!-- Contact Email --> <div class="form-group"> <label for="contact-email" class="col-sm-3 control-label">Email</label> <div class="col-sm-6"> <input type="text" name="email" id="contact-email" class="form-control" value="{{ old('email') }}"> </div> </div> <!-- Contact Subject --> <div class="form-group"> <label for="contact-subject" class="col-sm-3 control-label">Subject</label> <div class="col-sm-6"> <input type="text" name="subject" id="contact-subject" class="form-control" value="{{ old('subject') }}"> </div> </div> <!-- Contact Body --> <div class="form-group"> <label for="contact-body" class="col-sm-3 control-label">Body</label> <div class="col-sm-6"> <textarea name="body" cols="50" rows="10" class="form-control">{{ old('body') }}</textarea> </div> </div> <!-- Confirm Button --> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" class="btn btn-default"> <i class="fa fa-btn fa-plus"></i>Confirm </button> </div> </div> </form> </div> </div> </div> </div> @endsection
ビューを作成: contact/confirm.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="col-sm-offset-2 col-sm-8"> <div class="panel panel-default"> <div class="panel-heading"> Contact </div> <div class="panel-body"> <!-- Contact Form --> <form action="{{ url('contact/send')}}" method="POST" class="form-horizontal"> {{ csrf_field() }} <!-- Contact Name --> <div class="form-group"> <label for="contact-name" class="col-sm-3 control-label">Name</label> <div class="col-sm-6"> {{ $data['name'] }} </div> </div> <!-- Contact Email --> <div class="form-group"> <label for="contact-email" class="col-sm-3 control-label">Email</label> <div class="col-sm-6"> {{ $data['email'] }} </div> </div> <!-- Contact Subject --> <div class="form-group"> <label for="contact-subject" class="col-sm-3 control-label">Subject</label> <div class="col-sm-6"> {{ $data['subject'] }} </div> </div> <!-- Contact Body --> <div class="form-group"> <label for="contact-body" class="col-sm-3 control-label">Body</label> <div class="col-sm-6"> {!! nl2br(e($data['body'])) !!} </div> </div> <!-- Send Button --> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" name="action" value="back" class="btn btn-default"> Back </button> <button type="submit" class="btn btn-default"> <i class="fa fa-btn fa-plus"></i>Send </button> </div> </div> </form> </div> </div> </div> </div> @endsection
ビューを作成: contact/complete.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="col-sm-offset-2 col-sm-8"> <div class="panel panel-default"> <div class="panel-heading"> Contact </div> <div class="panel-body"> Complete </div> </div> </div> </div> @endsection
Laravel5で確認画面を経由したDB登録を実装する | Laravel | Ayumi Folio http://aym.sakura.ne.jp/system/20160521/ Laravel 5で確認画面付き問い合わせフォームを作る - Qiita http://qiita.com/ponko2/items/fd7ac38b964e10f16f52
■トラブル対応
■マイグレーションを実行できない 以下のエラーが表示される場合、 データベースの文字コードがutf8mb4の場合にVARCHARの最大文字数が191文字に制限されるため [Illuminate\Database\QueryException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was t oo long; max key length is 767 bytes (SQL: alter table `users` add unique ` users_email_unique`(`email`)) [PDOException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was t oo long; max key length is 767 bytes /test/app/Providers/AppServiceProvider.php の宣言に use Illuminate\Support\Facades\Schema; を追加し、さらに boot() 内に以下を追加するとマイグレーションできるようになる (なお、191を例えば255にすると「長すぎる」といってエラーになる)
Schema::defaultStringLength(191);
個別のマイグレーションを編集して対応はできるが、 他にもLaravelがマイグレーションファイルを作成することがあるため、この設定を入れておく方が無難 MySQL - Laravel 5.4 デフォルトで設定されているマイグレーションファイルを実行するとSQLエラーが出る(63441) https://teratail.com/questions/63441 MySQLのインデックスサイズに767byteまでしかつかえない問題と対策 - ハマログ https://blog.e2info.co.jp/2017/04/17/mysql%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%... ■意図したファイルを読み込めない・参照できない ファイルを参照できないときに試すこと 以下のように直接読み込めば参照できるが、正しい方法ではない
$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); require_once $baseDir . '/database/seeds/ArticlesTableSeeder.php'; $this->call(ArticlesTableSeeder::class);
このような場合、 ・use を指定しているか ・/config/app.php でパスは設定されているか ・/config/ 内の他のファイルにもパス指定がある ・/app/Http/Kernel.php でパスは設定されているか ・/app/Providers/AppServiceProvider.php でパスは設定されているか ・/app/Providers/ 内の他のファイルにもパス指定がある などを確認する。また composer dump-autoload でオートロードの設定を更新できる php artisan config:clear で設定(.env や config/*.php のファイル)のキャッシュを削除できる artisan view:clear でビューのキャッシュを削除できる artisan route:clear でルートのキャッシュを削除できる Laravel キャッシュクリア系コマンドなど - Qiita https://qiita.com/Ping/items/10ada8d069e13d729701 Laravelで名前空間を指定してオートロードされなかったら見る場所。 - Qiita http://qiita.com/niiyz/items/5b83ef5255a1ec64d9d6 [Laravel]デプロイ時の最適化 - Qiita http://qiita.com/qiita-kurara/items/d37dbc5b67e6b6dfbe1d ■「APP_ENV=production」にすると動作しない $ vi .env
APP_ENV=production
と設定するとエラーになって動作しない。「It is unsafe to run Dusk in production.」というエラーになっている Laravel5.4から5.5へ更新する - Qiita https://qiita.com/sutara79/items/c07882be23a53c498482 「解決策: discoverの対象からDuskを外す」 $ vi composer.json
"extra": { "laravel": { "dont-discover": [ "laravel/dusk" … 追加 ] } },
$ composer install これで大丈夫…のはずだが、試した時は以下の警告も表示された。(composer install は実行できる) Package phpoffice/phpexcel is abandoned, you should avoid using it. Use phpoffice/phpspreadsheet instead. 調べてみると composer.json 内に「excel」の文字は無いが composer.lock 内にはある という状態だった composer.lock 内に「phpoffice/phpexcel」の記述があるので、composer.lock 自体を削除してから再度実行 $ composer install これで大丈夫だった ■「APP_ENV=production」にするとマイグレーションとシーダーを直接実行できない $ vi .env
APP_ENV=production
と設定すると、マイグレーションやシーダーの実行時に確認が表示される これ自体は有用な仕組みだが、シェルスクリプトなどから実行したい場合には煩わしい 以下のように「--force」を付ければ、確認なしにマイグレーションやシーダーを実行できる $ php artisan migrate --force $ php artisan db:seed --force ■「PHP Fatal error: Maximum function nesting level of '512' reached, aborting!」というエラーになる 以下の現象ではあるが、上限を上げても解決しない 無限ループになってしまっているみたい PHP - Fatal error: Maximum function nesting level of '100' reached, aborting!(169)|teratail https://teratail.com/questions/169 複数のクラスから同じオブジェクトを注入しようとしたとき、このような現象になることがあるみたい? Laravelのコンストラクタインジェクションの仕様らしい? 部分的にコンストラクタインジェクションではなくメソッドインジェクションを使うか、 AppServiceProviderに登録するなどで回避できるみたい どういった方法がベストなのかは要検討 LaravelのDIの挙動を勘違いしていてドハマリした件 - Qiita https://qiita.com/ak-ymst/items/00e7d3cee16ba6d3e710
■HomesteadによるPHP5.6開発環境構築(旧メモ)
※HomesteadでPHP5.6環境を作る Homestead 0.3.3 をインストールすれば、PHP5.6環境ができる homesteadの導入とか - Qiita https://qiita.com/onekuri/items/02d9eb35774d1cec2d64 PHP5.6のLaravel Homestead環境を作成する | 遊び場 http://www30304u.sakura.ne.jp/blog/?p=3201 PHP - Laravel HomesteadにてPHP7.0がデフォルトインストールされてしまう(24237)|teratail https://teratail.com/questions/24237 ■Homestead Vagrant Box のインストール >cd C:\vagrant >vagrant box add laravel/homestead --box-version 0.3.3 ==> box: Loading metadata for box 'laravel/homestead' box: URL: https://atlas.hashicorp.com/laravel/homestead This box can work with multiple providers! The providers that it can work with are listed below. Please review the list and choose the provider you will be working with. 1) virtualbox 2) vmware_desktop Enter your choice: … virtualboxで使うので1を選択 ==> box: Adding box 'laravel/homestead' (v0.3.3) for provider: virtualbox box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/0.3.3/providers/virtualbox.box box: Progress: 100% (Rate: 2296k/s, Estimated time remaining: --:--:--) ==> box: Successfully added box 'laravel/homestead' (v0.3.3) for 'virtualbox'! ■Homesteadのインストール C:\vagrant\homestead にインストールするものとする >cd C:\vagrant\homestead >git clone -b 2.0 https://github.com/laravel/homestead.git Homestead 初期設定スクリプトを実行(init.bat は存在しないので Git Bash で操作) $ cd /c/localhost/home/homestead/public_html/Homestead $ bash init.sh ■鍵の作成 「Homesteadによる開発環境構築」を参照 ■Homesteadの設定 設定ファイルを編集 0.3.3ではプロジェクトディレクトリ内に Homestead.yaml は作成されないみたい 以下の場所に作成されているので編集する C:\Users\ユーザ名\.homestead\Homestead.yaml
- map: ~/code ↓ - map: C:\vagrant\homestead\code
■hostsの設定 「Homesteadによる開発環境構築」を参照 ■Vagrantの起動 Git Bash から起動 $ vagrant up ■SSHで接続 SSHで接続 $ vagrant ssh もしくはPoderosaから以下で接続 Host: 127.0.0.1 Port: 2222 Username: vagrant Private key: C:/localhost/home/wms/public_html/Homestead/.vagrant/machines/default/virtualbox/private_key C:\vagrant\homestead\Code\Laravel\public\index.html を作成すると http://homestead.test/ でアクセスできる SSHで以下にアクセスするとファイルが作成されている /home/vagrant/Code/Laravel/public/index.html ■PHPの動作確認 以下を作成してphpinfoを表示すると、 /home/vagrant/Code/Laravel/public/phpinfo.php 以下のバージョンを確認できた PHP 5.6.15 Nginx 1.8.0 MySQL 5.0.11 ■MySQLの動作確認 $ mysql -u root -p … rootで接続 secret mysql> ALTER USER root@localhost IDENTIFIED BY 'secret'; ... 一度はパスワードの変更コマンドを実行しないと操作できない。この例ではパスワードは変更はしていない mysql> GRANT ALL PRIVILEGES ON test.* TO homestead@localhost IDENTIFIED BY '1234'; … 作業用にユーザを作成 $ mysql -u homestead -p … 作業用ユーザで接続 1234 mysql> CREATE DATABASE test; PHPからも接続できた 以降は、通常の手順でLaravelを扱えるはず
■Laravel 5.1 クイックスタート(旧メモ)
※特に理由がなければ、今なら Laravel 5.5 を使用するといい Laravel 5.1 インデックス https://readouble.com/laravel/5.1/ja/ ■クイックスタート 初心者向けタスクリスト https://readouble.com/laravel/5.1/ja/quickstart.html C:\localhost\home\test\public_html\laravel にLaravel本体をインストールするために、以下を実行する(Laravelがインストールされ、プロジェクトの雛形も作成される) composer create-project laravel/laravel quickstart --prefer-dist 以下のようなURLでアクセスできる http://localhost/~test/laravel/quickstart/public/ https://github.com/laravel/quickstart-basic を C:\localhost\home\test\public_html\laravel\quickstart に上書きで配置。以降 quickstart で作業するものとする この時点で http://localhost/~test/laravel/quickstart/public/ にアクセスすると、大量のエラーが表示された。composerでのライブラリインストールが必要みたい quickstart 内に composer.bat と composer.phar を配置する(Windows環境の場合) $ cd /path/to/quickstart $ composer install composerでアップデートしてから再度上のページにアクセスすると、今度はデータベース接続エラーが表示された phpMyAdminなどで quickstart データベースを作成する。文字コードは utf8mb4_general_ci にしておく 次に .env にあるデータベース接続情報を編集する
DB_HOST=127.0.0.1 DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret ↓ DB_HOST=localhost DB_DATABASE=quickstart DB_USERNAME=root DB_PASSWORD=1234
$ php artisan migrate を実行するとマイグレーションが実行され、データベースにテーブルが作成された http://localhost/~test/laravel/quickstart/public/ にアクセスすると無事に動作した タスクを管理できた ■クイックスタート 中級者向けタスクリスト https://readouble.com/laravel/5.1/ja/quickstart-intermediate.html C:\localhost\home\test\public_html\laravel にLaravel本体をインストールするために、以下を実行する(Laravelがインストールされ、プロジェクトの雛形も作成される) composer create-project laravel/laravel quickstart2 --prefer-dist 以下のURLでアクセスできる http://localhost/~test/laravel/quickstart2/public/ https://github.com/laravel/quickstart-intermediate を C:\localhost\home\test\public_html\laravel\quickstart2 に上書きで配置。以降 quickstart で作業するものとする 以降も「初心者向けタスクリスト」と同じ要領で進められる 「quickstart」は「quickstart2」に読み替えて進める アクセスは以下のURLから行う(loginを付ける) ユーザ登録後、タスクを管理できる http://localhost/~test/laravel/quickstart2/public/login