- 投稿日:2020-03-26T22:10:42+09:00
Laravel Paginate
ページネーションメソッドを使ってページャの実装
HogeController.phpuse App\Models\Hoge; #Hogeモデルがある前提 $hoges = Hoge::paginate(5); return view('hoge.index', ['hoges' => $hoges]);style.scss.page ul li{ display: inline; }hoge/index.blade.php@foreach($hoges as $hoge) <tr> <td>{{ $hoge->id }}</td> <td>{{ $hoge->name }}</td> <td>{{ $hoge->email }}</td> <td>{{ $hoge->age }}</td> </tr> @endforeach <div class="page"> {{ $hoges->links() }} </div>1.コントローラでpaginateメソッドを使って表示件数を指定
2.viewでlinksメソッドでページャを表示orderByを使ってソート
#上記の例にorderByメソッドを追加するだけ Hoge::orderBy('age', 'desc')->paginate(5);参考URL
- 投稿日:2020-03-26T21:13:27+09:00
laravelでseedを使っていてIntegrity constraint violation:1452エラーが表示された時
表題の件、今日だけで2回つまづいたので自分のためにも残しておきます!
laravelのseedを使っていて下記のエラーが出てしまった時は、外部キー元のテーブルに当該キーのデータがあるか確認しておきましょう。
無いとエラーになります。
灯台下暗しでした汗SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails
- 投稿日:2020-03-26T19:23:49+09:00
テスト中にエラーが発生
こんなエラーが発生
vendorのadobeFontMetrics.phpの中でこれがダメだと$tree = $kern["tree"];結果的にlaravelのvendorのエラーだったぽい。
上記を削除して、下記を追加すれば良い。$tree = is_array($kern) ? $kern["tree"] : null;
- 投稿日:2020-03-26T18:11:31+09:00
Laravel7でのRoute Model Bindingの新機能を使ってみた
Route Model Bindingとは
自分は暗黙の結合やルートモデル結合と良く言っています
今までのLaravelでのルートモデル結合ではid
を指定する感じでした
下記のような感じですルートでこのように定義して
web.php
Route::get('/user/{user}', 'UserController@index');
コントローラーでこんな感じ
UserController.phppublic function index(User $user) { // }これでブラウザから
/user/1
などど叩くとUserモデルのidが1のモデルと勝手に結合してくれるものでした
これかLaravel7では新しくid以外のものを指定してできるようになってましたルートモデル結合について詳しくはこちらをご覧ください→こちら
id以外のフィールドを指定する
id以外のフィールドを指定してルートモデル結合するにはこんな感じで書きます
web.phpRoute::get('/user/{user:name}', 'UserController@index');これで
/user/{任意の名前}
でURLを叩くことで
その{任意の名前}
と一致するUserが結合して所得することができるようになっています実際にサンプルを作ってみました
サンプルを作成する
まずはモデル、コントローラー、シーダ、マイグレーションを作成します
$ php artisan make:model Person --all次にコントローラーですがリソースコントローラーが作成されていますが今回は使わず
別でコントローラーを作成します$ php artisan make:controller PersonControllerシーダファイルを編集します
PersonSeeder.php<?php use Illuminate\Database\Seeder; class PersonSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $param = [ 'name' => '太郎' ]; DB::table('people')->insert($param); $param = [ 'name' => '花子' ]; DB::table('people')->insert($param); $param = [ 'name' => '次郎' ]; DB::table('people')->insert($param); } }マイグレーションの編集をします
create_people_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreatePeopleTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('people', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('people'); } }マイグレーション実行とシーダの実行をします
$ php artisan migrate$ php artisan db:seedこれでDBとダミーデータの用意ができました
次にbladeファイルを作成します
resources
にmain
フォルダを作成してindex.blade.php
を作成してください
index.blade.php
を下記のように編集しておきますindex.blade.php<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>ユーザー表示</title> </head> <body> {{ $person->name }} </body> </html>
web.php
を編集します
下記を追加web.phpRoute::get('/{person:name}', 'MainController@index');コントローラーを変更します
MainController.php
に下記のアクションを追加してくださいMainController.phppublic function index(Person $person) { return view('main.index', compact('person')); }完了です
これでブラウザから
/太郎
としてみてください
ブラウザに太郎
と表示されていると思います今回はnameフィールドでバインドするように指定しているので
Personモデルに一致するnameをURLに指定してアクセスすると
一致したPersonの情報が所得できています今までのように
id
のみでなく
ルート定義の際にあんなに簡単にフィールドを指定してルートモデル結合できるのは
とても便利ですね色々な使い方ができるなと思いました
使い方考えながら遊んでみようと思います
- 投稿日:2020-03-26T16:31:05+09:00
phpunitテスト
- 投稿日:2020-03-26T16:30:23+09:00
Laravel7でのCors対応について
Corsとは
ブラウザでは同一でのドメイン間でのアクセスしかできないようになっています
そのため、異なるドメインへのアクセス(APIなど)と通信することができないのですが
それを解決するために使用するのがCorsです例えば、Laravel側で
localhost:8000
でNuxt側でlocalhost:3000
で動かしていた場合
localhost:3000
のNuxt側からaxiosなどを使ってlocalhost:8000
のLaravel側のAPIにアクセスができません
それをクロスドメインアクセスを可能にする事ができますLarvel7でのCors
ここからが本題何ですが、Cors対応しようと思い
今まで通りcomposer
でインストールしようと思いconfig
フォルダをみてみると
あれ?cors.phpがある!
という事に今頃気付きました。今までやってた作業しなくてもLaravel7では必要ない?みたいですね
app/Http/Kernel.php
を見てみても$middleware
に\Fruitcake\Cors\HandleCors::class
の記載もありましたcors.phpの中身をみてみる
cors.php<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | */ 'paths' => ['api/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => false, 'max_age' => false, 'supports_credentials' => false, ];apiからのアクセスは全て許可する設定になってるようで
これは今後LaravelではCors対応を手動でする必要がなくなったってことですかね?
とても助かりますね。。
と言うただ自分の中で発見があっただけの記事ですついでに
cors.php
の設定項目について自分のわかっている範囲で説明入れておきますcors.phpの設定項目
cors.php
の各設定項目について少し触れておきますcors.php<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | */ 'paths' => ['api/*'], //許可するパスの設定 'allowed_methods' => ['*'], //許可するメソッドの設定 'allowed_origins' => ['*'], //許可するドメインの設定 'allowed_origins_patterns' => [], //許可するドメインのパターン 'allowed_headers' => ['*'], //許可するヘッダーの設定 'exposed_headers' => false, //レスポンスヘッダーの公開指定 'max_age' => false, //ブラウザのキャッシュの保管期間 'supports_credentials' => false, //クッキーなどの認証の許可 ];
[*]
とすると全て許可することになります
少し不安点あるので間違っている点あれば、ご指摘頂きたいです。
- 投稿日:2020-03-26T16:17:52+09:00
LaravelのFormRequestの単体テストのやり方
ただバリデーションテストを書きたいだけだった
こんな感じでValidator取得すればいけるやろ〜
$formRequest = new FormRequest(); $v = $formRequest->getValidator(); $v->passes();そこでValidatorを取得できそうなメソッドを探したが、すべて
protected
メソッドだったのであった...こんなふうに書きました
public function test_FormRequest_validate() { $data = []; $formRequest = new FormRequest(); $v = \Validator::make( $data, $this->app->call([$formRequest, 'rules']), $formRequest->messages(), $formRequest->attributes() ); $this->assertTrue($v->passes()); }もとのFormRequestクラスの内部では↓こんなふうにValidatorを取得しています。
$factory->make( $this->validationData(), $this->container->call([$this, 'rules']), $this->messages(), $this->attributes() );
app->call()
とすることで、rules()
でメソッドインジェクションをしていた場合でもDIしてくれます。
- 投稿日:2020-03-26T15:43:41+09:00
LaravelのFormRequestでバリデートまでのソースコードを読む
FormRequestでvalidateされるまで
FormRequestクラスの誕生
Illuminate/Routing/ControllerDispatcher.phppublic function dispatch(Route $route, $controller, $method) { $parameters = $this->resolveClassMethodDependencies( $route->parametersWithoutNulls(), $controller, $method ); //... }
resolveClassMethodDependencies()
が怪しいので見ていきます。Illuminate/Routing/RouteDependencyResolverTrait.phpprotected function resolveClassMethodDependencies(array $parameters, $instance, $method) { //... return $this->resolveMethodDependencies( $parameters, new ReflectionMethod($instance, $method) ); }
resolveMethodDependencies()
の返り値をreturnしているようです。ならば見ていきましょう。
Illuminate/Routing/RouteDependencyResolverTrait.phppublic function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector) { $instanceCount = 0; $values = array_values($parameters); foreach ($reflector->getParameters() as $key => $parameter) { $instance = $this->transformDependency( $parameter, $parameters ); if (! is_null($instance)) { $instanceCount++; $this->spliceIntoParameters($parameters, $key, $instance); } elseif (! isset($values[$key - $instanceCount]) && $parameter->isDefaultValueAvailable()) { $this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue()); } } return $parameters; }まず
parameters
には基本的にルートモデルバインディングで解決されたものが入っています。$parameters = ['user' => Userのインスタンス]foreachのkeyとparameterの部分は、
0 => ReflectionParameterクラス
となります。
transforDependency()
にReflectionParameterクラスと$parameters
を渡しています。
今回は$parameters
には何も入っていない体でいきます。Illuminate/Routing/RouteDependencyResolverTrait.phpprotected function transformDependency(ReflectionParameter $parameter, $parameters) { $class = $parameter->getClass(); if ($class && ! $this->alreadyInParameters($class->name, $parameters)) { return $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : $this->container->make($class->name); } }
$parameter->getClass()
でメソッドにタイプヒンティングしてあるクラス名を取得しています。
stringやintの場合はnullになります。
alreadyInParameters
はその名の通り、すでにルートモデルバインディングで解決されたやつはtrueを返すやで〜ってやつなので今回は関係ないです。return文のところは、デフォルト引数だとその値を使って、そうじゃなければクラス名からインスタンスを生成します。
(ここでFormRequestのインスタンスが生成されます)そのままなんやかんやあってコントローラーメソッドに引き渡されていきます。
解決時の処理
FormRequestのインスタンスを生成するだけでバリデーションしてないじゃん!と思ったそこのあなた!
Laravelの魔法がここから始まります。
まずFormRequestはコンテナで解決されているので解決方法を見ていきましょう。そうサービスプロバイダーです。
Illuminate/Foundation/Providers/FormRequestServiceProvider.phppublic function boot() { $this->app->afterResolving(ValidatesWhenResolved::class, function ($resolved) { $resolved->validateResolved(); }); $this->app->resolving(FormRequest::class, function ($request, $app) { $request = FormRequest::createFrom($app['request'], $request); $request->setContainer($app)->setRedirector($app->make(Redirector::class)); }); }
resolving
,afterResolving
は第一引数に渡されたクラスがコンテナで解決されたときに発行されるイベントをもとに実行されます。さきほど見たようにFormRequestが生成された時点で
resolving
で登録したコールバックが呼ばれます。ここの
resolving
はFormRequestにコンテナとリダイレクタとリクエストをセットしているぐらいです。FormRequestは
ValidatesWhenResolved
インタフェースを実装しているので、afterResolving
のコールバックも呼ばれます。ここで
FormRequest#validateResolved
が呼ばれます。このメソッドはトレイとに実装されています。Illuminate/Validation/ValidatesWhenResolvedTrait.phppublic function validateResolved() { $this->prepareForValidation(); if (! $this->passesAuthorization()) { $this->failedAuthorization(); } $instance = $this->getValidatorInstance(); if ($instance->fails()) { $this->failedValidation($instance); } $this->passedValidation(); }
prepareForValidation()
はデフォルトではなにもしないメソッドになってます。
バリデーションの前になにかしたいことがあれば、このメソッドをオーバーライドすればよさそうです。
getValidatorInstance()
でバリデータを取得していそうです。Illuminate/Foundation/Http/FormRequest.phpprotected function getValidatorInstance() { //... $factory = $this->container->make(ValidationFactory::class); if (method_exists($this, 'validator')) { $validator = $this->container->call([$this, 'validator'], compact('factory')); } else { $validator = $this->createDefaultValidator($factory); } //... $this->setValidator($validator); return $this->validator; }FormRequestには
validator()
がないので、$this->createDefaultValidator($factory)
が呼ばれます。Illuminate/Foundation/Http/FormRequest.phpprotected function createDefaultValidator(ValidationFactory $factory) { return $factory->make( $this->validationData(), $this->container->call([$this, 'rules']), $this->messages(), $this->attributes() ); }これで
Illuminate\Contracts\Validation\Validator.php
を作成して、プロパティにセットしつつreturnします。Illuminate/Validation/ValidatesWhenResolvedTrait.phppublic function validateResolved() { $this->prepareForValidation(); if (! $this->passesAuthorization()) { $this->failedAuthorization(); } $instance = $this->getValidatorInstance(); if ($instance->fails()) { $this->failedValidation($instance); } $this->passedValidation(); }
$instance->fails()
でバリデーションを行って、バリデーションに引っかかればエラーが投げられます。
$this->passedValidation()
はなにもしないメソッドなので、バリデーション後にしたい処理をオーバーライドして記述すればよいでしょう。
- 投稿日:2020-03-26T15:04:43+09:00
LaravelでTDDを始めよう
LaravelでのUnitTestを始めよう
TestUnitを始める上で勢いで始めないように設計しましょう。
・基本的に読むべきこと
https://phpunit.readthedocs.io/ja/latest/
https://readouble.com/laravel/5.5/ja/testing.htmlアンチパターンとベストプラクティス
https://www.ryuzee.com/contents/blog/3982
テストの中で何もテストしていない
書きかけの場合は、テストに未完成の印をつけるなどしましょう
https://phpunit.readthedocs.io/ja/latest/incomplete-and-skipped-tests.html1つのテストメソッドの中で色々テストし過ぎている
引数に真にしたい条件を与えてなんでもassertTrueでチェックしている
@dependsアノテーションを使ってテストの実行順序を制御しつつ分割を行うべきです。テストデータのパターンが複数あるのを理由に1つのテストの中で順番に検証する
引数は検証対象の値やオブジェクトであるべきで、テストの意図を伝わりやすくすべきです。テストが分類されていない。適切なフォルダ構成になっていない
解決策は適切にフォルダに分けて配置したり、テストスイートをつくったり、@groupアノテーションを使ってテストを分類します。LaravelでPhpUnitを使う
LaravelでPHPUnitを使用する際、デフォルトの状態であればtests/TestCase.phpが作成されており、全てのテストクラスはこれを継承してテストを行います。参考:https://memorandumrail.com/laravel-phpunit/
LaravelではデフォルでhFeatureとUnitというディレクトリに分かれていて、Feature には機能テスト、Unit にはユニットテストを書く構成になっています。
関数単位、クラス単位の場合はUnitMockeryを使うと疑似モックを作ってくれるので便利らしい。
- 投稿日:2020-03-26T13:22:24+09:00
Laravelでイベントとリスナーを使ってみる
イベントとは
プログラムから何らかの操作があった際などに処理を実行する仕組です
イベントを発行することでイベントクラスを呼び出すことができ
イベント自体に処理はなく、イベントでは主に値を扱いその値をリスナーへと受け渡しますリスナーとは
発生したイベントを受け取りイベントの発生を監視しているのがリスナー
リスナーで実際に処理が行われ
リスナーはイベント毎に作成し登録する必要がありますイベントを利用する方法
イベントを利用するには、イベントクラスとそれに対応するリスナークラスを作成すし登録する必要があり
そして、必要に応じてイベントを発行すればそのイベントを受け取るイベントリスナーの処理が実行される仕組みになりますイベントとリスナーを作成する
イベントクラスとイベントリスナークラスを作成するには
providers
のEventServiceProvider
を利用します
EventServiceProvider.php
には$listen
とboot()
メソッドが用意されています
イベントクラスとイベントリスナークラスの作成には$listen
を使用しますEventServiceProvider.phpprotected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], 'App\Events\ItemEvent' => [ 'App\Listeners\ItemEventLister' ] ];今回はこのように記述しておきます
次に、下記コマンドを実行します$ php artisan event:generateこのコマンドの実行により、
$linten
に登録したイベントクラスとイベントリスナークラスが自動的に作成されます
イベントクラスはapp/Events
ファオルだに作成され
イベントリスナークラスはapp\Listners
フォルダに作成されますそれでは実際に作成された2つのファイルの構成を見ていきます
イベントクラスの構成
ItemEvent
クラスは下記のようになっていますItemEvent.php<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class ItemEvent { use Dispatchable, InteractsWithSockets, SerializesModels; public function __construct() { // } public function broadcastOn() { return new PrivateChannel('channel-name'); } }イベントクラス内には様々なトレイトが用意されているのと
またメソッドは__construct()
とbroadcastOn()
の二つのメソッドが用意されていますが
基本的には、イベントは値を扱うのがメインになりますので__construct()
以外はほとんと使用しません
忘れてはいけないのは、イベントクラスは値を扱うのが仕事なので処理などは必要ないです次に、イベントリスナークラスの構成を見てみます
イベントリスナークラスの構成
ItemEventLister
クラスは下記のようになっていますItemEventLister.phpuse App\Events\ItemEvent; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class ItemEventLister { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param ItemEvent $event * @return void */ public function handle(ItemEvent $event) { // } }イベントリスナークラスには
handle()
メソッドと__construct()
の二つが用意されています
リスナーは処理をする役割があるので、処理を記述するのがhandle()
メソッドになります
引数にはイベントクラスのインスタンスが渡されており、この引数からイベントで管理していた値にアクセスすることができます
また、このhandle()
メソッドが、イベント発生時に呼び出されます次にイベントを発行するにはどうすれば良いのかです
イベントの発行
イベントを発行するのは簡単で下記のようにすることでイベントを発行できます
event(new ItemEvent(値));ここまでで、イベントクラスとイベントリスナークラスの作成とイベントの発行方法など基本を説明しました
次にこれからを使って実際にイベント発行から値を受け取り、リスナーで処理を実行してみるサンプルを作成してみますサンプル機能の作成
前での説明で使用した際に作成した
ItemEvent
クラスとItemEventLister
クラスを引き続き使っていきます
まずは今回のサンプルで使用するItem
モデル、シーダ、マイグレーション、コントローラーを作成していきます$ php artisan make:model Item --allこれでモデル、シーダ、マイグレーション、コントローラーが作成できました
最初にマイグレーションの記述を行います
下記のようにしてくださいcreate_items_talbe.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateItemsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('items', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('items'); } }マイグレーション実行します
$ php artisan migrate次にシーダファイルを下記のように修正
ItemSeeder.php<?php use Illuminate\Database\Seeder; class ItemSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $param = [ 'name' => 'りんご' ]; DB::table('items')->insert($param); $param = [ 'name' => 'ばなな' ]; DB::table('items')->insert($param); $param = [ 'name' => 'ぶどう' ]; DB::table('items')->insert($param); } }シーダを実行します
$ php artisan db:seedこれでitemsテーブルに3件おダミーデータが用意できたかと思います
次にコントローラーを作成します
ここでイベントを使用します
ItemController
が作成されていると思いますがリソースコントローラーなので今回は使わずに新たに作成します$ php artisan make:controller MainController
MainController
を修正していきますMainController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Item; //Itemモデルをuse use App\Events\ItemEvent; //ItemEventクラスをuse class MainController extends Controller { public function index() { return view('main.index'); } public function item(Item $item) { event(new ItemEvent($item)); //イベント発行 return redirect()->route('index'); } }上記のコントローラーの
item()
アクションでイベントを発行する処理をしています次にルートの定義をしていきます
web.php<?php use Illuminate\Support\Facades\Route; Route::get('/item}', 'MainController@item')->name('item'); Route::get('/', 'MainController@index')->name('index');2つ追加できました
次に
resources
にmain
フォルダを作成しindex.blade.php
を作成してください
中身はこんな感じにしておきますindex.blade.php<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>ユーザー表示</title> </head> <body> イベントとリスナー </body> </html>ここまでで必要な準備が終わりです
イベントも発行してるのであとはそのイベントの処理を用意するだけになります
ItemEvent
を修正していきますItemEvent.php<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class ItemEvent { use Dispatchable, InteractsWithSockets, SerializesModels; public $item; public function __construct($item) { $this->item = $item; } public function broadcastOn() { return new PrivateChannel('channel-name'); } }
public $item
としてItemのインスタンスを保管しておくプロパティを用意しています
__construct
で引数で渡された$itemを利用してます
この引数の$item
はMainController
のitem()
アクションでイベント発行してる際に引数で渡されている値です
この部分ですevent(new ItemEvent($item))
ここで受け取ったItemの情報をイベントに渡してイベント側でそれを値として扱っています次にイベントリスナークラスの修正をします
ItemEventLister.php<?php namespace App\Listeners; use App\Events\ItemEvent; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Storage; class ItemEventLister { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param ItemEvent $event * @return void */ public function handle(ItemEvent $event) { Storage::append('item_accesslog.txt', '[Item]' . now() . '' . $event->item->name); } }簡単に説明すると、
item_accesslog.txt
というファイルに[item]+ 現在の時間 + ユーザーの名前
の形で
書き出すということを処理しているリスナーになります
ちなみに、item_accesslog.txt
ファイルは作ってませんがそのファイルがなければ自動的にstorage/app
に作成されますこれでサンプルの実装は完了です
ブラウザから/1
とURLを叩いてみてください
そうすると、storage/app/item_accesslog.txt
に[Item]現在の時間 りんご
と書き出しされているかと思います一連の流れを簡単に説明すると
- URLで指定された番号のItemの情報が
MainController
のitem()
アクションの引数で渡されるitem()
アクションの中のイベント発行の際にその値を渡してあげる- その値を受け取ったイベントクラスである
ItemEvent
がその値を__construct
を使って保管している- そのイベント発行により呼び出されたイベントリスナー
ItemEventLister
のhandle()
メソッドが処理を実行してファイルに書き出しを行っているこんな感じです
これが一連のイベント作成からイベント発行しリスナーに処理をさせるまでになりますまたイベントをキューに登録して実行させることもできます
キューを利用する
発行したイベントをキューテーブルに登録し処理したい場合は
イベントリスナークラスにてインターフェースを実装するだけで利用ができます
今回の場合だとItemEventLister
クラスになりますnamespace App\Listner; class ItemEventLister implements ShouldQueue { //省略 }
- 投稿日:2020-03-26T12:32:18+09:00
aws-sdk-php-laravelを利用してファイルアップロード
aws-sdk-php-laravelとは
AWS SDK for PHPをlaravelのために使いやすくしたもの
(そもそもAWS SDKとはawsのサービスを使いやすくするソフトウェア開発キットのこと)
インストール
aws-sdk-php-laravelをインストールする
composer.jsonに書く
compsoerを通じてインストールします
composer.json{ "require": { "aws/aws-sdk-php-laravel": "~3.0" } }composer update
conposer updateを実行する
$ composer updateconfig/app.php
config/app.phpにAWS Service Providerを追加します
'providers'
と下の方にある'aliases'
に下のように追加してくださいconfig/app.php'providers' => array( // ... Aws\Laravel\AwsServiceProvider::class, ) 'aliases' => array( // ... 'AWS' => Aws\Laravel\AwsFacade::class, )設定
環境変数
デフォルトでは、これらの環境変数が使われます。
.envAWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION (default = us-east-1)環境変数の設定方法はこれをみてもらったら早いと思います。
https://qiita.com/tiwu_official/items/ecb115a92ebfebf6a92fAWS_DEFAULT_REGION名前はAWS_REGIONに変更します。
aws.config
.envで設定した環境変数はこんな感じで使われてるよーって
aws.configreturn [ 'credentials' => [ 'key' => env('AWS_ACCESS_KEY_ID', ''), 'secret' => env('AWS_SECRET_ACCESS_KEY', ''), ], 'region' => env('AWS_REGION', 'us-east-1'), 'version' => 'latest', // You can override settings for specific services 'Ses' => [ 'region' => 'us-east-1', ], ];使い方
画像をアップロード
$s3 = App::make('aws')->createClient('s3'); $s3->putObject(array( 'Bucket' => 'YOUR_BUCKET', #s3で登録したbucketの名前 'Key' => 'YOUR_OBJECT_KEY', #登録したいファイルの名前 'SourceFile' => '/the/path/to/the/file/you/are/uploading.ext', #登録したいファイル ));これで画像をアップロードすることができる。
画像を表示
$s3 = App::make('aws')->createClient('s3'); $key = 'YOUR_OBJECT_KEY'; #取得したいファイルの名前 $bucket = env('AWS_BUCKET'); #bucketの名前 $image = $s3->getObjectUrl($bucket, $key); #getObjectUrlでurlを取得これでviewに表示できます。
参考
公式のgithub(ほぼこれの日本語訳です)
https://github.com/aws/aws-sdk-php-laravel/blob/master/README.md超簡単!LaravelでS3を利用する手順
https://qiita.com/tiwu_official/items/ecb115a92ebfebf6a92f
- 投稿日:2020-03-26T10:10:19+09:00
Mac Laravelのライブラリ「laravel-dompdf」を日本語表示に対応させる
目的
- Laravelにライブラリ「laravel-dompdf」を入れたが、日本語化されない問題があったため改善する
実施環境
- ハードウェア環境
項目 情報 備考 OS macOS Catalina(10.15.3) ハードウェア MacBook Air (11-inch ,2012) プロセッサ 1.7 GHz デュアルコアIntel Core i5 メモリ 8 GB 1600 MHz DDR3 グラフィックス Intel HD Graphics 4000 1536 MB
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Laravel バージョン 7.0.8
- ライブラリ環境
項目 情報 備考 laravel-dompdf v0.8.6 アプリ名ディレクトリに移動後コマンド`composer show -i 実施条件
- 下記の二つの投稿内容に沿ってライブラリが導入できていること
- 下記の内容は実施しなくても良いが一度読んでおくと理解が早いかも知れない。
- ローカル開発環境でブラウザから確認可能なLaravelアプリがあること。
実施方法概要
- Bladeファイルを利用した日本語化対応の方法をまとめる。
- 準備
- フォントのファイルの取得
- ライブラリに登録するためのスクリプト取得
- ライブラリに登録
- 確認
実施方法詳細
準備
下記コマンドを実行してフォントのファイルを設置するディレクトリを作成する
$ mkdir アプリ名ディレクトリ/fontsフォントのファイルの取得
下記リンク先にアクセスする。
リンク先の「TTFファイル」の「4書体パック」のzipをクリックしてフォントをインストールする。
お使いのMacのダウンロードフォルダにダウンロードされたフォントのzipファイル「IPAfontXXXXX.zip」を解凍する。(Xには任意の数字)
下記コマンドを実行して、解凍した「IPAfontXXXXX」の中のファイル郡を先に作成した
アプリ名ディレクトリ/fonts
にコピーする。$ cd アプリ名ディレクトリ $ cp ~/Downloads/IPAfont00303/* fonts/フォントのファイルを設置した
アプリ名ディレクトリ/fonts
直下の状態を下記に記載する。
- ~アプリ名ルートフォルダ/fonts
- IPA_Font_License_Agreement_vX.X.txt
- Readme_IPAfontXXXXX.txt
- ipag.ttf
- ipagp.ttf
- ipam.ttf
- ipamp.ttf
ライブラリに登録するためのスクリプト取得
- 下記のリンク先にアクセスする。
「Clone or download」をクリックする。
「Download ZIP」をクリックする。
お使いのMacのダウンロードフォルダにダウンロードされたzipファイル「utils-master.zip」を解凍する。
下記コマンドを実行して解凍したフォルダ「utils-master」の中の「load_font.php」をアプリ名ディレクトリ直下にコピーする。
$ cd アプリ名ディレクトリ $ cp ~/Downloads/utils-master/load_font.php .アプリ名ディレクトリ直下に「load_font.php」があることを確認する。
ライブラリに登録
下記コマンドを実行する。(筆者環境での下記コマンドの実行後の出力を付録に記載する。)
$ php load_font.php ipag fonts/ipag.ttf下記コマンドをアプリ名ディレクトリで実行してフォントの設定ファイルを確認する。
$ cd アプリ名ディレクトリ $ vi vendor/dompdf/dompdf/lib/fonts/dompdf_font_family_cache.php先のコマンドを実行して開いたファイルに下記の記載がある事を確認する。
vendor/dompdf/dompdf/lib/fonts/dompdf_font_family_cache.php'ipag' => array( 'normal' => $fontDir . '/ipag', 'bold' => $fontDir . '/ipag', 'italic' => $fontDir . '/ipag', 'bold_italic' => $fontDir . '/ipag', ),下記コマンドを実行してスクリプトファイルにより作成されたライブラリが読むためのフォントデータをコピーする。
$ cd アプリ名ディレクトリ $ cp -r vendor/dompdf/dompdf/lib/fonts storage/確認
下記コマンドを実行して
アプリ名ディレクトリ/resource/view
に「test」フォルダを作成する。$ cd アプリ名ディレクトリ $ mkdir resources/views/test下記コマンドを実行してTestコントローラを作成する。
$ cd アプリ名ディレクトリ $ php artisan make:controller TestController >Controller created successfully.下記コマンドを実行して先に作成したコントローラを開く。
$ cd アプリ名ディレクトリ $ vi app/Http/Controllers/TestController.php先のコマンドで開いたコントローラをの内容を下記の様に修正する。
アプリ名ディレクトリ/app/Http/Controllers/TestController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use PDF; class TestController extends Controller { public function japanese_pdf_test() { $pdf = PDF::loadView('test.japanese_pdf_test'); return $pdf->stream(); } }下記の記載を
アプリ名ディレクトリ/routes
にあるweb.phpのルーティングファイルに追記する。アプリ名ディレクトリ/routes/web.phpRoute::get('/japanese_pdf_test', 'TestController@japanese_pdf_test');下記コマンドを実行してビューファイルを作成し開く。
$ cd アプリ名ディレクトリ $ vi resources/views/test/japanese_pdf_test.blade.php開いたビューファイルに下記の内容を記載する。
アプリ名ディレクトリ/resources/views/test/japanese_pdf_test.blade.php<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>PDF</title> <style> body { font-family: ipag; } </style> </head> <body> <h1>こんにちは</h1> </div> </body> </html>下記コマンドを実行してローカルサーバを起動する。
$ cd アプリ名ディレクトリ $ php artisan serve下記のリンク先にアクセスしブラウザから文字化けせずに「こんにちは」とPDFとして出力されることを確認する。
付録
コマンド
php load_font.php ipag storage/fonts/ipag.ttf
を実行した際の筆者の環境でのターミナル出力を下記に記載する。$ php load_font.php ipag fonts/ipag.ttf >Unable to find bold face file. >Unable to find italic face file. >Unable to find bold_italic face file. >Copying fonts/ipag.ttf to /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/dompdf/dompdf/lib/fonts/ipag.ttf... >Generating Adobe Font Metrics for /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/dompdf/dompdf/lib/fonts/ipag... >PHP Notice: Trying to access array offset on value of type null in /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php on line 142 >PHP Stack trace: >PHP 1. {main}() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:0 >PHP 2. install_font_family() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:201 >PHP 3. FontLib\TrueType\File->saveAdobeFontMetrics() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:155 >PHP 4. FontLib\AdobeFontMetrics->write() /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php:361 > >Notice: Trying to access array offset on value of type null in /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php on line 142 > >Call Stack: > 0.0086 423096 1. {main}() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:0 > 0.0484 4914016 2. install_font_family() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:201 > 0.0858 5068992 3. FontLib\TrueType\File->saveAdobeFontMetrics() /Users/shun/workspace/study/laravel/calculation_drill_app/load_font.php:155 > 0.0862 5103120 4. FontLib\AdobeFontMetrics->write() /Users/shun/workspace/study/laravel/calculation_drill_app/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php:361参考文献
- 投稿日:2020-03-26T08:16:01+09:00
Laravel7+Reactの環境を構築する
Laravel7 で React を利用した開発を可能にする手順をご紹介いたします。
ここでは Laravel と React はインストール済みとして進めていきます。開発環境
- Laravel 7.2.1
- React 16.13.1
- PHP 7.4.3
Laravel で React を利用できるようにする
React を利用する Laravel のプロジェクトを作成し、
作成されたプロジェクトフォルダに遷移します。composer create-project laravel/laravel hoge-project cd hoge-project次に Laravel が React を利用するように設定を変更します。
ui artisan コマンドを使用するので、laravel/ui パッケージを先にインストールしています。composer require laravel/ui php artisan ui react最後に下記コマンドを実行すると、Laravel で React を使えるようになります。
npm install && npm run devLaravel の動作確認をする
Laravel が正常に動作するか確認します。
php artisan servehttp://127.0.0.1:8000 にアクセスして、Laravel のページが表示されたら正常に動作しています。
js ファイルの変更を常時反映させるようにしておく
現状のままだと js ファイルの変更が検知されないので、常時変更を検知してくれるように設定します。
別のターミナルを起動し、Laravel のプロジェクトフォルダ内でnpm run watch
を実行します。cd hoge-project npm run watchReact のサンプルファイルで文字列を表示する
React を利用して画面上に文字列を表示できるよう、既存ファイルを変更していきます。
まずは React を使用しているサンプルファイル(resources\js\components\Example.js)を見てみましょう。resources\js\components\Example.jsimport React from 'react'; import ReactDOM from 'react-dom'; function Example() { return ( // 省略 ); } export default Example; if (document.getElementById('example')) { ReactDOM.render(<Example />, document.getElementById('example')); }id が "example" の HTML タグに文字列を表示する処理だとわかります。
それでは Example.js が出力する内容を表示する HTML タグを記述しましょう。
http://127.0.0.1:8000 にアクセスすると表示される内容は welcome.blade.php(resources\views\welcome.blade.php)に記述されているので、
下記のように編集します。resouces/views/welcome.blade.php<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel</title> </head> <body> <div id="example"></div> <script src="{{ mix('js/app.js') }}"></script> </body> </html>body タグ内に、React 出力データの挿入場所である
<div id="example"></div>
と、
Example.js を処理するファイルapp.js
の読み込みを記述します。http://127.0.0.1:8000 にアクセスし、下記の通りに表示されていれば成功です。
もし Example.js 以外のファイル、例えば Hoge.js を読みこませたい場合は、
app.js(resources\js\app.js)を下記のように編集すると良いでしょう。\resources\js\app.jsrequire('./bootstrap'); require('./components/Example'); // 下記を追記 require('./components/Hoge');以上です。
- 投稿日:2020-03-26T01:26:55+09:00
【Laravel】テストのsetUp()でエラー「Declaration of…must be compatible with...」
状況
UserTest.phpclass UserTest extends TestCase { public function setUp() { //略 } }laravelのphpunitのsetUp()メソッドを上記のように書き、テストを行ったところ
次のようなエラーが出ました。PHP Fatal error: Declaration of Tests\Feature\UserTest::setUp() must be compatible with Illuminate\Foundation\Testing\TestCase::setUp(): void in /var/www/tests/Feature/Usertest.php on line 13エラー内容とその対処
要はテストのsetUp()とIlluminate\Foundation\Testing\TestCase
のsetUp()の返り値が違うということを言われているわけです。そこでIlluminate\Foundation\Testing\TestCaseクラスを見てみます。
TestCase.phpprotected function setUp(): void { //略 }Illuminate\Foundation\Testing\TestCaseクラスではsetUp()に
void
が返り値として宣言されています。したがってテストのほうにも
void
のタイプヒンディングをしましょう。UserTest.phpclass UserTest extends TestCase { public function setUp() :void { //略 } }これで完了。
setUp()が使えるようになると思います。
- 投稿日:2020-03-26T00:44:01+09:00
Laravel-admin で アバターを非表示にする
やりたいこと
Laravel-adminをいれるとデフォルトで管理者ユーザーにアバターが振られていて
しかも、管理者編集から画像を消そうにもデフォルトでセットされるうざいので消します
やりかた
下記のように管理者ユーザーテーブルのアバターはnullを許可されているので問題有りません
Schema::create(config('admin.database.users_table'), function (Blueprint $table) { $table->increments('id'); $table->string('username', 190)->unique(); $table->string('password', 60); $table->string('name'); $table->string('avatar')->nullable(); $table->string('remember_token', 100)->nullable(); $table->timestamps(); });次に、
Administrator
という管理者のモデルをみると
下記のようにavatar
のアクセサが定義されているのですが
そこでnullだとlaravel-admin側で用意した画像を使うようになっていますvendor/encore/laravel-admin/src/Auth/Database/Administrator.php/** * Get avatar attribute. * * @param string $avatar * * @return string */ public function getAvatarAttribute($avatar) { if (url()->isValidUrl($avatar)) { return $avatar; } $disk = config('admin.upload.disk'); if ($avatar && array_key_exists($disk, config('filesystems.disks'))) { return Storage::disk(config('admin.upload.disk'))->url($avatar); } $default = config('admin.default_avatar') ?: '/vendor/laravel-admin/AdminLTE/dist/img/user2-160x160.jpg'; return admin_asset($default); }なのでこいつを編集してやります
app/Admin/Models/Administrator.php
を作成
中身をコピーし、下記のように書き換えます。また、一つgetPlainAvatarAttribute
というアクセサを追加してやります。public function getPlainAvatarAttribute() { return $this->getOriginal("avatar");; } /** * Get avatar attribute. * * @param string $avatar * * @return string */ public function getAvatarAttribute($avatar) { if (URL::isValidUrl($avatar)) { return $avatar; } $disk = config('admin.upload.disk'); if ($avatar && array_key_exists($disk, config('filesystems.disks'))) { return Storage::disk(config('admin.upload.disk'))->url($avatar); } $default = config('admin.default_avatar') ?: null; return admin_asset($default); }その後
admin.php
の
Encore\Admin\Auth\Database\Administrator::class,
となっているところを
App\Admin\Models\Administrator::class,
に変更しますこれだけだと、画面で画像ありませんマークがでてしまうので
view側もいじります。
vendor/encore/laravel-admin/resources/views
を丸々コピーし
resources/views/laravel-admin
というディレクトリを作成し格納します。
app/Admin/bootstrap.php
に下記の一文を追加し、viewの参照を変更。app/Admin/bootstrap.phpapp('view')->prependNamespace('admin', resource_path('views/laravel-admin'));sidebarとheaderの修正をします
@if(Admin::user()->plainAvatar) <img src="{{ Admin::user()->avatar }}" class="img-circle" alt="User Image"> @endifこれで、きえました