20210227のlaravelに関する記事は10件です。

[自分メモ]複数レコードを配列にして一括でattach()

目的

中間テーブルを利用して複数レコードを一気に登録する場合、attach($配列)のような形で登録することができます。
attach()の引数はprimary keyなので、$primaryKeyの配列を作らなければなりません。

やり方

controller.php
$tagIds = array(); 
foreach ($request->tag as $val) {
  $tagIds[] = Tag::select('tag_id')->where('tag_name', $val)
              ->first()->tag_id;
}
$post->tags()->attach($tagIds);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OctoberCMS 管理画面実装テク:テキストフィールドの文字数制限を変更する

OctoberCMSの自作プラグインで管理画面を実装する際のテクニックのお話。

管理画面フォームのtextタイプのフィールドはデフォルトで255文字の制限がある。
これはattributesプロパティを使えば属性を上書きできる。

myfield:
    label: "My Field"
    type: text
    attributes:
        maxlength: '1000'

参考

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

OctoberCMS 管理画面実装テク:フォームに非表示フィールドを作る

OctoberCMSの自作プラグインで管理画面を実装する際のテクニックのお話。

fields.yamlでフィールドを定義して管理画面のフォームを実装する際、<input type="hidden">のようなフィールドが意外と直感的に作れない。

hiddenプロパティでできそうだが、これだとPOSTデータに含まれなくなる。
readOnlydisabledだと変更不可にはなるがページに表示されてしまう。

そこで、containerAttributesプロパティを使用してdisplay: noneを当ててやる。

containerAttributes:
    style: "display: none"

これで、POSTデータに含まれるが表示されないフィールドが実装できる。

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

Laravelの構成概念 第1回 ライフサイクル編

Laravelフレームワークの全体の処理の流れやサービスコンテナ、サービスプロバイダーについて調べたことをまとめます。

シリーズ

  • Laravelの構成概念 第1回 ライフサイクル編
  • Laravelの構成概念 第2回 サービスコンテナ編(近日公開)
  • Laravelの構成概念 第3回 サービスプロバイダ編(近日公開)

調べるきっかけ

こちらのイベントに参加するにあたって、読んでおかないと話についていけないだろうなぁと思って復習がてら読んでいきます。

初心者の方へ

ライフサイクル・サービスコンテナ・サービスプロバイダ周りは始めたばかりの人には難易度が高いですが、Laravelの処理の流れや初期設定などの仕組みを理解できるので概要だけでも把握できると良いです。

環境

  • Laravel 8.5.11

参考

Laravelの構成概念については公式ドキュメントにより詳しい内容がまとまっているので詳細を知りたい方は上記の公式ドキュメントをご覧ください。

用語概要

用語: Laravel ライフサイクル

Laravelが起動して終了するまでのLaravel全体の処理の流れをライフサイクルといいます。
ちなみに入り口はHttpとConsoleの二つあります。

用語: Laravel サービスコンテナ

クラスの依存関係を管理し、依存注入(DI)を実行するための機能です。

用語: Laravel サービスプロバイダ

Laravelアプリケーション全体の起動処理における初期起動処理を行っています。
サービスプロバイダからサービスコンテナへDIの設定(コンテナ結合)を行います。

サービスプロバイダーでは、コンテナ結合以外にもイベントリスナ、フィルター、ルーティングの設定なども行います。

用語: DI(Dependency Injection)とは

Dependency Injectionは依存性の注入となります。
PHPを実行する際に抽象クラス(interface)を具象クラスに差し替えて実行できる。

テストの時は、DBやAPIを実際にアクセスさせないために差し替えたり等テストコードが書きやすくなるメリットがあります。

本題: Laravelライフサイクル

http://localhost/foo/bar 等のHttp経由の場合、public/index.phpから始まります。

php artisan foo:bar 等のConsole経由の場合、artisanから始まります。

それぞれファイルの中身を見てみましょう。
(補足のコメントを入れてます)

public/index.php

Http経由の流れです。

public/index.php
<?php

use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;

// タイマー開始。フレームワークの起動にかかる時間などを測れる
define('LARAVEL_START', microtime(true));

// メンテナンスモードのファイルがあればメンテナンス画面を表示して終了する
// https://github.com/laravel/framework/blob/8.x/src/Illuminate/Foundation/Console/stubs/maintenance-mode.stub
if (file_exists(__DIR__.'/../storage/framework/maintenance.php')) {
    require __DIR__.'/../storage/framework/maintenance.php';
}

// composer dump-autoload で生成されたオートローダーを読み込む
// オートローダーとは外部にあるPHPファイルを自動的に読み込む仕組みです
// https://genkiroid.github.io/2016/07/15/about-composer-autoload
require __DIR__.'/../vendor/autoload.php';

// ここからLaravelのコアシステムが始まります
// Illuminate\Foundation\Application のインスタンスを取得して $app に格納してます
// サービスコンテナを生成してHttpカーネル、Consoleカーネル、例外ハンドラーのインスタンスの生成を行います
// https://github.com/laravel/laravel/blob/8.x/bootstrap/app.php
$app = require_once __DIR__.'/../bootstrap/app.php';

// 上の行で生成したHttpカーネルのインスタンスを取得してます(App\Http\Kernel)
$kernel = $app->make(Kernel::class);

// tapやsendの挙動がどうなっているのか怪しいですが...
// 送信されてきたHttpリクエスト(GET, POSTの中身)をHttpカーネルへ渡してHttpレスポンスを取得してます
$response = tap($kernel->handle(
    $request = Request::capture()
))->send();

// Laravelの終了処理
$kernel->terminate($request, $response);

artisan

Console経由の流れです。

artisan
#!/usr/bin/env php
<?php

// タイマー開始。Httpの時と同じ
define('LARAVEL_START', microtime(true));

// オートローダーの読み込み。Httpの時と同じ
require __DIR__.'/vendor/autoload.php';

// Illuminate\Foundation\Application インスタンスの取得。Httpの時と同じ
$app = require_once __DIR__.'/bootstrap/app.php';

// サービスコンテナからコンソールカーネルのインスタンス(App\Console\Kernel)を取得している
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

// 引数入力インスタンスとコンソール出力インスタンスをコンソールカーネルへ渡して実行する
// 実行結果のステータスを受け取る
$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

// Laravelの終了処理
$kernel->terminate($input, $status);

// $statusを出力して、スクリプトを終了する
// https://www.php.net/manual/ja/function.exit.php
exit($status);

public/index.phpの補足

public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';

bootstrap/app.php

bootstrap/app.php ファイルの補足です。

bootstrap/app.php
<?php

// 新しいLaravelアプリケーションのインスタンスを生成してます
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

// インターフェースに具象クラスをサービスコンテナにバインドしてます

// Httpカーネルのバインド
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

// Consoleカーネルのバインド
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

// 例外ハンドラーのバインド
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

まず、bootstrap/app.php#L29-L32

Illuminate\Foundation\Applicationのインスタンスを生成してます。

Illuminate\Foundation\ApplicationIlluminate\Container\Containerを継承してます。なのでサービスコンテナのインスタンスを作ってると言っても良いですね。

Illuminate\Foundation\Applicationのコンストラクタを見てみましょう。

Illuminate\Foundation\Application
class Application extends Container implements ApplicationContract, CachesConfiguration, CachesRoutes, HttpKernelInterface
{
    public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }

        $this->registerBaseBindings();
        $this->registerBaseServiceProviders();
        $this->registerCoreContainerAliases();
    }

    protected function registerBaseBindings()
    {
        static::setInstance($this);

        $this->instance('app', $this);

        $this->instance(Container::class, $this);
        $this->singleton(Mix::class);

        $this->singleton(PackageManifest::class, function () {
            return new PackageManifest(
                new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
            );
        });
    }

    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));
        $this->register(new LogServiceProvider($this));
        $this->register(new RoutingServiceProvider($this));
    }

    public function registerCoreContainerAliases()
    {
        foreach ([
            'app'                  => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
            'auth'                 => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
            // ... 略
            'view'                 => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
        ] as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }
}

この段階ではLaravelを起動するための最低限のクラスが読み込まれています。
深掘りしていくとキリがないのでここは気になったら詳しくみていけば良いかなと思います。

続いて、

Illuminate\Contracts\Http\Kernelインターフェースをキーとして、Illuminate/Foundation/Http/Kernelクラスを登録してます。

Consoleカーネル、例外ハンドラーも同様に登録してますね。
最後に $app を返して bootstrap/app.php は終了です。

public/index.php
$kernel = $app->make(Kernel::class);

先ほどサービスコンテナに登録した Illuminate\Contracts\Http\Kernelをキー使って、App\Http\Kernelインスタンスを取得します。

App\Http\Kernelのコンストラクタを見てみます。
このファイルはvendor配下ではなく、app/Http/Kernel.phpと配置されてます。
また、Illuminate\Foundation\Httpを継承していて、ここのコンストラクタが実行されます。

Illuminate\Foundation\Http\Kernel
    /**
     * Create a new HTTP kernel instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Routing\Router  $router
     * @return void
     */
    public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;

        $this->syncMiddlewareToRouter();
    }

    protected function syncMiddlewareToRouter()
    {
        $this->router->middlewarePriority = $this->middlewarePriority;

        foreach ($this->middlewareGroups as $key => $middleware) {
            $this->router->middlewareGroup($key, $middleware);
        }

        foreach ($this->routeMiddleware as $key => $middleware) {
            $this->router->aliasMiddleware($key, $middleware);
        }
    }

Httpカーネルクラスの初期処理でミドルウェアの設定が行われてますね。
どんなミドルウェアが登録されているかはApp\Http\Kernelを見るとわかります。

public/index.php
$response = tap($kernel->handle(
    $request = Request::capture()
))->send();

// Laravelの終了処理
$kernel->terminate($request, $response);

$request = Request::capture()

$_GET, $_POST などを元に Illuminate/Http/Requestのインスタンスを作っています。

$kernel->handle(...)
Httpカーネルのhandleメソッドを実行してIlluminate/Http/Requestインスタンスを渡して実行してます。
ルーティングやコントローラを通って、最終的にIlluminate\Http\Responseインスタンスを生成して返します。

$response = tap(...)->send()

Illuminate\Http\Responsesend メソッドを実行してます。
Illuminate\Http\ResponseSymfony\Component\HttpFoundation を継承していて、Symfonyコンポーネントのメソッドを呼び出してます。
ここでは、Httpレスポンスヘッダーやレスポンスボディの出力を行ってます。
つまりはここで最終的なHTMLが出力されてる訳ですね。

tapヘルパーメソッドを使うと、メソッドチェーンで関数を実行しても、返り値はtap関数の第一引数に渡した変数が返ってきます。
つまりは、 Illuminate\Http\Response のインスタンスが $response に入ります。

最後にLaravelの終了部分です。

$kernel->terminate($request, $response);

Illuminate\Foundation\Http\Kernel
    public function terminate($request, $response)
    {
        $this->terminateMiddleware($request, $response);

        $this->app->terminate();
    }

    protected function terminateMiddleware($request, $response)
    {
        $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
            $this->gatherRouteMiddleware($request),
            $this->middleware
        );

        foreach ($middlewares as $middleware) {
            if (! is_string($middleware)) {
                continue;
            }

            [$name] = $this->parseMiddleware($middleware);

            $instance = $this->app->make($name);

            if (method_exists($instance, 'terminate')) {
                $instance->terminate($request, $response);
            }
        }
    }

Laravelの終了時にもミドルウェアを差し込めるようですね。
各種ミドルウェアにterminateメソッドがあればそれを実行して終了処理を行っていきます。

さいごに

今回はLaravel ライフサイクルについて詳しく見ていきました。
改めて調べるとまだまだ知らない部分が発見できてよかったです。

サービスコンテナやサービスプロバイダについてもまとめて紹介しようと思いましたが、
流石に長くなるので別記事に分けて紹介しようと思います。

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

LaravelはRequestを使えと怒られたお話

LaravelではRequestを使おう!

はじめに

先輩「何この\$_POSTって、Requestを使わない理由は何?」
僕「え?りくえすと??\$_POSTと何が違うの??」
先日Laravelを初めて使用してサービスを作った際に先輩からご指摘をいただきました。
\$_POSTではなくRequestを?。。。はぁ?
調べてみるとなるほどな、納得納得。

とまあこんな初心者レベルですが、備忘録として、あわよくば同じ立場の初心者さんのためにも残しておこうかと思います。

開発環境

PHP 7.3.25
Laravel 8.16.1

Requestってなーに?

Laravelで、リクエスト(Request)というのは、ブラウザを通してユーザーから送られる情報をすべて含んでいるオブジェクトのことです。例えば、会員登録のフォームなら、画面でユーザーが入力したEメール、パスワード、名前、住所だけでなく、何のブラウザを使用したか(User Agent)、どのIPから送られたか、どのURLからアクセスしたかなど、また、会員ログイン後の画面なら、会員認証において保存されたクッキーもブラウザを介して、リクエストに含まれます。
引用:ララジャパン

ざっくりいうと、\$_POSTとか\$_GETとか、その他ユーザーが通信を行った情報を全てまるっとゴリっとまとめてくれたものか!

ちなみにこの\$_POSTとか\$_GETとかいうのは、PHPのスーパーグローバル変数の1つで、
これもブラウザからの情報を取得する際に使用できます。
詳細はこちらの記事を参考にするといいかも。

ふむふむ、めちゃくちゃ便利じゃん!

でもなぜRequestを使った方がいいの?

例えばあなたの前に一般電卓と関数電卓があったとします。
四則演算の範疇に収まらない複雑な計算をするときに一般電卓でも出来ないわけではないですが、関数電卓があれば楽ちんに計算出来ますよね?

そうです。
Requestの方が楽ちんなんです!
上記でも記載した通り、Requestには色々な情報がギュッと詰まっています。
ユーザーがブラウザからフォームで送信したデータはもちろん、
よく使うところの送信が行われたURLからユーザーのIPアドレスまでほんと色々。

これは使わない手はないです。

他にも「型」にも違いがあります。

\$_POSTなどのハイパーグローバル変数は基本的に連想配列と文字列で構成されます。
それに比べてRequestはオブジェクト型で構成されます。

さほど影響ないのでは?
果たしてそうでしょうか。。。

取得データの扱いが変わります

まずは下記条件で実験してみましょう。

formデータ:
// user_name→anomeme
// test_data→空欄

<input name="user_name" class="form-control" type="text-input" value="anomeme">
<input name="test_data" class="form-control" type="text-input" value="">

$_POST
array:2 [▼
  "user_name" => "anomeme"
  "test_data" => ""
]

request()->all()
array:2 [▼
  "user_name" => "anomeme"
  "test_data" => null
]

お分かりいただけますでしょうか。
そう、test_dataのvalueが別物に変わってますよね?

空白もnullも一緒じゃないか!と思う人がいるかもしれませんが、これは別物です。
・空白は文字列が0のデータ!
・nullは存在しないよ!
です。

例えば isset() 関数でこれを見てみましょうか。

isset($_POST["test_data"])
→ bool: true

isset(request()->all()["test_data"])
→ bool: false

一緒のデータを使用しているのに結果が変わってしまいましたね?
(is_null() でも判定が変わりますね)

長くなりましたが、requestは便利だけどこういった違いがありますよというお話でした。

終わりに

取れる値については他の人がさらに研究をしたりリファレンスを読み解いているので、こちらを参考にしてみるといいと思います。

PHPを勉強してきた人ならフォームデータを\$_XXXを取得したくなる気持ちは分かりますが、
一度に様々なデータが取得できるRequestを是非使ってみてください。
これは私からのリクエストです。

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

laravel6 メールのファイル添付機能で日本語のファイル名が文字化けした

目的

  • laravel6のメールにファイル添付をする機能を作成したら日本語のファイル名が文字化けしたのて解決策をメモ的に紹介する。

環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.8 Homebrewを用いてこちらの方法で導入→Mac HomebrewでPHPをインストールする
Laravel バージョン 6.X commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

方法

  • ファイル名指定部分にてmb_encode_mimeheader()を用いてファイル名を指定しエンコードを行うと正常に表示された。
  • mb_encode_mimeheader(添付ファイル名)とすることでエンコードすることができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】EC2インスタンスで作成したLaravelとRDS(MySQL)を連携させる方法

今回はAWSのRDS(MySQL)についてい書いていきます。

AWSにもDockerにも段々慣れてきたのですが、まだまだ知らない機能の方が多いですね。

さて、早速説明していきましょう。

はじめに

まず初めに前提条件として以下の条件を満たしている前提で話を進めめていきます。

・ECSで既にLaravelに関するコンテナの環境が構築済み
・EC2インスタンスへのSSH接続が可能

上記2つの条件を満たしていな場合は下記の記事にすべてやり方が書いてあるので是非ご覧ください!

【AWS】AWS超初心者が、頑張ってDockerで作ったLaravelプロジェクトをECR、ECS、EC2を使ってAWS上で動かしてみた

RDSでMySQLのデータベースを作成

サービス検索欄から「RDS」と検索してください。

そして、「データベースの作成」をクリック。

するとこのような画面になると思います。

image.png

そしたら以下の条件を元にデータベースを作成してください。

データベースの作成方法 標準作成
エンジンのオプション MySQL
バージョン 任意(大体8.*か5.7)
テンプレート 本番稼働用
DBクラスター識別子 database-1
マスターユーザー名・マスターパスワード 任意
Virtual Private Cloud (VPC) 利用するEC2インスタンスと同じVPCを選択
パブリックアクセス可能 あり
VPCセキュリティグループ 既存の選択
追加設定 最初のデータベース名(ローカル環境で使っていたデータベース名を入れるところ)

注意すべきポイントを2つ解説します。

1つ目は「Virtual Private Cloud (VPC)」です。
これは、書いてる通りEC2インスタンスと同じVPCを必ず選択してください。選択しないとEC2インスタンスへSSH接続したときにMySQLへアクセスできないのでくれぐれもご注意を。

2つ目は追加設定の「最初のデータベース名」です。
「DBクラスター識別子」ではdatabase-1と設定しましたが、これとは全く別物です。
例えばローカル環境でexample_databaseという名前のデータベースを利用していた場合は、example_databaseを「DBクラスター識別子」ではなく、「最初のデータベース名」に書いて下さい。

また、ローカルでのデータベース名と本番環境用のデータベース名を分けたい場合でも「最初のデータベース名」に入力してください。

「データベースの作成」をクリックし、しばらくするとデータベースが作成されます。

SSH接続でMySQLのアクセス確認

次に、EC2インスタンスにSSH接続を行いMySQLへアクセスしてみます。

その前に先ほど作成したデータベースの画面へ行ってください。

image.png

「セキュリティ」の「VPCセキュリティグループ」をクリックしてください(sg-から始まるもの)

※注意 「ネットワーク」のVPCではございませんのでご注意を。

そしたらインバウンドルールの編集を行います。

SSH接続でMySQLにアクセスするために、利用するEC2インスタンスの「プライベート IPv4 アドレス」をコピーして、「すべてのトラフィック」で「IPv4アドレス/32」として追加してください。

例:10.0.0.0/32

また、LaravelからもMySQLのデータベースにアクセスできるようにタイプで「MYSQL/Aurora」を選んで「0.0.0.0/0」もしくはALBのセキュリティグループを選んでください。

そしてルールを保存してください。

これでSSH接続でMySQLにアクセスが可能になり、Laravelからもデータベースにアクセスすることができます。

試しにSSH接続でMySQLにアクセスしてみましょう!

ssh -i "C:~\zaemonia-ec2-key.pem" ec2-user@ec2-xxx.ap-northeast-1.compute.amazonaws.com
mysql -u{マスターユーザー名} -p -h{MySQLのエンドポイント}
Enter password:{マスターパスワード}
#パスワード入力は文字が出力されないのでコピペするのが良いかと

無事MySQLにアクセス出来たら成功です!

これでアクセスできない場合は以下の事項を再確認してください。

・マスターユーザー名、マスターパスワード
・セキュリティグループのインバウンドルール

これら2つがしっかりと合っていれば必ずアクセスできます。

諦めずに頑張りましょう!!

以上、「【AWS】EC2インスタンスで作成したLaravelとRDS(MySQL)を連携させる方法」でした!

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください!

Thank you for reading

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

【Laravel6.x〜】デバッグするならdd()?いやいや、ddd()を使おう!!

概要

ZennやQiitaでLaravelの記事を色々見ていたらたまたまddd()というデバッグ用のヘルパー関数を見つけたので実際に使ってみました。

結論:今後はddd()を使う!!

環境

$ composer -V
Composer version 1.10.20 2021-01-27 15:41:06

$ php artisan --version
Laravel Framework 6.20.16

dd()とddd()とは

どちらのデバッグ用のヘルパー関数です。

デバッグ関数 概要
dd() Laravel本体のヘルパー関数
ddd() facade/ignitionのヘルパー関数

(ヘルパー関数:PHPとは別にLaravelや各パッケージが用意している関数)

ddd()Laravel6.xから統合されたfacade/ignitionのヘルパー関数だそうです。

Laravel Ignition Introduces the ddd() Helper

Laravel本体のヘルパー関数でないから

でも見つけることができませんでなかったわけですね。

※ddd()がfacade/ignitionのヘルパー関数であることはこの記事へのコメント、Twitterで教えていただきました。ありがとうございます?‍♂️(どちらもLaravel本体のヘルパー関数だと思っていました)

サンプルケース

記事投稿アプリで特定のidの記事の編集画面表示の処理とします。
ViewからControllerのアクションに記事(Article)クラスのインスタンス(=編集する記事データ)を渡している仕様とします。

app/Http/Controllers/ArticleController.php
class ArticleController extends Controller
{
    // 略

    /**
     * 記事編集画面表示
     *
     * @param  App\Article  $article
     * @return \Illuminate\Http\Response
     */
    public function edit(Article $article)
    {
        return view('articles.edit', compact('article');
    }

    // 略
}

dd()使用時の画面

app/Http/Controllers/ArticleController.php
class ArticleController extends Controller
{
    // 略

    /**
     * 記事編集画面表示
     *
     * @param  App\Article  $article
     * @return \Illuminate\Http\Response
     */
    public function edit(Article $article)
    {
        // dd()
        dd($article);
        return view('articles.edit', compact('article');
    }

    // 略
}

スクリーンショット 2021-02-27 0.05.42.png

モデルインスタンスの場合、多くの場合

  • attributes:デバッグ対象のプロパティ
  • relations:デバック対象のリレーション関係にある情報

を見るかと思いますし、dd()で取得できるのはそれくらいかと思います。

ddd()使用時の画面

app/Http/Controllers/ArticleController.php
class ArticleController extends Controller
{
    // 略

    /**
     * 記事編集画面表示
     *
     * @param  App\Article  $article
     * @return \Illuminate\Http\Response
     */
    public function edit(Article $article)
    {
        // ddd()
        ddd($article);
        return view('articles.edit', compact('article');
    }

    // 略
}

【Debugタブ(初期表示)】

これはdd()で表示される内容と同じですね。

スクリーンショット 2021-02-26 23.34.00.png

【Stack Traceタブ】

スクリーンショット 2021-02-26 23.32.59.png

スタックトレースとは、こちらの記事でこう書かれています。

エラーが発生するまでに、どんな処理を、どの順番で呼び出したか表現したもの

こうは書いていますが、今回のように処理を中断した場合は中断したところまでの処理の流れを追えるようになっています。

今回の場合でいうとpublic/index.phpの55行目がまずは実行されて、最終的にapp/Http/Controllers/ArticleController.phpの89行目(ddd($article);)が実行されて処理が中断されているという流れがわかります。
(ここまでに計44個の工程があるんですね〜)

【Requestタブ】

スクリーンショット 2021-02-26 23.33.30.png

リクエストの情報が記載されています。

  • GETであること
  • CSRFトークンの値
  • リクエストヘッダー

などが一目でわかります。

【App】

スクリーンショット 2021-02-26 23.33.46.png

  • Controller:実行したアクションメソッド
  • Route name:ルーティング名
  • Route Parameters:アクションメソッドに渡したパラメーター(引数にあたる)
  • Middleware:ルーティングに設定したミドルウェア

こんな情報が一目で確認できます。

【User】

スクリーンショット 2021-02-26 23.33.51.png

Userタブではログイン中のユーザーの情報が表示されます。

この情報は

dd(Auth::user());

で取得できるUserクラスインスタンスのattributesプロパティからpasswordの情報を除いたものです。(これはセキュリティの観点からなのかな...?)

【Context】

スクリーンショット 2021-02-26 23.33.56.png

  • Repository:GitHubのリポジトリ
  • Message:最新のコミットメッセージと
  • Laravel version:Laravelのバージョン
  • Laravel locale:言語設定
  • Laravel config cache:設定ファイルのキャッシュをクリアしたか(※)
  • PHP version:PHPのバージョン

(※)
この画像ではfalseになっていますが、

$ php artisan config:cache

実行後に再度、ddd($article);するとtrueに変わっていました。

config系のキャッシュをクリアしてない(=config系の設定が反映されてない)ことに起因するエラーの発見もしやすくなりそうですね。

まとめ

ddd()、めっちゃ良い!!

  • UI(タブ切り替えでわかりやすい)
  • 情報量(ログインユーザーや設定情報もすぐわかる)

の観点から完全にdd()の上位互換な印象ですね。

ddd()ではなくdd()を使う理由が見つからないので今後はddd()使います。

(もっと早く知りたかった...?‍♂️)

参考

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

もちろんバックエンドとのやり取りはCORS対策を行っていますよね???

皆さんバックエンドとのやり取りでCORS対策は行っていますか?

今回はLaravelでCORS対策を行うやり方を書いていこうかなと思います。

既にLaravelに触れたことのある方はconfig\cors.phpを見たことがあるかと思います。

config/cors.php
<?php
return [
    'paths' => ['*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,
];

実際はこんな感じに書いてあります。

ただ、これ注目し欲しいのがallowed_originsです。

この中身を*(アスタリスク)にしてしまうと、全てのドメインから通信を行うことができてしまいます。

便利かもしれませんが、本番環境でどのドメインからも通信を行うことができたら個人情報だだもれですね。

じゃあ対策をしてあげましょう!

ミドルウェアの作成

以下のコマンドを入力してミドルウェアを作成します。名前は適当に変えて下さい。今回はAPI通信用のCORS対策を行うのでApiCorsとしています。

php artisan make:middleware ApiCors

そしたらapp\Http\Middleware\ApiCors(自分で決めた名前)を開いてください。

Middleware\ApiCors.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ApiCors
{
    public function handle(Request $request, Closure $next)
    {
        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            return $next($request)
                ->header('Access-Control-Allow-Origin', config('cors.allowed_origins'))
                ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
                ->header('Access-Control-Allow-Headers', ['X-Requested-With', 'Content-Type', 'Origin', 'Cache-Control', 'Authorization', 'Accept', 'Accept-Encoding'])
                ->header('Access-Control-Allow-Credentials', true);
        } else {
            return $next($request)
                ->header('Access-Control-Allow-Origin', config('cors.allowed_origins'))
                ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
                ->header('Access-Control-Allow-Headers', ['X-Requested-With', 'Content-Type', 'Origin', 'Cache-Control', 'Authorization', 'Accept', 'Accept-Encoding']);
        }
    }
}

Access-Control-Allow-Originで通信を行うドメインを入力します。

config('cors.allowed_origins')としているのは、環境変数としてローカルで開発をしている時と本番環境用に分けるためです。

なんたって本番環境用にローカルホストのドメイン入力したら一発アウト。

Karnel.php

次にMiddleware\Kernel.phpに今作成したミドルウェアを登録します。

Middleware\Karnel.php
    protected $routeMiddleware = [
        'apicors' => \App\Http\Middleware\ApiCors::class,
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

'apicors' => \App\Http\Middleware\ApiCors::classのようにします。

apicorsの部分はお好きな名前で。

CORS対策の適用

routes\api.phpにCORS対策のミドルウェアを適用させます。

routes\api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ApisController;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('apicors')->group(function() {
  Route::apiResource('/apis', ApisController::class);
});

こんな感じでCORS対策を行うことができます!

これでもまだセキュリティ対策は甘い方だと思います。

まぁでも一歩前進ということで。

以上、「もちろんバックエンドとのやり取りはCORS対策を行っていますよね???」でした!

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください!

Thank you for reading

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

npm install && npm run devした際のエラー

laravel8でnpm install && npm run devを実行し、npm ERR!というエラーが出た際に解決した方法を書きます。

下記コマンド実行

$ php artisan cache:clear

実行結果

Cache cleared successfully

下記コマンド実行

php artisan config:cache

実行結果

Configuration cache cleared!
Configuration cached successfully!

再度下記コマンドを実行

npm install && npm run dev

解決しました。

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