20200916のlaravelに関する記事は13件です。

PHP、Laravel学習 2

ポートフォリオ作成

ここからポートフォリオ作成に入ります。

献立を考えてくれるWebアプリケーションを開発したいです
私は料理が好きで、よく作るのですがレパートリーがたくさんあるにもかかわらず
いざ何かを作ろうとすると、かなり悩んでしまいます
私は料理を買い物からするので、スーパーに行ったは良いものの、そこで買い物をしながら
悩んでしまったり、いつも作っている物の繰り返しになってしまいます。

解決したい問題

1、たくさんレパートリーがあるはずなのに忙しい中で料理をしようと思うとそれを思うように
引き出すことができない問題

2、料理の種類が和食、洋食、中華...等、いざ作ろうと思うと混ざってしまったりする問題

3、献立を考えている時間の削減、スーパーで買い物をする時の効率化

4、似通った料理の連続になってしまう問題(例えば、オムライス→炊き込みご飯→カレーの繰り返し等)

欲しい機能

1、献立をランダムで表示してくれる
2、献立に必要な材料の入力、保存ができる
3、献立のレパートリーを入力、保存できる
4、献立をカテゴライズできる
5、献立のカテゴリーを入力、保存できる
6、入力した献立の確認ができる
7、入力したカテゴリーの確認ができる

次回から作っていきます。

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

PHPでログイン機能を実装したら、ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)というエラーでハマった話

https://awesome-linus.com/2019/06/05/laravel-tutorial-todo/

こちらのサイト様の記事を参考にログイン機能を実装していたら、はまりました。

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

パスワードは使用していません。
ルートユーザーでアクセスしたら拒否されたというもの。
ログインと新規登録機能を実装し、アプリをWEBで開いた段階でこのメッセージが表示されました。

$ mysql -u root
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

mysql -u rootでログインするとエラーが出るが、

$mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.

このように打ち込むとエラーが出ない。つまり、パスワードをYESにして、設定したパスワードをPHPのファイルのどこかに打ち込む必要があるのではないかと判断。

それっぽいファイルを探してみる。

database.php
'connections' => [
  'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
# 以下省略

'password' => env('DB_PASSWORD', ''),
この部分に注目した。
’’の間にパスワードを手動で入れればもしかしたら行けるのでは?
と思ったが、’’の中に手動でパスワードを打ち込んでもうまくいかない。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306

DB_DATABASE=todo
DB_USERNAME=root
DB_PASSWORD=secret

しやここのsecretで設定されているパスワードの部分を書き換えたらいけるのではと思い、
secretの部分を書き換えたりしたがうまくいかず。

パスワードをYESで接続するにはどうしたらいいのだ。

仕方ないので方針転換して、rootユーザのパスワードをなしにすることにしました。

 $mysql -u root -p

パスワードを打ち込んで接続

mysql> SET PASSWORD FOR ユーザ名@ホスト名=password('新しいパスワード');

新しいパスワードの部分を空欄にして設定。
パスワードを打ち込んでログインしてみます。

スクリーンショット 2020-09-16 22.05.00.png

無事に成功!

rootユーザのパスワードの設定を使用をYESにして、パスワードを設定する方法はわかりませんでしたが、
パスワードの設定をログインして書き換えることでうまく行きませんでした。

またなにかエラーを解決しましたら、引き続きアウトプットしていきます。

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

クロスサイトに対応する

環境

環境
Laravel7.x
HTTPS
AWS ALB, EC2

PHPでフレームワークはLaravel7.14を利用しています。
webサーバーはnginxです。(あまり関係ない)
下記に行った順で課題に対するコミットを記述していきます。

課題

クロスサイトのPOSTメソッドで指定したオリジンから取得したものをsessionに保存したい。
当プロジェクトでは、あるプラットフォームから取得できるuser_idのようなものを
sessionに保存したい、という課題でした。
しかしながら、解決策はフロントエンドとサーバーサイドでオリジンを分けている際にも対応可能の解決策でした。

①HTTPS化する

私が調査した中で、クロスサイトを有効にするには
通信を暗号化(常時ssl化)し、CookieのSecure属性をtrueにする
というのが最初の打開策でした。
まず、はじめにロードバランサーのエンドポイントにssl証明書を発行します。
30分ぐらいで証明書の発行ができます。
参考URLはこちら↓
https://recipe.kc-cloud.jp/archives/11084

さらに、常時HTTPS化するには(Laravel7.x)

src/app/Http/Middleware/TrustProxies.php
<?php

namespace App\Http\Middleware;

use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array|string|null
     */
    protected $proxies = '**';

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_ALL;
}

で、LBの終端でHTTPS化が可能となります。

CookieのSecure属性をtrueにする。

次に、Samesite=none; Secureにするために

## .env

SESSION_SECURE_COOKIE=true
src/config/session.php
/*
〜〜〜〜〜〜〜
〜〜〜〜〜〜〜
*/
'same_site' => 'none',

とします。
これでchromeとfireboxでは課題を解決しましたが、
safariではSamesite=noneが実装されていないため
sessionを保存できず、、、、という感じでした。

②CORSの設定を編集

そして行き着いたのは、ヘッダーに許容するオリジンを追加するという手段です。
Laravel7.xにはデフォルトでsrc/config/cors.phpがあるので
下記のように編集します。

src/config/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
    |
    */

    // 許容するmiddlewareのグループを記載する。
    // 当プロジェクトでは * にしています。
    'paths' => ['*'],

    // 許容するメソッドを指定する
    'allowed_methods' => ['GET', 'POST'],

    // 許容するオリジンを指定する。
    // .envに記載してもいい
    // 'allowed_origins' => [env('REQUIRE_DMAIN', NULL)],
    'allowed_origins' => ['https://<ドメイン>'],

    'allowed_origins_patterns' => [],

    // 許容するヘッダーを記載する
    'allowed_headers' => ['X-CSRF-TOKEN', 'Content-Type', 'Origin', 'Authorization', 'Accept', 'X-Requested-With'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

上記のようにすることで、safariでもsessionを記載することができました。
Laravel7.x未満でもcors.phpはcomposerでインストールできますのでそちらを参照してください。

以上が私が行った解決策です。
allowed_originsにポート番号も記載すれば異なるポート番号からも許容できるようになります。
もっと良い方法があれば是非、コメントよろしくお願いします。

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

laravel8系でTarget class [XXXController] does not exist. error

公式コピペ注意報発令中です。
どこもおかしくない、でもわからん原因は?

原因

web.phpの書き方が8系になって変わった。

参考:https://laravel.com/docs/8.x/releases

対処例

これまで

web.php
Route::get('/users', UserController@index);

これから

web.php
use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);

以上。

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

[Laravel] LINEログインv2.1を実装する

今回の題

LINEログインをLaravelにて実装しました。
アウトプットとして残します。

使用したバージョン

  • Laravel 6.8 ※Laravelのインストール手順は省略します。
  • LINEログインv2.1
  • guzzle7.0

チャネルIDとチャネルシークレットの取得

4ステップに分けて説明していきます。

1. 開発者用のアカウントを作成

以下にアクセスし、LINEのアカウントを使って開発者用のページにログインしてください。
LINE Developers

2. プロバイダーの作成

以下のページの左サイドバーのプロバイダーを選択し、画面中央辺りの「作成」を押してください。
LINE Developers コンソール

スクリーンショット 2020-07-08 5.00.01.png

作成するプロバイダー名を求められるので適当に入力し、「作成」を押したらプロバイダーの作成は完了です。
スクリーンショット 2020-07-08 5.12.40.png

3. チャネルの作成

プロバイダーの作成後、以下のようなページに飛ばされます。
画像内の赤枠の「LINE ログイン」を選択してください。
スクリーンショット 2020-09-15 21.58.03.png

チャネル作成画面に飛びます。
チャネルの名前や説明などは適宜全て入力し、アプリタイプは「ウェブアプリ」を選択しておいてください。
「作成」を押したらチャネルの作成は完了です。
スクリーンショット 2020-07-08 5.20.24.png

4. チャネルIDとチャネルシークレットの取得

チャネル作成後、作成したチャネルの設定ページに飛ばされます。
(画像は黒く塗りつぶしています)

  • チャネルID
    チャネル基本設定のタブの一番上にのっています。
    あとで使うのでコピーしておいてください。
    スクリーンショット 2020-09-15 22.00.29.png

  • チャネルシークレット
    チャネル基本設定のタブの下の方にのっています。
    スクリーンショット 2020-09-15 22.00.35.png

以上でチャネルIDとチャネルシークレットがGETできました。

コールバックURLの設定

ユーザーが認証を許可した後に、リダイレクトされるURLを指定します。
LINEログイン設定のタブに移動してコールバックURLを入力し更新を押してください。
尚、この記事では後のルーティングでauth/line/callbackというパスを指定するので、記事通りに進めるのであれば、
https://各自のドメイン/auth/line/callbackで設定しておいて下さい。
スクリーンショット 2020-09-16 11.55.09.png

メールアドレスを取得する設定

ログインしたユーザーのメールアドレスを取得するための設定です。必要な場合のみ設定して下さい。
チャネル基本設定のタブの下の方に「OpenID Connect」という項目があります。
申請ボタンを押すと、申請条件への同意と、メールアドレスの取得と利用についてユーザーに提示する文面のスクリーンショットのアップロードを求められるので済ませます。
スクリーンショット 2020-09-15 22.17.32.png

必要なライブラリの準備

・Guzzle
リクエストを送信する際に使用します。
公式

$ composer require guzzlehttp/guzzle

Laravel

ここからコードを書いていきます。

設定

・チャネルID
・チャネルシークレット
・コールバックURL
.envに書いておき、config経由で呼び出すことにします。

.env
LINE_CHANNEL_ID=あなたのチャネルid
LINE_CHANNEL_SECRET=あなたのチャネルシークレット
LINE_CALLBACK_URL=設定したコールバックURL

configディレクトリにline.phpというファイルを作り、envから値を受け取るように編集します。

config/line.php
<?php
return [
    'client_id' => env('LINE_CHANNEL_ID'),
    'client_secret' => env('LINE_CHANNEL_SECRET'),
    'callback_url' => env('LINE_CALLBACK_URL'),
];

ルーティング

routes/web.phpを以下のように設定します。

routes/web.php
// LINEの認証画面に遷移
Route::get('auth/line', 'Auth\LineOAuthController@redirectToProvider')->name('line.login');
// 認証後にリダイレクトされるURL(コールバックURL)
Route::get('auth/line/callback', 'Auth\LineOAuthController@handleProviderCallback');

view

この記事ではURLに遷移させるだけの単純なものを書いておきます。
本来は、公式の指定するデザインのログインボタンを設定する必要があります。
LINEログインボタン デザインガイドライン

<a href="{{ route('line.login') }}">LINEでログイン</a>

コントローラー

以下で作成

$ php artisan make:controller Auth/LineOAuthController

編集します。

app/Http/Controllers/Auth/LineOAuthController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use GuzzleHttp\Client;

class LineOAuthController extends Controller
{
    private const LINE_OAUTH_URL = 'https://access.line.me/oauth2/v2.1/authorize?';
    private const LINE_TOKEN_API_URL = 'https://api.line.me/oauth2/v2.1/';
    private const LINE_PROFILE_API_URL = 'https://api.line.me/v2/';
    private $client_id;
    private $client_secret;
    private $callback_url;

    public function __construct() {
        $this->client_id = Config('line.client_id');
        $this->client_secret = Config('line.client_secret');
        $this->callback_url = Config('line.callback_url');
    }

    public function redirectToProvider()
    {
        $csrf_token = Str::random(32);
        $query_data = [
            'response_type' => 'code',
            'client_id' => $this->client_id,
            'redirect_uri' => $this->callback_url,
            'state' => $csrf_token,
            'scope' => 'profile openid',
        ];
        $query_str = http_build_query($query_data, '', '&');
        return redirect(self::LINE_OAUTH_URL . $query_str);
    }

    public function handleProviderCallback(Request $request)
    {
        $code = $request->query('code');
        $token_info = $this->fetchTokenInfo($code);
        $user_info = $this->fetchUserInfo($token_info->access_token);
        //  ログイン処理
    }

    private function fetchUserInfo($access_token)
    {
        $base_uri = ['base_uri' => self::LINE_PROFILE_API_URL];
        $method = 'GET';
        $path = 'profile';
        $headers = ['headers' => 
            [
                'Authorization' => 'Bearer ' . $access_token
            ]
        ];
        $user_info = $this->sendRequest($base_uri, $method, $path, $headers);
        return $user_info;
    }

    private function fetchTokenInfo($code)
    {
        $base_uri = ['base_uri' => self::LINE_TOKEN_API_URL];
        $method = 'POST';
        $path = 'token';
        $headers = ['headers' => 
            [
                'Content-Type' => 'application/x-www-form-urlencoded'
            ]
        ];
        $form_params = ['form_params' => 
            [
                'code'          => $code,
                'client_id' => $this->client_id,
                'client_secret' => $this->client_secret,
                'redirect_uri'  => $this->callback_url,
                'grant_type'    => 'authorization_code'
            ]
        ];
        $token_info = $this->sendRequest($base_uri, $method, $path, $headers, $form_params);
        return $token_info;
    }

    private function sendRequest($base_uri, $method, $path, $headers, $form_params = null)
    {
        try {
            $client = new Client($base_uri);
            if ($form_params) {
                $response = $client->request($method, $path, $form_params, $headers);
            } else {
                $response = $client->request($method, $path, $headers);
            }
        } catch(\Exception $ex) {
            // 例外処理
        }
        $result_json = $response->getbody()->getcontents();
        $result = json_decode($result_json);
        return $result;
    }
}

アクション名が思いつかなかったのでSociliteから流用して使ってます。

handleProviderCallback()の変数、$token_info$user_infoには、

  • $token_info → トークン情報(アクセストークン、リフレッシュトークンなど)
  • $user_info → ユーザー情報(ユーザーID、ユーザー名、プロフィール画像、プロフィールメッセージ)

が入っていますので、これらを各自のテーブル構成に合わせて保存しログインさせて下さい。
尚、アクセストークンの有効期間は30日ですので、適宜リフレッシュトークンを使って新しいアクセストークンを取得して下さい。

一言

ソーシャルログインはSocialiteを使ったTwitterログインしか実装経験はありませんでしたが、LINEでも意外と簡単にできて驚きでした。特に鬼門はないかと思います。
何か間違えなどがあればコメントにお願いいたします。

参考

LINE公式 ウェブアプリにLINEログインを組み込む
LINE公式 ユーザープロフィールを取得する

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

[Laravel] LINEログインを実装する

今回の題

LINEログインをLaravelにて実装しました。
アウトプットとして残します。

使用したバージョン

  • Laravel 6.8 ※Laravelのインストール手順は省略します。
  • LINEログインv2.1
  • guzzle7.0

チャネルIDとチャネルシークレットの取得

4ステップに分けて説明していきます。

1. 開発者用のアカウントを作成

以下にアクセスし、LINEのアカウントを使って開発者用のページにログインしてください。
LINE Developers

2. プロバイダーの作成

以下のページの左サイドバーのプロバイダーを選択し、画面中央辺りの「作成」を押してください。
LINE Developers コンソール

スクリーンショット 2020-07-08 5.00.01.png

作成するプロバイダー名を求められるので適当に入力し、「作成」を押したらプロバイダーの作成は完了です。
スクリーンショット 2020-07-08 5.12.40.png

3. チャネルの作成

プロバイダーの作成後、以下のようなページに飛ばされます。
画像内の赤枠の「LINE ログイン」を選択してください。
スクリーンショット 2020-09-15 21.58.03.png

チャネル作成画面に飛びます。
チャネルの名前や説明などは適宜全て入力し、アプリタイプは「ウェブアプリ」を選択しておいてください。
「作成」を押したらチャネルの作成は完了です。
スクリーンショット 2020-07-08 5.20.24.png

4. チャネルIDとチャネルシークレットの取得

チャネル作成後、作成したチャネルの設定ページに飛ばされます。
(画像は黒く塗りつぶしています)

  • チャネルID
    チャネル基本設定のタブの一番上にのっています。
    あとで使うのでコピーしておいてください。
    スクリーンショット 2020-09-15 22.00.29.png

  • チャネルシークレット
    チャネル基本設定のタブの下の方にのっています。
    スクリーンショット 2020-09-15 22.00.35.png

以上でチャネルIDとチャネルシークレットがGETできました。

コールバックURLの設定

ユーザーが認証を許可した後に、リダイレクトされるURLを指定します。
LINEログイン設定のタブに移動してコールバックURLを入力し更新を押してください。
尚、この記事では後のルーティングでlogin/line/callbackというパスを指定するので、記事通りに進めるのであれば、
https://各自のドメイン/login/line/callbackで設定しておいて下さい。
スクリーンショット 2020-09-16 11.55.09.png

メールアドレスを取得する設定

ログインしたユーザーのメールアドレスを取得するための設定です。必要な場合のみ設定して下さい。
チャネル基本設定のタブの下の方に「OpenID Connect」という項目があります。
申請ボタンを押すと、申請条件への同意と、メールアドレスの取得と利用についてユーザーに提示する文面のスクリーンショットのアップロードを求められるので済ませます。
スクリーンショット 2020-09-15 22.17.32.png

必要なライブラリの準備

・Guzzle
リクエストを送信する際に使用します。
公式

$ composer require guzzlehttp/guzzle

Laravel

ここからコードを書いていきます。

設定

・チャネルID
・チャネルシークレット
・コールバックURL
.envに書いておき、config経由で呼び出すことにします。

.env
LINE_CHANNEL_ID=あなたのチャネルid
LINE_CHANNEL_SECRET=あなたのチャネルシークレット
LINE_CALLBACK_URL=設定したコールバックURL

configディレクトリにline.phpというファイルを作り、envから値を受け取るように編集します。

config/line.php
<?php
return [
    'client_id' => env('LINE_CHANNEL_ID'),
    'client_secret' => env('LINE_CHANNEL_SECRET'),
    'callback_url' => env('LINE_CALLBACK_URL'),
];

ルーティング

routes/web.phpを以下のように設定します。

routes/web.php
// LINEの認証画面に遷移
Route::get('auth/line', 'Auth\LineOAuthController@redirectToProvider')->name('line.login');
// 認証後にリダイレクトされるURL(コールバックURL)
Route::get('auth/line/callback', 'Auth\LineOAuthController@handleProviderCallback');

view

この記事ではURLに遷移させるだけの単純なものを書いておきます。
本来は、公式の指定するデザインのログインボタンを設定する必要があります。
LINEログインボタン デザインガイドライン

<a href="{{ route('line.login') }}">LINEでログイン</a>

コントローラー

以下で作成

$ php artisan make:controller Auth/LineOAuthController

編集します。

app/Http/Controllers/Auth/LineOAuthController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use GuzzleHttp\Client;

class LineOAuthController extends Controller
{
    private const LINE_OAUTH_URL = 'https://access.line.me/oauth2/v2.1/authorize?';
    private const LINE_TOKEN_API_URL = 'https://api.line.me/oauth2/v2.1/';
    private const LINE_PROFILE_API_URL = 'https://api.line.me/v2/';
    private $client_id;
    private $client_secret;
    private $callback_url;

    public function __construct() {
        $this->client_id = Config('line.client_id');
        $this->client_secret = Config('line.client_secret');
        $this->callback_url = Config('line.callback_url');
    }

    public function redirectToProvider()
    {
        $csrf_token = Str::random(32);
        $query_data = [
            'response_type' => 'code',
            'client_id' => $this->client_id,
            'redirect_uri' => $this->callback_url,
            'state' => $csrf_token,
            'scope' => 'profile openid',
        ];
        $query_str = http_build_query($query_data, '', '&');
        return redirect(self::LINE_OAUTH_URL . $query_str);
    }

    public function handleProviderCallback(Request $request)
    {
        $code = $request->query('code');
        $token_info = $this->fetchAccessToken($code);
        $user_info = $this->fetchUserInfo($token_info->access_token);
        //  ログイン処理
    }

    private function fetchUserInfo($access_token)
    {
        $base_uri = ['base_uri' => self::LINE_PROFILE_API_URL];
        $method = 'GET';
        $path = 'profile';
        $headers = ['headers' => 
            [
                'Authorization' => 'Bearer ' . $access_token
            ]
        ];
        $user_info = $this->sendRequest($base_uri, $method, $path, $headers);
        return $user_info;
    }

    private function fetchAccessToken($code)
    {
        $base_uri = ['base_uri' => self::LINE_TOKEN_API_URL];
        $method = 'POST';
        $path = 'token';
        $headers = ['headers' => 
          [
          'Content-Type' => 'application/x-www-form-urlencoded'
          ]
        ];
        $form_params = ['form_params' => 
          [
          'code'          => $code,
          'client_id' => $this->client_id,
          'client_secret' => $this->client_secret,
          'redirect_uri'  => $this->callback_url,
          'grant_type'    => 'authorization_code'
          ]
        ];
        $token_info = $this->sendRequest($base_uri, $method, $path, $headers, $form_params);
        return $token_info;
    }

    private function sendRequest($base_uri, $method, $path, $headers, $form_params = null)
    {
        try {
            $client = new Client($base_uri);
            if ($form_params) {
                $response = $client->request($method, $path, $form_params, $headers);
            } else {
                $response = $client->request($method, $path, $headers);
            }
        } catch(\Exception $ex) {
            // 例外処理
        }
        $result_json = $response->getbody()->getcontents();
        $result = json_decode($result_json);
        return $result;
    }
}

handleProviderCallback()の変数、$token_info$user_infoには、

  • $token_info → トークン情報(アクセストークン、リフレッシュトークンなど)
  • $user_info → ユーザー情報(ユーザーID、ユーザー名、プロフィール画像、プロフィールメッセージ)

が入っていますので、これらを各自のテーブル構成に合わせて保存しログインさせて下さい。
尚、アクセストークンの有効期間は30日ですので、適宜リフレッシュトークンを使って新しいアクセストークンを取得して下さい。

一言

ソーシャルログインはSocialiteを使ったTwitterログインしか実装経験はありませんでしたが、LINEでも意外と簡単にできて驚きでした。特に鬼門はないかと思います。
何か間違えなどがあればコメントにお願いいたします。

参考

LINE公式 ウェブアプリにLINEログインを組み込む
LINE公式 ユーザープロフィールを取得する

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

Laravel getFormattedDueDateAttributeでTrailing data のエラーが出た場合

初めに

仕事でLaravelの知識が必要となり、下記リンクの良さげなチュートリアルをやっていまた。
https://www.hypertextcandy.com/laravel-tutorial-todo-app-list-tasks

チュートリアル通りにやっていてもエラーってやっぱり起きるのですよね〜。。。

環境

チュートリアルが2018年のものだったので、少し古めのv6でやってみた(この時、もっと詳しく年代別のバージョンを調べていれば・・・:persevere:

$ php artisan --version
Laravel Framework 6.18.40

問題の箇所

上記リンク先のチュートリアルである「日付の表示形式を変更する」の部分

Task.php
// この行を追加
use Carbon\Carbon;

class Task extends Model
{
    /* 中略 */

    /**
     * 整形した期限日
     * @return string
     */
    public function getFormattedDueDateAttribute()
    {
        return Carbon::createFromFormat('Y-m-d', $this->attributes['due_date'])
            ->format('Y/m/d');
    }
}

Carbon ライブラリを使って期限日の値の形式を変更して返す部分で、以下のようなエラーが!

Carbon\Exceptions\InvalidFormatException
Trailing data (View: /Users/***/resources/views/tasks/index.blade.php)

全てコピペし直したりしたので、どう考えてもスペルミスではない。とすると、、、

バージョンによる違いでした。。

https://stackoverflow.com/questions/50208932/laravel-model-trailing-data-when-save-the-model

上記stackoverflowで見た回答を試すとエラーは解消された。。。
おそらくv5とv6の違いなのでしょう。

Task.php
  /**
     * 整形した期限日
     * @return string
     */
   public function getFormattedDueDateAttribute()
    {
        // createFromFormatの中を 'Y-m-d H:i:s'に変更する
        return Carbon::createFromFormat('Y-m-d H:i:s', $this->attributes['due_date'])
        ->format('Y/m/d');
    }

と言うか、チュートリアルのページをよく見ると、以下のようなコメントもありました:joy:

お世話になります。
Laravel6.14.0です。
後半の「日付の表示形式を変更する」の項で、getFormattedDueDateAttributeを追加し、テンプレートの修正をしてブラウザを再読み込みしたところ、「trailing error」が出ました。
createFromFormat内を('Y-m-d H:i:s' , ......)と修正したところ、解決しました。
念のため、お知らせしておきます。

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

Trelloも要らない? Exmentでカンバンボードを運用してみる

BacklogやRedmineが要らないとか煽り記事書いてしまった筆者です。どうもです。

さて、Exmentでガントチャートを作ってしまったら、どうせならカンバンボードもやってみたくなるのが人情ではないでしょうか?

というわけで、やってみました。

Expressでフロントを作る

簡易原価計算の時や、ガントチャートの時と同じく、Express + Pugでフロント画面を作ります。

今回、フロントではSortable.jsというライブラリを用いました。HTMLの要素を自由にドラッグアンドドロップできるようになる魔法のようなライブラリです。

まずは、タスク表示のGET部分です。

app.js
app.get('/kanban', async(req,res) => {
  ;(async () => {
    const exmentToken = JSON.parse(await fs.readFileSync('./exment_tokens.txt')).access_token
    let tasksData = await axios.get('https://example.com/api/data/tasks/?orderby=start_at', {
      headers: {
        'Authorization': 'Bearer ' + exmentToken
      }
    })
    .then(res => { return res.data.data })
    .catch(err => { console.error(err) })

    let tasksArray = []
    tasksData.forEach(item => {
      const id = item.id
      const name = item.value.title
      const status = item.value.status
      tasksArray.push({
        id: String(id),
        name: name,
        status: status,
        endAt: item.value.end_at
      })
    })

    let statuses = await axios.get('https://example.com/api/column/86', {
      headers: {
        'Authorization': 'Bearer ' + exmentToken
      }
    })
    .then(res => { return res.data.options.select_item_valtext })
    .catch(err => { console.error(err) })

    statuses = statuses.split('\r\n')
    let statusData = []
    statuses.forEach(status => {
      status = status.split(',')
      statusData.push({
        id: status[0],
        title: status[1]
      })
    })

    // console.log(statusData)

    let payload = []
    let i = 1
    statusData.forEach(item => {
      const tasks = _.filter(tasksArray, { status: item.id })
      let j = 1
      const taskItems = () => {
        let taskItems = []
        tasks.forEach(item => {
          taskItems.push({
            id: `item-id-${j}`,
            exmentId: item.id,
            title: item.name,
            endAt: item.endAt
          })
          j++
        })
        return taskItems
      }
      payload.push({
        id: `board-id-${i}`,
        title: item.title,
        item: taskItems()
      })
      i++
    })

    // console.log(payload)

    res.render('kanban', { payload })
  })()

})

今回も、Exment側からデータを取得して、Sortableのデータ形式に整形しています。なんというか、JSONコネコネ屋さんって感じですね…

続いて、viewです。

kanban.pug
<!DOCTYPE html>
html(lang="ja")
  head
    meta(charset="UTF-8")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    title Document
    .
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" integrity="sha512-NhSC1YmyruXifcj/KFRWoC561YpHpc5Jtzgvbuzx5VozKpWvQ+4nXhPdFgmx8xqexRcpAglTj9sIBWINXa8x5w==" crossorigin="anonymous" />
    style.
      * {
        box-sizing: border-box;
      }
      [class^="wrapper-"] {
        background: #ddd;
        padding: 0;
        width: 300px; 
        margin-right: 30px;
        flex: 0 0 auto;
        height: 100%;
      }
      [id^="board-id-"] {
        padding-left: 0;
        padding: 15px;
      }
      #boards {
        width: 100vw;
        overflow-x: scroll;
        display: flex;
        height: 100%;
      }
      .title {
        margin-top: 15px;
        text-align: center;
        margin-bottom: 0;
      }
      .item {
        background: #f2f2f2;
        list-style-type: none;
        padding: 15px;
        margin-bottom: 15px;
        margin-left: 0;
      }
      .item:last-child {
        margin-bottom: 0;
      }
      .due {
        font-size: 11px;
        color: #777;
        font-weight: bold;
      }


  body
    #boards
      each board in payload
        div(class="wrapper-" + board.id)
          h2.title=board.title
          ul(id=board.id).sortable
            if board.item
              each item in board.item
                li.item(data-exmentid=item.exmentId)
                  =item.title
                  br
                  span.due=`期日:${item.endAt}`

    .
      <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js" integrity="sha512-ELgdXEUQM5x+vB2mycmnSCsiDZWQYXKwlzh9+p+Hff4f5LA+uf0w2pOp3j7UAuSAajxfEzmYZNOOLQuiotrt9Q==" crossorigin="anonymous"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.20.0/axios.min.js" integrity="sha512-quHCp3WbBNkwLfYUMd+KwBAgpVukJu5MncuQaWXgCrfgcxCJAq/fo+oqrRKOj+UKEmyMCG3tb8RB63W+EmrOBg==" crossorigin="anonymous"></script>
    script.
      var boards = document.querySelectorAll('.sortable')
      var i = 1
      boards.forEach(function(board) {
        new Sortable(board, {
          group: 'status',
          onEnd: function(event) {
            var newStatus = event.to.id.replace(/[^0-9]/g, '')
            var itemId = event.item.dataset.exmentid
            axios.put('/kanban', {
              id: itemId,
              status: newStatus
            })
          }
        })
      })

これでhttp://localhost:3000/kanbanにアクセスしてみましょう。

2020-09-16_10h24_21.png

こんな感じになっていれば成功です!

なお、Trello風のUIを実現するCSSは、以下の記事を参考にしました。

カンバン間を移動したらステータスが変更されるようにPUTしよう

ガントチャートの時もそうでしたが、フロントで弄った時に、きちんとバックエンドに通信が飛んでデータが上書きされてほしいですよね。

Sortable.jsにもコールバックの仕組みがきちんと用意されているので、以下の技術を追記します。

kanban.pug
script.
  var boards = document.querySelectorAll('.sortable')
  var i = 1
  boards.forEach(function(board) {
   new Sortable(board, {
      group: 'status',
      onEnd: function(event) {
        var newStatus = event.to.id.replace(/[^0-9]/g, '')
        var itemId = event.item.dataset.exmentid
        axios.put('/kanban', {
          id: itemId,
          status: newStatus
        })
      }
    })
  })

オプションのプロパティonEndが、ドラッグアンドドロップを完了した時のコールバックです。
ここにExment側のAPIから持ってきたExmentのタスクのIDと、ボードのIDを取り込みます。

SortableのボードのIDは、board-id-1みたいな感じなので、数字以外を取り払ってあげます。Exmentで設定した列設定では、enum型で値は数字を列挙しているので、ID番号=Exment側の値となります。

最後にaxiosで、Express側にデータを投げます。

Express側はこんな感じです。

app.js
app.put('/kanban', async (req, res) => {
  ;(async () => {
    const exmentToken = JSON.parse(await fs.readFileSync('./exment_tokens.txt')).access_token

    // console.log(req.body)

    await axios.put('https://example.com/api/data/tasks/' + req.body.id, {
      value: {
        status: req.body.status
      }
    }, {
      headers: {
        'Authorization': 'Bearer ' + exmentToken
      }
    })
    .then(res => { return res })
    .catch(err => { console.error(err.response.data) })
  })()
})

シンプルですね。タスクのIDをエンドポイントに付加して、PUTします。statusも、先程のボードのID番号を指定してあげれば、ステータスの状態と連動します。

動作確認してみよう

Exmentにアクセスし、カンバン上で動かす予定のタスクを確認しておきます。未着手になってますね。

2020-09-15_15h04_01.png

カンバン上でドラッグ&ドロップしてみます。

2020-09-16_10h19_17.png

ドロップ後、リロードしてみましょう。

2020-09-16_10h22_02.png

また、Exment側もリロードして確認してみましょう。

2020-09-15_15h07_39.png

きちんとステータスが変わっているのを確認できました!

課題もあります

まあ、ガントチャートの時ほどではないのですが、課題はあります。

縦方向の並びを変えるのが難しい

無理じゃないとは思うんですが、今回はExment側に並び順のフィールドを作ってないので、並べ替えられません。並べ替えも、並べ替えごとに番号を全部のタスクで書き換えないといけないので、API的にも負荷は大きそうです。

まとめ

というわけで、いかがでしたでしょうか。

この実装ではTrelloほど高機能な実装はしませんでしたが、簡易とはいえ、Exmentでガントチャートも、そしてカンバンも出来るとなると、これでタスク管理したくなってきませんか? もちろん、もっと作り込めば、Backlog/Redmine/Trelloに負けないタスク管理サービスを作ることも可能です。

興味持った方は、ぜひトライしてみてください!

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

Laravel8 ユーザ認証時にメールアドレス確認の処理を付与する

目的

  • LaravelのAuth認証ではメールアドレスはただ入力するだけで、入力されたメールアドレスが正しい物であるかどうかの確認がない
  • 入力メールアドレス宛にメールを送信し、初回のログインはメール内のURLからのみ行える様にする

実施環境

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

前提条件

  • 前述した実施環境に準ずる環境が整っていること。
  • Laravelアプリが作成され、アプリの起動、ブラウザからの確認ができる状態になっていること。
  • Authを用いたユーザ認証機能がすでに付与されていること。

前提情報

  • 筆者は新規でLaravel8のアプリを作成して本検証を実施する。

読後感

  • LaravelのAuth認証にメールアドレスの確認処理を付与することができる。
  • ユーザ認証情報入力→メール受信→メール内のURLから初回ログインをしてもらう。

概要

  1. .envの修正
  2. モデルファイルの修正
  3. ルーティング情報の修正
  4. コントローラファイルの修正
  5. 確認

詳細

  1. .envの修正

    1. アプリ名ディレクトリで下記コマンドを実行して.envファイルを開く。

      $ vi .env
      
    2. MAIL_MAILERの設定を下記の様に修正する。メール送信は行われず、情報がLaravelのログに出力される。

      アプリ名ディレクトリ/.env
      MAIL_MAILER=log
      
    3. 下記に修正後の.envファイルの全体の内容を記載する。

      アプリ名ディレクトリ/.env
      APP_NAME=Laravel
      APP_ENV=local
      APP_KEY=アプリキーの記載は各個人で異なります。
      APP_DEBUG=true
      APP_URL=http://localhost
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_DATABASE=DB名
      DB_USERNAME=root
      DB_PASSWORD=皆さんの環境のMySQLのrootユーザのパスワード
      
      BROADCAST_DRIVER=log
      CACHE_DRIVER=file
      QUEUE_CONNECTION=sync
      SESSION_DRIVER=file
      SESSION_LIFETIME=120
      
      REDIS_HOST=127.0.0.1
      REDIS_PASSWORD=null
      REDIS_PORT=6379
      
      MAIL_MAILER=log
      MAIL_HOST=smtp.mailtrap.io
      MAIL_PORT=2525
      MAIL_USERNAME=null
      MAIL_PASSWORD=null
      MAIL_ENCRYPTION=null
      MAIL_FROM_ADDRESS=null
      MAIL_FROM_NAME="${APP_NAME}"
      
      AWS_ACCESS_KEY_ID=
      AWS_SECRET_ACCESS_KEY=
      AWS_DEFAULT_REGION=us-east-1
      AWS_BUCKET=
      
      PUSHER_APP_ID=
      PUSHER_APP_KEY=
      PUSHER_APP_SECRET=
      PUSHER_APP_CLUSTER=mt1
      
      MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
      MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
      
  2. モデルファイルの修正

    1. アプリ名ディレクトリで下記コマンドを実行してusersテーブルにリンクするモデルファイルを開く。

      $ vi app/Models/User.php
      
    2. 下記の様に修正する。

      アプリ名ディレクトリ/app/Models/User.php
      <?php
      
      namespace App;
      
      // 下記を修正する
      //use Illuminate\Contracts\Auth\MustVerifyEmail;
      use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract;
      use Illuminate\Auth\MustVerifyEmail;
      // 上記までを修正する
      use Illuminate\Foundation\Auth\User as Authenticatable;
      use Illuminate\Notifications\Notifiable;
      
      // 下記を修正する
      //class User extends Authenticatable
      class User extends Authenticatable implements MustVerifyEmailContract
      {
          // 下記を修正する
          //use Notifiable;
          use MustVerifyEmail, Notifiable;
      
          /**
           * The attributes that are mass assignable.
           *
           * @var array
           */
          protected $fillable = [
              'name', 'email', 'password',
          ];
      
          /**
           * The attributes that should be hidden for arrays.
           *
           * @var array
           */
          protected $hidden = [
              'password', 'remember_token',
          ];
      
          /**
           * The attributes that should be cast to native types.
           *
           * @var array
           */
          protected $casts = [
              'email_verified_at' => 'datetime',
          ];
      }
      
      
  3. ルーティング情報の修正

    1. アプリ名ディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ vi routes/web.php
      
    2. 下記の様に修正を行う。

      アプリ名ディレクトリ/routes/web.php
      <?php
      
      use Illuminate\Support\Facades\Route;
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      // 下記を修正する
      //Auth::routes();
      Auth::routes(['verify' => true]);
      
  4. コントローラファイルの修正

    1. アプリ名ディレクトリで下記コマンドを実行してコントローラファイルを開く。

      $ vi app/Http/Controllers/HomeController.php
      
    2. 下記の様に修正を行う。

      アプリ名ディレクトリ/app/Http/Controllers/HomeController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      
      class HomeController extends Controller
      {
          /**
           * Create a new controller instance.
           *
           * @return void
           */
          public function __construct()
          {
              // 下記を修正する
              //$this->middleware('auth');
              $this->middleware('verified');
          }
      
          /**
           * Show the application dashboard.
           *
           * @return \Illuminate\Contracts\Support\Renderable
           */
          public function index()
          {
              return view('home');
          }
      }
      
  5. 確認

    1. アプリ名ディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. 下記リンクにアクセスする。

    3. 下記のページの右上の「REGISTER」をクリックする。

      Laravel-12.png

    4. 任意の情報を入力し「Register」をクリックする。

      Laravel-13.png

    5. 下記画面が表示されることを確認する。

      Laravel-14.png

    6. アプリ名ディレクトリで下記コマンドを実行してLaravelのログファイルを開く。

      $ vi storage/logs/laravel.log
      
    7. 開いたログファイルの最終行付近に下記の様な記載があることを確認する。

      アプリ名ディレクトリ/storage/logs/laravel.log
      [Laravel](http://localhost)
      
      # Hello!
      
      Please click the button below to verify your email address.
      
      Verify Email Address: http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161
      
      If you did not create an account, no further action is required.
      
      Regards,
      Laravel
      
      If you’re having trouble clicking the "Verify Email Address" button, copy and paste the URL below
      into your web browser: [http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161](http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161)
      
      © 2020 Laravel. All rights reserved.
      
    8. みなさんのlogファイルの「Verify Email Address」の後に書かれたURLにアクセスする。(本記事のリンクは筆者の環境の物なのでアクセスしても正常な処理にならない。)

    9. 下記の様なページが表示されたらメールアドレスの確認が完了し初回ログインも完了である。

      Laravel-16.png

参考文献

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

VSCodeのdevcontainerでLaravel・Docker環境を構築する

概要

VSCodeのdevcontainerを使ってLaravel環境構築を解説する記事です。
使用ツールさえ用意すれば高速で環境構築でき、Dockerを使用しているのでチームでの統一された開発環境としても使用できます。
とりあえず動かしたい人はリポジトリリンクからどうぞ。

リポジトリ

https://github.com/naoyayamamoto/laravel-docker-sample
テンプレートリポジトリとして用意しているので、雛形としても使用できます。

使用ツール

以上のツールを使用します。
DockerVisual Studio Codeについてはインストールの詳細を省きます。
Visual Studio Code Remote - ContainersについてはMicrosoft謹製のVSCodeプラグインです。リンクよりVSCodeにインストールできます。

使用方法

リポジトリをVSCodeで開く

$ git clone https://github.com/naoyayamamoto/laravel-docker-sample
$ code laravel-docker-sample
# codeコマンドが入っていなければVSCodeでリポジトリを開いてください

devcontanerを使用してVSCodeを開く

  1. VSCodeにVisual Studio Code Remote - Containersが入っていれば左下にアイコンが追加されているので、それを押してダイアログを開くか、Show All Commandscmd + shift + Pでダイアログを開く。
  2. Remote-Containers: Reopen in Containerを実行すると環境の準備が始まります。
  3. 初回はDockerの準備を行うため時間がかかりますが、2回目以降は高速で立ち上がります。

アクセス確認

http://localhost:8000へアクセスでLaravel初期ページが表示されるはずです。

laravel-8-top.jpg

解説

devcontainerを開いた際に.devcontainer/docker-compose.ymlを元にDocker環境が構築されます。
以下のコンテナを使用しており、それぞれ解説していきます。

  • nginx:alpine (web)
  • mysql:8 (db)
  • VSCode用にカスタマイズしたphp:7-fpm (app)

nginx:alpine (web)

  • デフォルトイメージを使用
  • フロントとして常に起動するので、php artisan serveなしでアクセス可能
  • .devcontainer/docker/nginx/default.confを読み込ませ、php-fpmへ処理を流している

mysql:8 (db)

  • デフォルトイメージを使用
  • .devcontainer/docker/mysql/my.cnfを読み込ませている(日本語基本設定)
  • 環境変数MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' MYSQL_DATABASE: laravelを設定しているので、rootパスワードなし、初期テーブルlaravelが作成される

VSCode用にカスタマイズしたphp:7-fpm (app)

  • devcontainerを開いた際の初期接続コンテナ
  • .devcontainer/docker/php/Dockerfileを使用してビルド
  • composernodenpmyarnが使用可能
  • php-fpmをsockで実行するために.devcontainer/docker/php/php-fpm.d/zzz-www.confを読み込ませている
  • DB_HOST: dbを設定しているので、.envでの設定よりこちらが優先されてdbコンテナに接続されます

おまけ

本番環境用のDockerfile

本番を想定してビルドを行うDockerfileがリポジトリに追加しているので、カスタマイズの元として使用してください。
マルチステージビルドを使用して、jsのコンパイルとcomposerのインストールを先行して行い、最終イメージを極力小さくしています。
GithubActionsでビルドテストだけは通しています。

kubernetesで使用する場合

参考までにkubernetesで使用する場合の設定です。
nginxとphpを1PODとして動かすことを想定しています。

参考deployment.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  default.conf: |
    access_log /dev/stdout main;
    error_log /dev/stderr warn;
    server {
        server_tokens off;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        gzip on;
        gzip_http_version 1.0;
        gzip_disable "msie6";
        gzip_proxied any;
        gzip_min_length 1024;
        gzip_comp_level 6;
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
        open_file_cache max=100000 inactive=20s;
        open_file_cache_valid 30s;
        open_file_cache_min_uses 2;
        open_file_cache_errors on;

        listen 80;
        root /workspace/public;

        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options "nosniff";

        index index.html index.htm index.php;

        charset utf-8;

        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }

        location = /favicon.ico { access_log off; log_not_found off; }
        location = /robots.txt  { access_log off; log_not_found off; }

        error_page 404 /index.php;

        location ~ \.php$ {
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
            include fastcgi_params;
        }

        location ~ /\.(?!well-known).* {
            deny all;
        }
    }
---
kind: Service
apiVersion: v1
metadata:
  name: laravel-service
spec:
  selector:
    app: laravel
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      name: laravel-http
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: laravel
  labels:
    app: laravel
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: laravel
  template:
    metadata:
      labels:
        app: laravel
    spec:
      containers:
        - name: app
          image: myimage
          volumeMounts:
            - mountPath: /var/run/php-fpm
              name: php-fpm-socket
            - mountPath: /shared
              name: public-contents
          lifecycle:
            postStart:
              exec:
                command: ["/bin/sh", "-c", "cp -aT /workspace/public /shared"]
        - name: web
          image: nginx:alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: /var/run/php-fpm
              name: php-fpm-socket
            - mountPath: /etc/nginx/conf.d
              name: nginx-conf
            - mountPath: /workspace/public
              name: public-contents
      volumes:
        - name: php-fpm-socket
          emptyDir: {}
        - name: public-contents
          emptyDir: {}
        - name: nginx-conf
          configMap:
            name: nginx-conf
            items:
              - key: default.conf
                path: default.conf

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

Gate権限制御 @canでクエリ発行数が増えちゃった話+対処法

なんだかものすごい権限参照のクエリ重複してる...

①対処

blade内の

@can

@endcan

の記述を

@if
@endif

へと変更し、判定に用いる権限の値をviewへと渡す。

ループ処理内でcanが使われていた場合、これが原因の可能性が高いですが以下に続きを...

②原因

@canによる判定をおこなう時にFacadeクラスのインスタンス化、権限参照のDBアクセスが発生します。
今回の自分の場合レコード情報と合わせてアイコン表示をする必要がありました。
この時、ループ処理内で表示/非表示の制御をしていたことからレコード数分のDBアクセスが起こっていました。

③対処例

自分の作業でおこなった対処の一例です。

1:①の通りbladeの記述を@can→@ifへ変更
2:controller側でユーザーインスタンスから権限、またはそれに相当する値を取得し、compactもしくはwithでviewへ渡す
3:bladeで受け取り判定に使用

大したことはしていませんが、手間をかけずにできたのでこの方法で解決できるパターンもあると思います。


参考記事

@Yorinton
https://qiita.com/Yorinton/items/604c5b1f3445cb23d565

公式
https://readouble.com/laravel/5.7/ja/authorization.html


以上となります。
仕様や現状の改善点など調べておりますので、備忘録ではありますが随時修正していきます。

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

【備忘録】Gate権限制御 @canでクエリ発行数が増えちゃった話+対処法

なんだかものすごい権限参照のクエリ重複してる...

①対処

blade内の

@can

@endcan

の記述を

@if
@endif

へと変更し、判定に用いる権限の値をviewへと渡す。

ループ処理内でcanが使われていた場合、これが原因の可能性が高いですが以下に続きを...

②原因

@canによる判定をおこなう時にFacadeクラスのインスタンス化、権限参照のDBアクセスが発生します。
今回の自分の場合レコード情報と合わせてアイコン表示をする必要がありました。
この時、ループ処理内で表示/非表示の制御をしていたことからレコード数分のDBアクセスが起こっていました。

③対処例

自分の作業でおこなった対処の一例です。

1:①の通りbladeの記述を@can→@ifへ変更
2:controller側でユーザーインスタンスから権限、またはそれに相当する値を取得し、compactもしくはwithでviewへ渡す
3:bladeで受け取り判定に使用

大したことはしていませんが、手間をかけずにできたのでこの方法で解決できるパターンもあると思います。


参考記事

@Yorinton
https://qiita.com/Yorinton/items/604c5b1f3445cb23d565

公式
https://readouble.com/laravel/5.7/ja/authorization.html


以上となります。
仕様や現状の改善点など調べておりますので、備忘録ではありますが随時修正していきます。

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

【備忘録】Laravelのエラーメッセージを日本語にしたい場合

エラーメッセージ定義があるのはよいけど日本語にならないものか...

1つずつ設定することも可能ですが、公式ドキュメントや偉大なる先人が日本語化したものがあるので簡単に変更できます。

手順

1:resources/lang 配下に日本語メッセージ用ファイルを作成
@ama_keshi様の記事を参考に作成していただくとスムーズです。お世話になりました。
https://qiita.com/ama_keshi/items/27292949d41fdd8bd930

Laravel5.7の時点では、以下のコマンド実行で日本語化用ファイルがまとめて生成できます。
https://readouble.com/laravel/5.7/ja/validation-php.html

このページの内容を含め、auth.php、passwords.php、pagination.php,validation.phpの4日本語ファイルをまとめて生成するには、以下のコマンドをプロジェクトのルートディレクトリで実行してください。(Windows環境ではまだ試していません。)

php -r "copy('https://readouble.com/laravel/5.7/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"

2:メッセージ編集
好きなメッセージへと個別に変更することも可能ですが、日本語にしたいだけであれば一括で書き換えましょう。

公式ドキュメント
https://readouble.com/laravel/5.7/ja/validation-php.html
git minoryorg様
https://github.com/minoryorg/laravel-resources-lang-ja


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