メモ > 技術 > フレームワーク: Laravel > 実装メモ
実装メモ
■APP_KEYの設定
他環境で実行する場合、GitからCLONEやPULLしたあとに以下の操作で APP_KEY の生成が必要
もしくは、キーを .env.example に含めるという手段もありそう
$ cp .env.example .env
$ composer install --no-dev
$ php artisan key:generate
複数台環境の場合、Web1で上記手順を実行して、
Web2では、上記作業によって .env に記録された APP_KEY の値を手動で設定
…とすると良さそう
(データの暗号化などに使われるようなので、すべてのサーバで同じ値に統一しておく)
Laravelについてのメモ - Qiita
https://qiita.com/reflet/items/76ce4d7743c7d9a8a963
[Laravel5.5] APP_KEY の行方を追う - Qiita
https://qiita.com/yk2220s/items/dcbf54c6d1f33a0cb06f
[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...
初めてのフレームワーク-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
ただし定番の方法は言いづらい
以下などを参考に自作する方が無難かも
PHPで列挙型(enum)を作る - Qiita
https://qiita.com/Hiraku/items/71e385b56dcaa37629fe
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
セッションをRedisに保存する場合、以下が参考になる
Laravel 5.3でセッション管理にredisを使用する方法 - Qiita
https://qiita.com/LowSE01/items/5f8a9c9b09948a6403dc
AWS EC2にredisをインストールする - Qiita
https://qiita.com/stoshiya/items/b8c1d2eb41770f92ffcf
404ページなどでセッションが使えない場合、以下が参考になる
($middleware に StartSession::class を追加する)
Laravel5 例外ハンドラで指定した遷移先ページでセッションを使う方法 - Qiita
https://qiita.com/papyrus9/items/c7394346ca509d016e70
■メールアドレスとパスワードではなく、ユーザ名とパスワードで認証する
未検証
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
■キャッシュ
例えば以下のようにすると、Laravelでキャッシュを扱える
<?php
namespace App\Http\Controllers\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TestController extends Controller
{
/**
* テストページ
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function test(Request $request)
{
if (Cache::has('test')) {
$value = Cache::get('test') + 1;
} else {
$value = 1;
}
Cache::put('test', $value, 1);
return 'cache_test test=[' . $value . ']';
}
}
標準ではキャッシュはファイルに保存される
Redisを使用する場合、
$ composer require predis/predis
を実行する
.env
CACHE_DRIVER=file
REDIS_HOST=xxxxx.cache.amazonaws.com
REDIS_PASSWORD=null
REDIS_PORT=6379
キャッシュ 5.5 Laravel
https://readouble.com/laravel/5.5/ja/cache.html
Laravel Recipes日本語版 | クエリー結果をキャッシュする
http://recipes.laravel.jp/recipe/748
Laravel5.5でAWSのElastiCacheでRedisを使用する場合は以下が参考になりそう
Laravelのセッション管理にRedisを指定し、AWSのElastiCacheを利用する - 商売力開発ブログ
https://www.prj-alpha.biz/entry/2018/05/06/230232
【Elasticache】ELB配下の複数インスタンス間でのセッション管理【Laravel例】 - 備忘録の裏のチラシ
https://norikone.hatenablog.com/entry/2016/02/08/%E3%80%90Elasticache%E3%80%91ELB%E9%85%8D%E4%B8%8B%...
■Laravel+Redis の putCache と putManyCache
キャッシュへの保存は putCache を使うが、大量の情報を保存する場合は putManyCache を使うと速度が劇的に向上する可能性がある
以下、速度が向上する理由のメモ
putManyCache は vendor/laravel/framework/src/Illuminate/Cache/RedisStore.php の100行目あたりに処理がある
基本的には配列の内容を put しているだけだが、前後で multi と exec が呼ばれている
/**
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes)
{
$this->connection()->multi();
foreach ($values as $key => $value) {
$this->put($key, $value, $minutes);
}
$this->connection()->exec();
}
multi は vendor/predis/predis/src/Transaction/MultiExec.php の250行目あたりに処理がある
/**
* Finalizes the transaction by executing MULTI on the server.
*
* @return MultiExec
*/
public function multi()
{
if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) {
$this->state->unflag(MultiExecState::CAS);
$this->call('MULTI');
} else {
$this->initialize();
}
return $this;
}
キャッシュへの保存前に MULTI という文字を送っていることが判る
MULTI はRedisに用意されたトランザクション機能で、速度を向上させるために実装されているもの。これによって速度が向上する
ただしRedisのトランザクションは整合性を保つためのものではなく、あくまでもパフォーマンスを向上させるための機能なので注意
詳細は以下を参照
トランザクション - Redis Documentation (Japanese Translation)
https://redis-documentasion-japanese.readthedocs.io/ja/latest/topics/transactions.html
Redisのトランザクションとパフォーマンス | よしやのブログ
https://ameblo.jp/myon53/entry-11797609797.html
■Queue
未検証
キューが正しく動作しなくなったら、以下で再起動すると正常になる…ことが多いらしい
キューを削除しても動き出さない…という場合、SQSの処理中メッセージが無くなってから再起動すれば大丈夫のことがあったらしい
要検証
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
タスクスケジュール 6.x Laravel
https://readouble.com/laravel/6.x/ja/scheduling.html
laravelでバッチ作ってcronで動かしてみた - Qiita
https://qiita.com/ritukiii/items/a70d89fa988b2d9afbc4
Laravelで定期実行(スケジューラー)を設定する方法 - ポッポプログラミング
https://poppotennis.com/posts/laravel-scheduler
【Laravel】 Cron タスクスケジューラの onOneServer() と withoutOverlapping() の違い - Qiita
https://qiita.com/mpyw/items/15d14d920250a3b9eb5a
php artisan make:command Example
を実行すると
app\Console\Commands\Example.php
が作成されるので、これを編集する
php artisan list
コマンドの登録を確認
php artisan example
コマンドを実行
なお /etc/crontab にCronを記載する場合、
* * * * * php /var/www/vhosts/test/artisan schedule:run >> /dev/null 2>&1
ではなく、以下のように実行ユーザとパスも指定する必要があるので注意
(実行ユーザを指定していない場合、日時ログファイルが root 権限で作られて nginx から書き込めずエラーになる可能性がある)
* * * * * nginx /usr/bin/php /var/www/vhosts/test/artisan schedule:run >> /dev/null 2>&1
Laravel6の場合、以下のように「プロジェクトの場所へ移動してから実行」という書き方に変わっている
Laravel6を使うなら、こちらに合わせておく方が無難かも
* * * * * nginx cd /var/www/vhosts/test && php artisan schedule:run >> /dev/null 2>&1
重複起動はwithoutOverlappingで防止できるが、複数台サーバだと防止にならないので注意
Laravel Scheduler のコードリーディング - Qiita
https://qiita.com/mikakane/items/e9d2a70174e8af584db0
Laravelでタスクをスケジュール実行する | scratchpad
https://takayukii.me/20160824836
また、onOneServerで複数台サーバでの重複起動を防止できるが、
デフォルトキャッシュドライバとして、memcached か redis を使用する必要がある
タスクスケジュール 6.x Laravel
https://readouble.com/laravel/6.x/ja/scheduling.html
■関数やクラスのパスを取得
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
\vendor\laravel\framework\src\Illuminate\Foundation\Application.php
コマンドで確認できるが、このファイルにある
const VERSION = '5.4.34';
この部分の数値が表示されている
■SSLの強制
/routes/web.php の最後に以下を追加する
// 本番環境ではSSL用のURLを生成
if (app()->environment('production')) {
URL::forceScheme('https');
}
productionでhttpsにしたい - ド忘れ防止雑記帳
http://dolphin.hatenablog.jp/entry/2015/04/27/010635
ただしこれは「LaravelにSSL用のURLを生成させる」処理なので、SSLにリダイレクトさせたいなら別途処理が必要
ロードバランサーが無い場合、以下で対応できそう(未検証)
Laravel5で.htaccessを使用せず常時SSL化対応する方法 - Qiita
https://qiita.com/qwe001/items/7cd0bcb149b5b5cc0fd7
ロードバランサーがある場合、以下のようにnginxで「$http_x_forwarded_proto」の条件を追加する
# vi /etc/nginx/nginx.conf
Laravel 5 - laravel5.5 httpからhttpsへリダイレクトさせたい|teratail
https://teratail.com/questions/98537
【Nginx】HTTPSへのリダイレクト設定〜rewriteではなくreturnでいこう〜 | ぴぐろぐ
https://pig-log.com/nginx-redirect-return/
■万年カレンダーを表示する
Laravel+Carbonでカレンダーの作り方 - Crieit
https://crieit.net/posts/Laravel-Carbon
/routes/web.php
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name laravel.example.com;
root /var/www/vhosts/laravel/public;
if ($http_x_forwarded_proto = 'http') {
return 301 https://$server_name$request_uri;
}
# service nginx configtest
# service nginx restart
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