[Laravel] 5.6.19がリリースされました
laravel/frameworkのバージョン5.6.19がリリースされました。更新された機能について確認していきます。
Added
1. SparkPostのエンドポイントが指定可能になりました (#23910)
2. Optionalクラスに__isset()メソッドが追加されました (#24042)
3. メール通知の際にcc、bcc、replyToが複数指定可能になりました (#23760)
詳しい変更については以下を確認してください。
Release v5.6.19 · laravel/framework · GitHub
[Laravel] 5.6.18がリリースされました
laravel/frameworkのバージョン5.6.18がリリースされました。更新された機能について確認していきます。
Added
1. MySQL 8.0に対応しました (#23948)
2. カスタムFileSystemでオリジナルのURLを指定できるようになりました (#23964)
3. PostgreSQLのオペレーターがが追加されました (#23945)
以下のオペレーターが追加されました。
not ilike
is distinct from
is not distinct from
4. PusherのBroadcastにJSONPコールバックが利用できるようになりました (#24018, @b9ab427)
詳しい変更については以下を確認してください。
Releases · laravel/framework · GitHub
[Laravel] 5.6.17がリリースされました
laravel/frameworkのバージョン5.6.17がリリースされました。更新された機能について確認していきます。
Added
1. クエリビルダーのサブクエリを生成するメソッドが追加されました (#23818)
以下のメソッドでサブクエリを生成できます。
joinSub
leftJoinSub
rightJoinSub
DB::table('table')->joinSub('select * from "subtable"', 'sub', ...);
DB::table('table')->joinSub(function ($q) { $q->from('subtable'); }, 'sub', ...);
DB::table('table')->joinSub(DB::table('subtable')->where('foo', 'bar'), 'sub', ...);
詳しい変更については以下を確認してください。
Release v5.6.17 · laravel/framework · GitHub
[Laravel] Optionalヘルパーの便利な使い方
optionalhelperは、Laravel5.5で導入されました。このクラスは、オブジェクトに存在しないプロパティを参照した場合に発生するエラーを回避することができます。
一般的な利用方法は以下のように任意のオブジェクトに対して行います。
ユーザー情報を持つ$userが存在しなかった場合にも、各プロパティへの参照はnullが返却されます。
$user = App\User::find($id);
return optional($user)->name;
また、Laravel5.7ではBladeオペレータのorが削除される予定です。
PHP7で実装された??オペレータを使って同等のことができますが、optionalを使用することで冗長をなくすことができます。
下記の二つのコードは同じ動作を行います。
// null coalesce
return $user->profile->twitter_id ?? null;
// optional
return optional($user->profile)->twitter_id;
そして、Laravel5.5.13ではoptionalメソッドの第二引数にクロージャが渡せるようになりました。
これは第一引数のオブジェクトが存在する場合のみ、クロージャが実行されます。
return optional(User::find($id), function ($user) {
return TwitterApi::findUser($user->twitter_id);
});
optionalをうまく活用することで、より見やすいコーディングが可能になるでしょう。
Using the Laravel Optional Helper and the New Optional Closure - Laravel News
[Laravel] 5.6.15がリリースされました
laravel/frameworkのバージョン5.6.14がリリースされました。更新された機能について確認していきます。
今回はセキュリティアップデートが含まれているので、アップグレードすることをお勧めします。
Security
1. Encrypter::validPayload()メソッドで暗号ivの長さについて評価するようになりました (886d261)
詳しい変更については以下を確認してください。
Release v5.6.15 · laravel/framework · GitHub
[Laravel] 5.6.14がリリースされました
laravel/frameworkのバージョン5.6.14がリリースされました。更新された機能について確認していきます。
Added
1. SlackMessage::info()メソッドが追加されました (#23711)
Slack通知のレベルはデフォルトでinfoでしたが、これを明示的に指定できるようになりました。
2. SessionGuard::logoutOtherDevices()メソッドが追加されました (@9c51e49)
使用しているデバイス以外のユーザーセッション情報を無効にすることができます。この機能を利用するためには\Illuminate\Session\Middleware\AuthenticateSessionミドルウェアを利用している必要があります。
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($password);
詳しい変更については以下を確認してください。
Release v5.6.14 · laravel/framework · GitHub
[Laravel] 5.6.13がリリースされました
laravel/frameworkのバージョン5.6.13がリリースされました。更新された機能について確認していきます。
Added
1. view:cacheコマンドが追加されました (@9fd1273, @2ab8acf)
2. コレクションのmin()、max()メソッドがショートコードによるアクセスが可能になりました (#23560)
以下のようにエイリアスとして利用できます。
$c->min('foo');
$c->max('foo');
// ショートコード
$c->min->foo;
$c->max->foo;
3. Bladeディレクテイブに@elseauth、@elseguestが追加されました (#23569)
認証方法によって分岐ができるようになりました。
@auth('administrator')
@elseauth('standard')
@endauth
4. 暗号化のレベルを設定できるようになりました (#23573, @d6e3ca9)
以下のようにconfig/hashing.phpにてカスタマイズが可能になりました。
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => 10,
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 1024,
'threads' => 2,
'time' => 2,
],
];
5. タグ付けされたキャッシュキーのincrementとdecrementが可能になりました (#23578)
6. SeeInOrderのテストが追加されました (#23594, @ca39449)
7. コレクションのgroupBy()メソッドがショートコードによるアクセスが可能になりました (#23608)
8. created_atの更新を無効化出来るようになりました (#23667)
CREATED_AT、UPDATED_AT共にnullを指定することで日付の挿入をスキップ可能になりました。
class Model extends Eloquent
{
public const CREATED_AT = null;
public const UPDATED_AT = null;
}
9. optional()ヘルパーにコールバックが渡せるようになりました (#23688)
10. Eloquent\Collection::loadMorph()メソッドが追加されました (#23626)
morphリレーションのレコードについてもEager Loadingが可能になりました。
$activities = ActivityFeed::with('parentable')
->get()
->loadMorph('parentable', [
Event::class => 'calendar',
Photo::class => 'tags',
Post::class => ['author', 'commentsCount'],
]);
詳しい変更については以下を確認してください。
Release v5.6.13 · laravel/framework · GitHub
[Laravel] EloquentモデルでJSON型のカラムを扱う方法
MySQL 5.7ではカラム型にJSONを扱えるようになりました。もちろんLaravelのEloquentも対応しており、簡単に読み取りや検索を行うことができます。
実践的なアプリケーションでMySQLのJSON型を扱う方法について検証してみましょう。
テーブルの作成
JSON型を扱うテーブルのModelと、migrationファイルを作成します。
$ php artisan make:model Archive -m
今回はarchivesというテーブルにユーザー情報を持つmetaカラムがあるとします。
// database/migrations/2018_03_28_013327_create_archives_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateArchivesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('archives', function (Blueprint $table) {
$table->increments('id');
$table->json('meta');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('archives');
}
}
JSON型に対応していないバージョンの場合はTEXT型で作成しましょう。
Eloquentモデルのcastsプロパティに値を指定することで、データベースから取得した値を指定したフォーマットへ変更することができるようになります。
int (integer)
real (float, double)
string
bool (boolean)
object
array (json)
collection
date
datetime (custom_datetime)
timestamp
metaカラムをJSONへキャストするように設定しておきましょう。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Archive extends Model
{
protected $guarded = ['*'];
protected $casts = ['meta' => 'json'];
}
Factory (ダミーデータ) の作成
データの登録を簡単にするために、Archiveに対してのFactoryを作成しておきましょう。
$ php artisan make:factory ArchiveFactory
metaカラムに対して、名前、フリガナ、住所、メールアドレスが登録されるものとします。
Fakerを利用してダミーデータを登録できるようにします。
※ config/app.phpに'faker_locale' => 'ja_JP'を追加することで日本語に対応したダミーデータの作成が可能になります。
<?php
use Faker\Generator as Faker;
/** @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\Archive::class, function (Faker $faker) {
return [
'meta' => [
'name' => $faker->name,
'kana' => $faker->kanaName,
'address' => $faker->address,
'email' => $faker->email
]
];
});
Factoryが作成できたらtinkerを使ってダミーデータを登録しておきましょう。
$ php artisan tinker
Psy Shell v0.8.17 (PHP 7.1.10 — cli) by Justin Hileman
>>> factory(App\Archive::class, 100)->create();
コントローラー&Viewファイルの作成
登録されたArchive情報を取得するためのコントローラーを作成します。
$ php artisan make:controller ArchiveController
対応するルーティングも追加しておきましょう。
// routes/web.php
Route::get('archive', 'ArchiveController');
Archive情報の一覧表示に加えて、metaカラムのJSON情報を検索できるようにしてみましょう。
以下のようなクエリを発行することで、JSON型のカラムに対しての条件検索ができます。
select * from `archives` where `meta`->'$."name"' like '%鈴木%'
これをEloquentのORMで利用するには以下のように記述します。
Archive::where('meta->name', 'like', '%鈴木%');
Illuminate\Database\Query\Grammars\MySqlGrammar::wrapJsonSelectorにより、->に対してラップ処理が行われ、%s->'$.%s'に置換されてJSON型の検索が可能になります。
/**
* Wrap the given JSON selector.
*
* @param string $value
* @return string
*/
protected function wrapJsonSelector($value)
{
$path = explode('->', $value);
$field = $this->wrapValue(array_shift($path));
return sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
return '"'.$part.'"';
})->implode('.'));
}
Archiveテーブルの検索条件に使うパラメーターが?q=name:田中,address=東京都の用に送られてくるとして、ここから必要な条件のトリミングを行いkey:valueをもつコレクションを作成します。
以上をふまえて以下のようなコントローラーを作成します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use App\Archive;
class ArchiveController extends Controller
{
protected $metaKeys = ['name', 'kana', 'address', 'email'];
public function __invoke(Request $request)
{
$searchMetas = array_reduce(explode(',', $request->q), function($meta, $q) {
$key = str_before($q, ':');
$value = str_after($q, ':');
if (in_array($key, $this->metaKeys) && filled($value)) {
$meta->put($key, $value);
}
return $meta;
}, new Collection);
$model = Archive::query();
foreach ($this->metaKeys as $metaKey) {
if ($searchMetas->has($metaKey)) {
$model->where('meta->'.$metaKey, 'like', '%'.$searchMetas->get($metaKey).'%');
}
}
$archives = $model->get();
return view('archive', compact('archives', 'searchMetas'));
}
}
resources/viewsへarchive.blade.phpを作成します。
Laravelに同封されているBootstrapを利用して以下の用に作成します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>{{ config('app.name') }}</title>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<div class="container my-5">
<form class="form-row" action="{{ URL::current() }}">
<div class="col">
<div class="form-group mx-sm-3">
<input type="text" class="form-control" name="q" value="{{ Request::get('q') }}" placeholder="key1:value1,key2:value2...">
</div>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Search</button>
</div>
</form>
</div>
<div class="container my-5">
<div class="list-group">
@foreach ($archives as $archive)
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="mt-2">
<dl class="row">
<dt class="col-sm-2">名前</dt>
<dd class="col-sm-10 meta-name">{{ $archive->meta['name'] }}</dd>
<dt class="col-sm-2">フリガナ</dt>
<dd class="col-sm-10 meta-kana">{{ $archive->meta['kana'] }}</dd>
<dt class="col-sm-2">住所</dt>
<dd class="col-sm-10 meta-address">{{ $archive->meta['address'] }}</dd>
<dt class="col-sm-2">メールアドレス</dt>
<dd class="col-sm-10 meta-email">{{ $archive->meta['email'] }}</dd>
</dl>
</div>
</a>
@endforeach
</div>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
/archiveへアクセスすると一覧が表示されます。
検索フォームにname:田と入力して検索していみます。
meta情報のnameに田が入っている情報が出力されました。
(番外編) 検索にヒットした文字列をハイライトする
Laravelとは関係ありませんが、検索した文字列をハイライトさせることで、より視覚的に判断することができます。
Javascriptのmark.jsを利用してマーキングを行います。
js/app.jsを読み込んでいる下に以下のコードを追加します。
<script src="{{ asset('js/app.js') }}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js"></script>
<script>
@foreach ($searchMetas as $key => $value)
$(".meta-{{ $key }}").mark('{{ $value }}');
@endforeach
</script>
検索したキーワードがハイライトされます。
[Laravel] ユーザーのアクセスログを保存する
アプリケーションへアクセスしたユーザーのアクセスログは何かと役に立つので取っておきたいと思う人が多いと思います。
Laravelには認証系のイベントが用意されており、ログインしているユーザーからのリクエストがあった場合にIlluminate\Auth\Events\Authenticatedイベントが発火されます。このイベントを購読することで、アクセスログの取得が可能です。
保存先には色々考えられますが、オンラインのログサービスのRollbarへ保存する方法を紹介します。Rollbar本来の利用方法とは異なるかもしれませんが、とても利便性の高いログ情報を取得することができます。
データベース等、保存先を変更する場合でもリスナーを変更すれば同じような動作が可能です。
パッケージの導入
Composerでxzxzyzyz/laravel-logging-rollbarをインストールします。
パッケージの詳細についてはGitHub - xzxzyzyz/laravel-logging-rollbar: Rollbar for Laravel Loggingを参照してください。
$ composer require xzxzyzyz/laravel-logging-rollbar
Rollbarの準備
Rollbarのアカウントをお持ちでない場合は以下からサインアップしてください。無料プランも用意されており、5,000件/月まで利用できます。
Rollbar
create new projectで任意のプロジェクトを作成しましょう。
作成が完了するとプロジェクトへのアクセストークンが発行されます。
Laravel側で利用するのはserver-side access tokenなので、この情報を.envファイルへ追記します。
LOG_ROLLBAR_TOKEN=your post_server_item token
config/logging.phpへRollbarのchannelを追加することで、Rollbarへの
通知をLaravelのログとして利用できるようになります。
// channelsへ追加
'channels' => [
...
'rollbar' => [
'driver' => 'custom',
'access_token' => env('LOG_ROLLBAR_TOKEN'),
'via' => Xzxzyzyz\Laravel\Logging\Rollbar\RollbarLogger::class
],
],
アクセスログを保存するリスナーの作成
以下の用なIlluminate\Auth\Events\Authenticatedイベントのリスナーを作成しましょう。
今回はログのラベルとしてアクセスのあったURLを利用します。
<?php
namespace App\Listeners;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Auth\Events\Authenticated;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
class AccessLogToRollbarListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle(Authenticated $event)
{
Log::channel('rollbar')->info(URL::current());
}
}
作成したリスナーをEventServiceProviderへ登録します。
// app/Providers/EventServiceProvider.php
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Auth\Events\Authenticated' => [
'App\Listeners\AccessLogToRollbarListener',
],
];
以上でアクセスログをRollbarへ保存する準備ができました。
次に実際にログを送信してみます。
アクセスログの確認
migrationを実行してユーザー情報の入れ物を用意しておきます。
$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
認証周りを有効にしておきます。
$ php artisan make:auth
Authentication scaffolding generated successfully.
/registerへアクセスしてユーザー登録を行うのと同じですが、tinkerを利用してユーザーを作成します。
$ php artisan tinker
Psy Shell v0.8.17 (PHP 7.1.10 — cli) by Justin Hileman
>>> factory(App\User::class)->create();
=> App\User {#790
name: "Sally Stiedemann",
email: "[email protected]",
updated_at: "2018-03-23 02:21:32",
created_at: "2018-03-23 02:21:32",
id: 1,
}
>>>
/loginへアクセスして作成したユーザーでログインした後、Rollbarへアクセスしてログを確認してみましょう。
Peopleでユーザー毎のログを確認できます。
Rollbarではユーザーのリクエスト値やサーバー情報、SESSION情報、利用ブラウザ等の様々なデータが格納でき、柔軟な検索が可能です。
とても便利なサービスですので、この機会に是非使ってみましょう。
[Laravel] ログの保存先にオンラインのRoolbarを使う
Laravelのログは通常ローカルの/storage/logsディレクトリへ保存されますが、オンライン上のエラーモニタリングサービスのRollbarへ保存してみましょう。
Error Tracking & Crash Reporting for Software Developers - Rollbar
Laravelのバージョン5.6で、Log周りに関する大きなアップデートがありました。
Loggingにchannel形式が採用され、一つ、あるいは複数の場所へログを保存できるようになりました。
デフォルトで便利なドライバが複数用意されていますが、オリジナルのchannnelを作成してRollbarへログを送信します。
公式のパッケージ
Rollbarでは公式に以下のLaravel用のパッケージが用意されています。
GitHub - rollbar/rollbar-php-laravel: Rollbar error monitoring integration for Laravel projects
しかしこれは5.6に対応したロギングドライバではなく、単に今までのログシステムの拡張として、全てのログをRollbarへ送信する実装です。
src/RollbarServiceProvider.php のbootメソッドを見てみましょう。
/**
* Bootstrap the application events.
*/
public function boot()
{
// Don't boot rollbar if it is not configured.
if ($this->stop() === true) {
return;
}
$app = $this->app;
// Listen to log messages.
$app['log']->listen(function () use ($app) {
$args = func_get_args();
// Laravel 5.4 returns a MessageLogged instance only
if (count($args) == 1) {
$level = $args[0]->level;
$message = $args[0]->message;
$context = $args[0]->context;
} else {
$level = $args[0];
$message = $args[1];
$context = $args[2];
}
$app['Rollbar\Laravel\RollbarLogHandler']->log($level, $message, $context);
});
}
ログイベントのリスナーとして、Rollbarへのログ送信を登録しています。
Auto-Discoveryに対応しているので、composerでパッケージの追加を行った時点でこのリスナーが追加されることになります。5.6でも利用可能ですが、二重にログが保存されるでしょう。
カスタムドライバの実装
前述のサービスプロバイダを参考にして、Laravel5.6のログchannelとして実装します。
channelの登録には、Monolog\Loggerインスタンスを返すクラスを実装するだけです。
インスタンスの作成にはchannel名と、Monolog\Handlerが必要ですが。
公式パッケージより、Rollbar\Laravel\RollbarLogHandlerが利用できるので、これをMonolog\Handler\PsrHandlerとしてハンドラへ登録します。
// RollbarLoggerクラス
public function __invoke(array $config)
{
$defaults = [
'environment' => app()->environment(),
'root' => base_path(),
];
$config = array_merge($defaults, $config);
if (empty($config['access_token'])) {
throw new \InvalidArgumentException('Rollbar access token not configured');
}
if (empty($config['person_fn'])) {
$config['person_fn'] = function() {
return $this->person();
};
}
$rollbar = new \Rollbar\Rollbar;
$rollbar->init($config);
$logger = new \Rollbar\Laravel\RollbarLogHandler($rollbar->logger(), app());
$handler = new \Monolog\Handler\PsrHandler($logger);
return new \Monolog\Logger('rollbar', [$handler]);
}
あとはconfing/logging.phpへchannelとして登録するだけでRollbarへのログが送信されます。
全ての実装はGitHubで公開していますので利用してみてください。