20190816のlaravelに関する記事は9件です。

laravelのcookieで一日血迷ったこと#解決したので備忘録(*_*;

laravel Cookieの落とし穴?

  • laravelで今話題のLeague Of Legendsのtftのビルドサイトを作ろうとしていて

    Cookieで閲覧回数を記録したいなーと思って
    色々やってみたけど、なかなかCookieが保存されず...

    ※home_cookieが今回保存したいcookie名です

chromeのデベロップツールのApplicationタグには保存されていないが
2019081601.png

chromeのデベロップツールのNetworkタグには保存されている!?!?!?!??!?!?:cold_sweat:
2019081602.png

  

  • 原因はphp artisan serveだった! xammpで起動してみると 2019081603.png

保存されている!!!

無事解決!!

勉強になりました...:sob:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel環境を3分で構築する【ついでにDocker入門】

うわあああああ!Laravelが使いたいよおおおお!
だけどサーバー構築面倒くさいよおおおお!!!!

生きてるとそんなときもありますよね。
大丈夫、3分あればできます。

実はもう別の方がQiitaに投稿されているのですが、Docker入門にもちょうどいいので少しだけ多めに解説した記事になります。
Docker で Laravel 環境 を 3分くらいで作る

対象読者

  • Dockerなんて(ほとんど)わかんない人
  • でもとにかく一刻も早くLaravelを使いたい人

上記記事より

これだけです。

DBMSは MariaDB となっているようです。

https://github.com/bitnami/bitnami-docker-laravel

$ mkdir ~/myapp && cd ~/myapp
$ curl -LO https://raw.githubusercontent.com/bitnami/bitnami-docker-laravel/master/docker-compose.yml
$ docker-compose up

解説するよ

何をしているのかを堅苦しくいうと、
「WordPressのローカル構築用ソフトを開発しているBitnamiが公式に提供しているDockerイメージをdocker-composeで一気に準備して起動している」
といったところでしょうか。

素晴らしいことに、この3行だけで本当に仮想Docker環境を手元に用意することができてしまいます。
こちらを少しだけ詳細に解説してみましょう。
※Dockerのインストールだけは先に済ませておいてください

1行目はいいですよね。
適当なフォルダを作って移動するだけです。
次にcurlコマンドでdocker-compose.ymlファイルを入手します。

なんだそれは、と思われた方もいるかもしれません。
中身を見てみましょう。

docker-compose.yml
version: '2'

services:
  mariadb:
    image: 'bitnami/mariadb:10.1'
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
      - MARIADB_USER=my_user
      - MARIADB_DATABASE=my_database
      - MARIADB_PASSWORD=my_password

  myapp:
    tty: true
    image: bitnami/laravel:5-debian-9
    environment:
      - DB_HOST=mariadb
      - DB_USERNAME=my_user
      - DB_DATABASE=my_database
      - DB_PASSWORD=my_password
    depends_on:
      - mariadb
    ports:
      - 3000:3000
    volumes:
      - ./:/app
    # privileged: true # Privileged mode could be required to run this container under Windows

docker-composeは複数のコンテナを一元管理するためのツールです。
詳しくはこちら。
DockerComposeの基本

簡単に言うと、こちらのymlファイルでは"mariadb" と "myapp" 、それぞれの仮想環境(=Dockerコンテナ)を2つ手元に起動しますよ! という起点が定義されています。
"mariadb" がDBサーバ、"myapp" がwebサーバ兼APサーバですね。Laravelに触れてるとこの言葉自体もなかなか聞かなくなりますが……

さて、3行目の

$ docker-compose up

を入力するとお察しの通り念願のDockerコンテナが起動するわけですが、少しお待ちを。
先にこの超便利ツールをインストールしちゃいましょう。
(すでに3分超えてると思いますが、そこは触れないでください)

DockerとDocker ComposeのTerminal UI「lazydocker」のご紹介

これはDockerにあまり触れる機会がない方でもパッと使える素晴らしいツールです。

起動したらこんな感じの画面になります。
スクリーンショット 2019-08-16 20.23.19.png

さて、この状態で例の

$ docker-compose up

を実行してみましょう。
実行!

こうなります。
スクリーンショット 2019-08-16 20.32.04.png

なんか2つrunningの状態になっていますね。
これが先程docker-compose.ymlファイルで確認した2つのコンテナです。
もうこの状態で2つのサーバがローカルで動いているような状態になっています。

このサーバを停止、再起動したりするのもlazydocker上から簡単にできるので、先の記事を参考にしながらぜひやってみてください。

また、「本当にサーバとして起動している」のを実際に中に入って確認してみたい場合、

$ docker exec -it myapp_myapp_1 /bin/bash

で入ってみることができます。
この操作もlazydocker上からできるのですが、macで利用する場合上下ボタンが効かなくなるんですよね……

閑話休題。

さて、いよいよ http://localhost:3000/ にアクセスしてみましょう。
スクリーンショット 2019-08-16 20.36.21.png

Hello, Laravel!
簡単ですね。

実際のソースコードは先程作った"myapp"ディレクトリに展開されています。
ローカルのコードを仮想環境が共有して使っているイメージですね。なので、ローカルのコードをちゃちゃっといじればブラウザ側のLaravelの動作に即座に反映されます。

この方法で構築すれば、デバッグも可能です。
こちらの記事で完璧にまとめてくださっているので、ぜひお読みください!
Dockerで構築したPHP環境をxdebugでデバッグ(vscode)

今回はこのあたりで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel collectionの比較(イコール)

Collection関数を使って、比較を行う(イコール)

$oldCollection = collect([1]);
$newCollection = collect([1]);
$diffCollection = $oldCollection->diff($newCollection);

if($diffCollection->isEmpty() && $oldCollection->count() === $newCollection->count()){
    dd('hello');
}

これでCollectionのイコール比較完了

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでログインしてそのままそのURLに進む方法

Laravelでログイン後に元の画面に戻る方法は探せばそれなりに出てくるけど、
ログイン後に行こうとしてたURLにそのまま行く方法がなかなか見つからなかったので、自己解決した結果を書いておきます。

考え方としてはログイン画面を開く直前に、行こうとしてるURLを残しておいて、ログイン後そこに戻る、という手法を取ります。

さて、未ログインのままログインが必要な画面に行こうとするときにはAuthenticateミドルウェアを通るため、ここでログイン画面へ行こうとするときにURLを保存します。
このとき、URLはセッションに保存しておきます。

app/Http/Middleware/Authenticate.php
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            session(['url.intended' => url()->current()]);
            return route('login');
        }
    }

そしてLoginControllerのredirectPath()でセッションを見て、
保存されていたURLにリダイレクトすることで、もともと行こうとしてたURLに行けます。

app/Http/Controllers/Auth/LoginController.php
    public function redirectPath()
    {
        return session('url.intended') ?: $this->redirectTo;
    }

    public function showLoginForm()
    {
        if (empty(session('url.intended'))) {
            session(['url.intended' => url()->previous()]);
        }
        return view('auth.login');
    }

ちなみにこのLoginControllerでは、「ログイン後に元の画面に戻る」もやりたいので、
showLoginForm()で、セッションが空の場合にリファラーを指定してます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AmazonLinux2/Laravel】Laravelで認証機能作成

Laravelでは、標準で会員登録やログインなどの必要最低限の機能が一通り揃っています。

実際に作成する場合も、いくつかのコマンドを叩くだけなので便利なのですが、いつも自分で実装していると、簡単にできてしまいすぎて、何が起きているのかわからないと気持ち悪いですよね。
なので、実際にどんなことをやっているのかを確認していきたいと思います。

認証用のデータベースの準備

ここでは、RDSでAurora MySQLが、すでに準備されていることとします。

AmazonLinux2では、標準の yum や、amazon-linux-extras では、MySQLクライアントがインストールできないので、公式のリポジトリを有効化してMySQL5.7クライアントのインストールを行います。

$ sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
$ sudo yum-config-manager --disable mysql80-community
$ sudo yum-config-manager --enable mysql57-community
$ sudo yum install mysql-community-client

Mysqlクライアントをインストールしたら、RDSに接続して、データベースを作成します。データベース名は任意ですが、一旦 tools という名称で進めます。

mysql> create database tools;

Laravelのデータベース用の設定ファイルを変更します。
Laravelでは、標準の文字コードがutf8mb4となっています。
utf8mb4は、絵文字などの4バイト文字にも対応した文字コードになりますが、テーブル作成時に、エラーが出てしまう場合は、utf8に変更しましょう。
問題なければ、そのままでも構いません。
MySQL5.7.7未満のバージョンの場合に、767bytes制限を超えた、emailカラムの作成時にエラーになります。

文字コードをutf8へ変更

config/database.php
//'charset' => 'utf8mb4',
'charset' => 'utf8',
//'collation' => 'utf8mb4_unicode_ci',
'collation' => 'utf8_unicode_ci',

認証用のファイル生成

認証用の機能を追加するために、下記のコマンドを実行します。
実行後、必要なファイルが生成されます。

$ php artisan make:auth

設定ファイルにデータベース情報をセットします。
.env(.env.development)設定

.env
APP_NAME=Tools
APP_ENV=development
APP_KEY=base64:******************************************
APP_DEBUG= true
APP_URL=http://ドメイン

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=(RDSのエンドポイント)
DB_PORT=3306
DB_DATABASE=tools
DB_USERNAME=(ユーザー名)
DB_PASSWORD=(パスワード)

:
:

認証用のテーブル作成

$ APP_ENV=development php artisan migrate
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

usersテーブルと、password_resetsテーブル、さらに管理テーブルとして、migrationsテーブルの3つが作成されます。

これで認証機能の作成は完了です。

サイトにアクセスすると、右上に、「LOGIN」「REGISTER」のリンクが追加されているかと思います。
実際に、「REGISTER」から会員登録を行い、認証機能が利用できることを確認してみてください。

自動生成の内容の確認

ただ、こんな感じで自動生成すると、実態がよくわからなくて変更したいときや、問題があった際にどうして良いのかわからなくなってしまうので、まずは何が起きたのかを確認していきます。

追加されたファイル

app/Http/Controllers/HomeController.php
resources/views/home.blade.php
resources/views/auth/login.blade.php
resources/views/auth/passwords/email.blade.php
resources/views/auth/passwords/reset.blade.php
resources/views/auth/register.blade.php
resources/views/auth/verify.blade.php
resources/views/layouts/app.blade.php

変更されたファイル

routes/web.php

ルーティングについて

routes/web.phpに、下記の2行が追加されています。

routes/web.php
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');

Route::get('/home', 'HomeController@index')->name('home');
の方は、Laravelのルーティングについて理解している方であれば、 /home でリクエストされた際に、HomeControllerのindexメソッドが実行されるということでわかりやすいですね。

ただ、
Auth::routes();
は、まとめられすぎてちょっとわかりにくいですね。

ということで、あえて、Auth::routes(); は使わずに、同じ動作を実現してみようかと思います。

routes/web.php
//Auth::routes();

Route::namespace('Auth')->group(function(){
    Route::get('login','LoginController@showLoginForm')->name('login');
    Route::post('login','LoginController@login');
    Route::post('logout','LoginController@logout')->name('logout');

    Route::prefix('password')->name('password.')->group(function(){
        Route::post('email','ForgotPasswordController@sendResetLinkEmail')->name('email');
        Route::get('reset','ForgotPasswordController@showLinkRequestForm')->name('request');
        Route::post('reset','ResetPasswordController@reset')->name('update');
        Route::get('reset/{token}','ResetPasswordController@showResetForm')->name('reset');
    });
    Route::get('register','RegisterController@showRegistrationForm')->name('register');
    Route::post('register','RegisterController@register');

});

Route::get('/home', 'HomeController@index')->name('home');

上記が実際に、Auth::routes(); で設定されているルーティングになります。
通常は、Auth::routes();を利用して問題ありませんが、URLなど必要に応じてカスタマイズする必要がある際には、上記を変更してあげることで実現可能になります。

認証機能について

認証機能は、下記のコマンドで必要なファイルが自動生成されました。

$ php artisan make:auth

ただ、実際に生成されたのは、認証機能に必要なViewファイルのみとなります。
ですので、基本的にはLaravelではインストール後にすでに認証機能自体は含まれていることになります。
実際の中身は、
app/Http/Controllers/Auth 以下にControllerがあるので、確認してみてください。

ですので、少しだけ複雑な認証機能の実装が必要な場合でも、コマンドで自動生成するとカスタマイズがしづらいからという理由で、安易に1から認証機能を実装せずに、一旦標準の認証機能を確認して、利用できるところは利用してみるのが良さそうですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel 5.8] トランザクションのネストとカウント

コミット

ネストされたDB::commit()は無視され、最も浅いDB::commit()でコミットされます。

以下の例では、DBにユーザー2人が作成されるのは、t1()のDB::commit()実行時です。
t2()内のDB::commit()では、DBにユーザーは誰も作成されていません。

function t1()
{
    DB::beginTransaction();
    User::create(['id' => 1]);
    t2();
    sleep(10);
    DB::commit(); // ここでコミットされる
}

function t2()
{
    DB::beginTransaction();
    User::create(['id' => 2]);
    DB::commit(); // ここではコミットされない
}

t1();

sleep(10)実行中に、DBでselect * from users;することで、コミットされていないことを確認できます。

Laravelはトランザクションのカウントを取っている

トランザクションのカウント($this->transactions)を取り、そのカウントが1の時のみコミットを実行しているようです。
カウントはトランザクション開始するごとに増え、コミットするごとに減っています。

vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php
public function beginTransaction()
{
    $this->createTransaction();

    $this->transactions++;

    $this->fireConnectionEvent('beganTransaction');
}

public function commit()
{
    if ($this->transactions == 1) {
        $this->getPdo()->commit();
    }

    $this->transactions = max(0, $this->transactions - 1);

    $this->fireConnectionEvent('committed');
}

ちなみに2回目以降のトランザクション開始はセーブポイント作成になるようです。
参考:https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Concerns/ManagesTransactions.php

DB::commit()し忘れると

トランザクションのカウントはDB::beginTransaction()するごとに増えていきますので、1つ忘れると全てのトランザクションがコミットされません。
シンプルな例ですが、以下はDBにユーザーは誰も作成されません。

DB::beginTransaction();
User::create(['id' => 1]);
// DB::commit(); // コミット忘れた

DB::beginTransaction();
User::create(['id' => 2]);
DB::commit();

ロールバック

DB::rollBack()した時もトランザクションのカウントが1つ減ります。また2回目以降のDB::beginTransaction()はセーブポイント作成になります。
よって以下の例は、id1のユーザーのみ作成されます。

DB::beginTransaction();
User::create(['id' => 1]);

DB::beginTransaction();
User::create(['id' => 2]);

DB::rollBack();
DB::commit();

以下の例では、id1のユーザーのみDBに作成されます。
t2()で例外がスローされていますが握りつぶしているため、t2()はロールバック、t1()はコミットという結果になっています。

function t1()
{
    try {
        DB::beginTransaction();
        User::create(['id' => 1]);
        t2();
        DB::commit();
    } catch (Exception $e) {
        DB::rollBack();
    }
}

function t2()
{
    try {
        DB::beginTransaction();
        User::create(['id' => 2]);
        throw new Exception; // 例外発生
        DB::commit();
    } catch (Exception $e) {
        DB::rollBack();
        // ここでthrow $eせず、例外を握りつぶしている
    }
}

t1();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@section ディレクティブの閉じ方

@sectionとは

Laravel 5.8 Bladeテンプレートによると、

@sectionディレクティブは名前が示す通りにコンテンツのセクションを定義し、(中略)します。

とのこと。しかし、そのディレクティブの閉じ方は複数ある模様。Bladeでコンテンツの挿入を開始するによると、

セクションは常に@sectionで開始されます.
第二引数でコンテンツを指定する場合を除いて、
@sectionは下記の5つの方法のうち、いずれかの方法で終了させなければなりません

@stop セクションへの挿入を終了する.
@endsection @stopのエイリアスです
@show Bladeテンプレートで現在のセクションを取得する.
@append レンダリングを停止してセクションを追加する.
@overwrite コンテンツの挿入を停止してセクションを上書きする.

の5手段があるようだが、それぞれがどのような意味を持つのかを、自分なりにまとめてみた。
なお、下記の例ではこのようなディレクトリ構造を想定して記載する。
resources
    +--views
        +--example
        |   +--index.blade.php ...子テンプレート
        +--layouts
            +--layout.blade.php ... 親テンプレート

@show

@showは、bladeテンプレート内でレンダリングされる。つまり、@showで閉じていない@sectionディレクティブは反映されない。だからこそ、「親テンプレート」や「ベースレイアウト」では@showを使い、「子テンプレート」や「継承レイアウト」では@stop@endsectionを使用するとの説明がされることが多い。


例 show-1

親テンプレートに@showを使用して、子に@parentを使用した場合
おそらく、例中で最も思った結果になる方法だと思う。

親 layout.blade.php
@extends('layouts.layout')
<p>例1</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
@parent
hoge
@endsection

結果

<body>
<p>例1</p>
hoge
</body>

親の@section@showを引き継ぎつつ子の@section@endsectionに置き換えている。


例 show-2

親テンプレートに@showを使用しただけの場合
これは失敗例。@parentを忘れた場合。

親 layout.blade.php
@extends('layouts.layout')
<p>例2</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
hoge
@endsection

結果

<body>
<p>例2</p>
</body>

親の@section@showが子の@section@endsectionに置き換えているため、レンダリングされないまま出力された。


例 show-3

失敗例?その2。
子に@showを、親に@endsection付けた場合

親 layout.blade.php
<p>例3</p>
@section('hoge')
@endsection
子 index.blade.php
@extends('layouts.layout')
@section('hoge')
hoge
@show

結果

<body>
hoge
<p>例3</p>
</body>

推測では、子の@section@showが継承前にレンダリングされ、その後親の@section@endsectionを処理したため、親のレイアウトより優先されて<body>タグの直下に子の@section@showの内容が反映されたのではないかと思う。
(文献がないから推測の域を出ないけど...。)

@stop,@endsection

Laravel 5.8 Bladeテンプレート」によると

@endsectionディレクティブはセクションを定義するだけに対し、(以下略)

とあり、単純に@sectionを閉じるだけの機能しか持たない。使い方としては@showでも述べたように「子テンプレート」や「継承レイアウト」で@endsectionを使用することが多くなる。
@stopは「Bladeでコンテンツの挿入を開始する」によると

@endsection @stopのエイリアスです

とあるように、@endsectionと同じ機能を持つ。

@append

Bladeでコンテンツの挿入を開始する」によると

@append レンダリングを停止してセクションを追加する

とあるが、いまいちピンとこないので「Laravel 5.5 Blade @appendの使い方」を参考にいくつか例をあげつつ使用方法を検証する。


例 append-1

親 layout.blade.php
@extends('layouts.layout')
<p>例1</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
    @parent
    hoge
@endsection

@section('hoge')
    <br/>
    fuga
@append

結果

<body>
<p>例1</p>
hoge<br/>fuga
</body>

つまりは先の@sectionに内容を追記できる。この機能と条件分岐を使用することで表示結果を変えるという使い方ができそう。


例 append-2

親 layout.blade.php
@extends('layouts.layout')
<p>例2</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
    @parent
    メッセージは
@endsection

@if($msg !='')
    @section('hoge')
        <br/>{{$msg}}
    @append
@else
    @section('hoge')
        <br/>ありません!
    @append
@endif

結果

$msgが空のとき

<body>
<p>例1</p>
メッセージは<br/>ありません!
</body>

$msgが'hoge'のとき

<body>
<p>例1</p>
メッセージは<br/>hoge
</body>

@overwrite

Bladeでコンテンツの挿入を開始する」によると

@overwrite コンテンツの挿入を停止してセクションを上書きする.

とある。例をあげて挙動を確認する。

例 overwrite-1

親 layout.blade.php
@extends('layouts.layout')
<p>例1</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
    @parent
    hoge
@endsection

@section('hoge')
    @parent
    <br/>fuga
@overwrite

結果

<body>
<p>例1</p>
    <br/>fuga
</body>

@appendと同様に条件分岐と組み合わせてなんかできそう。


例 overwrite-2

親 layout.blade.php
@extends('layouts.layout')
<p>例2</p>
@section('hoge')
@show
子 index.blade.php
@section('hoge')
    @parent
    メッセージは
@endsection

@if($msg !='')
    @section('hoge')
        <br/>{{$msg}}
    @append
@else
    @section('hoge')
        @parent
        からっぽ!
    @append
@endif

結果

$msgが空のとき

<body>
<p>例1</p>
からっぽ!
</body>

$msgが'hoge'のとき

<body>
<p>例1</p>
メッセージは<br/>hoge
</body>

参考

ありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

名前をつけるときは予約語に注意

今回出てたLaravelの開発で出たエラー。結構ハマったのでメモ。

結論:フォルダ名も予約語使わないこと。

PHP Parse error: Syntax error, unexpected T_INTERFACE, expecting T_STRING or '{' on line 1

初めは全体通して動かしてたけど問題の切り分けのために、tinkerで直指定してファイル呼び出し。

php artisan tinker

>>> use App\Infrastructure\Repositories\Interface\IGreetdictionarysRepository;

PHP Parse error: Syntax error, unexpected T_INTERFACE, expecting T_STRING or '{' on line 1

ファイルの中身を全部消してもエラー出るので、フォルダ名を変えた。
Interfaceって名前が予約語なのでIRepositoriesに変えた。

use App\Infrastructure\Repositories\IRepositories;
use App\Infrastructure\Repositories\IRepositories\IGreetdictionarysRepository;

これで通った。

他の言語でも同じように注意

Pythonの予約語に注意

Pythonの変数名で避けた方がいい名前は?
https://qiita.com/matsui2019/items/98165df7bee980ae6a18

MySqlの予約語に注意

MySqlで予約語を使うと、syntax errorが出る
https://qiita.com/ma_me/items/a98842d1cb65bfcd20cf

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Class env does not exist に悩まされたときに読む書

何が起こったか

image.png

ある日突然、Laravelのすべてのセッション開始時に
Class env does not exist というエラーを吐いて全機能が停止した。

すごいのは、WEBだけでなくコンソールも動かない。
それどころかcomposerもまともに動かない。

直接の原因は、
サービスコンテナに env として登録されているはずのインスタンスが
見つからない、というエラー。

いったい何をやらかしたのだろうか?

類型

Class config does not exist とか、
Class view does not exist とか。

結論

原因1 composerでやらかしましたよね?

ある日突然このような事態になることはほぼありません。
絶対に何かやらかしているはず。
ほら、今インストールしたモジュール、とりあえずそれを除去するか、
個別に対策するか、
composer dump-autoloadで解決することもあるようです。

参考 [小ネタ]LaravelにTelescopeを追加したら、PHP Unitでエラーが出たときの対応

原因2 .envが壊れている

.envファイルを削除して状況が変わるか試しましょう。

原因3 ->enviroment() が早すぎる

アプリケーション内でこのメソッドを使っているところを探して、下記のように修正してください。

BEFORE
if ($this->shouldReport($exception) && app()->environment('prod')) {
AFTER
if ($this->shouldReport($exception) && app()->bound('env') && app()->environment('prod')) {

原因4 まったく関係ない

一番最初に聞きたかったこと。
それは、直接の原因は、サービスコンテナやenvとは全く関係ないことがある、ということ。

Class env does not exist本来のエラーを隠している、全然関係ないエラー。

なのでとりあえず本来のエラーに登場していただきましょう。
こうします。

app\Exceptions\Handler.php
    // このメソッドを追加
    public function render($request, Exception $exception)
    {
        // メソッドがすでにあるなら↓この1行を追加
        dd($exception);
        return parent::render($request, $exception);
    }

image.png

あーー! AWS SDKのインストールに失敗していたのか!!

#今回のケースでは config/aws.php でクラスの参照に失敗していたのが本来のエラーでした。

解説

なぜこんなエラーが出るかというと
「サービスプロバイダーの初期化」などという極めて初期段階では
まだサービスコンテナのインスタンスが用意されていません。

でもエラーハンドラはもっと初期で活動開始するので、
その後起きたエラーは標準のエラーハンドラがキャプチャします。

で、そのエラーハンドラが何をしているかというと

vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\HandleExceptions.php
        // あー、env使ってるわ
        if (! $app->environment('testing')) {
            ini_set('display_errors', 'Off');
        }
vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php
        // そうだよね、view使うよね
        view()->replaceNamespace('errors', [
            resource_path('views/errors'),
            __DIR__.'/views',
        ]);

なので、こういった箇所で env が無いとか、 view がないといったエラーが発生し、
本来のエラーが捨て置かれてしまうようです。

同様に、エラーハンドラ内で ->enviroment()app('xxx') といった感じに
サービスコンテナ系を触っていると、同じ現象が起こります。

エラーハンドラ内で ->enviroment() するときには、app()->bound('env') も併用して、
envが初期化されているか確認、エラーが起こらないようにしましょう。

感想

これだけのことに、2時間以上はまってしまい、穴埋め係根性で勢いのままに執筆させていただきました。

エラーの原因と対策は一概にコレ!とはなかなか言えませんが、その解決の一助になっていただければ幸いです。

こんな記事も書いています

Laravelのちょっとマニアックな視点から、誰も書かない記事を書いています(笑
合わせてご覧いただけると幸いです(^^)

ヘルパ関数とファサード

サービスコンテナ講座

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む