20191130のlaravelに関する記事は7件です。

composer dump-autoload

半日ほど悩まされました。

まずは経緯。

$ php artisan db:seed

こちらを実行しようとすると、

ReflectionException : Class EntriesTableSeeder does not exist

こちらのエラーが。
ただ、少し調べると、

$ php composer.phar dump-autoload

を実行すれば解決できるとのこと。
しかし結果は...

Could not open input file: composer.phar

何故このエラーが出たかというと...
プロジェクトディレクトリの中で実行していなかったんですね。
...そりゃそうだ。

という訳で、composer.pharをプロジェクトディレクトリの中に移動してあげてから、
$ php composer.phar dump-autoload
を実行してあげたら解決出来ました。

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

【Laravel】画面で指定したソート条件をページング先でも反映させる

画面上でボタンなどでソート条件を指定→ページング先でも反映させるための一連の手順です。

やりたいこと

image.png

環境

  • PHP:バージョン7.3.7
  • Laravel:バージョン5.8
  • OS:Windows10

手順

1. ページング機能の作成

まずはページング機能を作成します。
ControllerとViewを編集するだけなので、わりと簡単。

(1)Controllerの編集

全件を取得し、1ページ当たり5データ表示させる場合は以下の通り。
pagenate()のカッコ内の数字を任意の数字に変えましょう。

TestController.php
    public function initialize(){

        $test_forms = TestForm::all(); // テーブル:test_formsから全件を取得
        $test_forms = TestForm::paginate(5); // 1ページに表示されるデータを5件に設定
        return view('test_list', ['test_forms' => $test_forms]);

    }

(2)Viewの編集

Viewにページングを効かせる記述を追加します。
アロー演算子(->)の左辺にはControllerで渡した変数を記述。

test_list.blade.php
 {{ $test_forms->links() }}

2. ソート機能の追加

続いてソート機能。
専用のプロジェクトを追加するので、ページングよりは少し手間がかかります。

(1)ソート用パッケージのインストール

ソート用パッケージ、kyslik/column-sortableをインストールします。
コマンド実行場所は作成したLaravelプロジェクト直下です。

> composer require kyslik/column-sortable

(2)ソート用パッケージの設定ファイルの作成

続いて設定ファイルの作成。
こちらもコマンド実効場所はプロジェクト直下。

> php artisan vendor:publish --provider="Kyslik\ColumnSortable\ColumnSortableServiceProvider" --tag="config"

(3)config/app.phpへの追記

config/app.phpのproviders配列に以下の通り追記します。
これで、今回インストールしたサービスプロバイダクラスが読み込まれるようになります。

config/app.php
    'providers' => [

        Kyslik\ColumnSortable\ColumnSortableServiceProvider::class, //追加

    ],

(4)モデルの編集

モデルクラスにソート対象を記述します。
「public $sortable =」に、ソート対象を配列形式で。
※要素は後の手順で出てくるViewの@sortablelink()の第1引数に一致させます。

TestForm.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Kyslik\ColumnSortable\Sortable; // 追加

class TestForm2 extends Model
{

    use Sortable; // 追加
    public $sortable = ['id', 'name', 'created_at', 'updated_at']; // 追加

}

(5)Controllerの編集

Controllerの「paginate()」の前に「sortable()->」を追記します。

TestController.php
    public function initialize(){

        $test_forms = TestForm::all();
        $test_forms = TestForm::sortable()->paginate(5); // sortable()->を追記
        return view('test_list', ['test_forms' => $test_forms]);

    }

(6)Viewの編集

①ソート対象を@sortablelink()で記述
  • 第1引数:ソート対象のカラム名
  • 第2引数:画面に表示する文字列(省略するとカラム名が表示される)

を入力します。必要に応じて、

  • 第3引数:デフォルトのクエリストリング(省略可)
  • 第4引数:追加のアンカータグの属性(省略可)

も入力しましょう。

test_list.blade.php
@section('content')
    <table class = "table">
        <thead>
            <tr>
                <td>@sortablelink('id', 'ID')
                <td>@sortablelink('name', '名前')
                <td>@sortablelink('created_at', '作成日')
                <td>@sortablelink('updated_at', '更新日')
        </thead>
        @foreach($test_form2s as $test_form2)
        <tbody>
            <tr>
                <td>{{ $test_form2->id }}
                <td>{{ $test_form2->name }}
                <td>{{ $test_form2->created_at }}
                <td>{{ $test_form2->updated_at }}
        </tbody>
        @endforeach
    </table>
②ページングを効かせる記述を変更 →【ココが重要!】

ページング機能作成時にViewに記述した「 {{ $test_forms->links() }}」を以下のように修正します。
「->appends(request()->query())->」を追加してあげることで、ページングで次のページの内容を読み込んでも画面でしていたソート条件が反映されます。
※appendsのsを忘れないようにしましょう。

test_list.blade.php
{{ $test_forms->appends(request()->query())->links() }}

参考

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

【Laravel】自作artisanコマンド実行中のログを保存する方法

はじめに

Laravelで自作artisanコマンド実行中の様子をstorage\logs内にログファイルとして保存したい場合の方法を書いてみます。

artisanコマンドを生成

   $ php artisan make:command HelloCommand

app\HelloCommand.phpを以下のように編集

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class HelloCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'HelloCommand';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        createHelloLog('ログが出力されました!');
    }
}

自作関数を作成

app\helper.phpを作成して編集
関数の中身は、こちらの記事を参考にさせていただきました。

[Laravel] ログの扱い方 [5.8]

helper.php
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;

if (!function_exists('createHelloLog')) {

  function createHelloLog($data)
  {
    $log_path = storage_path('logs/hello.log');
    $days = 90;
    $log_level = 'debug';
    $bubble = true;
    $filePermission = 0777;
    $handler = new RotatingFileHandler($log_path, $days, $log_level, $bubble, $filePermission);
    $formatter = new LineFormatter(null, null, true, true);
    $handler->setFormatter($formatter);

    $logger = new Logger('hello');
    $logger->pushHandler($handler);
    $logger->info($data);
  }
}

エラーを出力して保存した場合はエラー用の関数を用意して、$logger->info($data);の部分を$logger->error($data);とするといいですね!

オートローダの設定

自作関数を読み込むためにオートローダの設定をします。
composer.json"files"以下を追記

composer.json
"autoload": {
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "psr-4": {
            "App\\": "app/"
        },
        "files": [
            "app/helpers.php" 
        ]
    }

composer dumpautoloadを実行

$ composer dumpautoload

実行してみる

$ php artisan HelloCommand

storage\logs\hello-xxxx-xx-xx.logが生成されているので中身を確認
スクリーンショット 2019-11-30 19.29.19.png
きちんと保存されていますね!

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

AWS Cognito ✗ Laravelを使った会員機能構築の話

はじめに

ここ3ヶ月程、某WEBサイトに会員機能+決済機能を追加するお仕事をしてました。
基本的に設計〜リリースまで自分主担当で進めるにあたり、なんか便利な外部サービスないかなと目をつけたのがAWS Coginitoでした。

導入対象のシステム構成

インフラはAWSを使っており、サーバーサイド&フロントはphp×Laravel構成です。

導入の背景

リリースまで3ヶ月、運用を楽にしたい、個人情報周りを自社で持つようなことをしたくないというのを軸にいい感じのサービスないかなーと探しておりました。
AWSでいい感じのマネージドサービスあるんじゃないかと思って見つけたCognito。さっとドキュメント読んだ感じ、ソーシャルIDプロバイダーを介したサインインや多要素認証(MFA)といった機能もあり導入を決めました。

検証・設計時に苦労したこと

cognitoで検索すると結構な数の記事が出てきたのですが、どれもAWS Amplifyを使ったモダンな利用方法ばかり。。。自分のようにサーバーサイドで使う例があまり見当たらず、また公式ドキュメントもamplifyを使った例しか見つけれず中々時間がかかりましたが、
aws-sdk-phpのCognitoIdentityProviderClientを利用すればできそうなところまでなんとかあたりをつけられました。

Cognito側の設定

まずはマネジメントコンソールからUserPoolsを作成します。
で出来上がったのがこちら。
スクリーンショット 2019-11-30 14.19.55.png
スクリーンショット 2019-11-30 14.26.04.png

CognitoIdentityProviderClientのAPI呼び出しにおいて、プールID(UserPoolId)およびアプリクライアントID(ClientId)を指定しなければならないものもあるため確認しておきます。
また、トークンの有効期限を更新(日)の項目もAPI呼び出しに必要なTokenの有効期限を設定するもののため適切な値に適宜設定すると良いと思います。

サーバーサイドからのCognitoAPI呼び出し

私が主に使用したのは認証・取得・更新のAPIになります。
それぞれ下記のような形で呼び出せます。

sample.php
$email = $request->input('email');
$password = $request->input('password');

$client = new CognitoIdentityProviderClient([
            'profile' => 'default',
            'region'  => 'ap-northeast-1', // 東京リージョンの場合
            'version' => '2016-04-18'
        ]);
$result = $client->initiateAuth([
    'AuthFlow' => 'USER_PASSWORD_AUTH',
    'ClientId' => 'アプリクライアントID',
    'ClientMetadata' => ['USERNAME' => $email, 'PASSWORD' => $password],
]);

レスポンスはこんな感じです。

[
    'AuthenticationResult' => [
        'AccessToken' => '<string>',
        'ExpiresIn' => <integer>,
        'IdToken' => '<string>',
        'NewDeviceMetadata' => [
            'DeviceGroupKey' => '<string>',
            'DeviceKey' => '<string>',
        ],
        'RefreshToken' => '<string>',
        'TokenType' => '<string>',
    ],
    'ChallengeName' => 'SMS_MFA|SOFTWARE_TOKEN_MFA|SELECT_MFA_TYPE|MFA_SETUP|PASSWORD_VERIFIER|CUSTOM_CHALLENGE|DEVICE_SRP_AUTH|DEVICE_PASSWORD_VERIFIER|ADMIN_NO_SRP_AUTH|NEW_PASSWORD_REQUIRED',
    'ChallengeParameters' => ['<string>', ...],
    'Session' => '<string>',
]

この中で重要なのがAccessTokenとRefreshTokenになります。
AccessTokenは認証後にユーザー情報の取得や更新等他のAPIを呼び出す際にKeyとして利用します。ただし、有効期限は発行から1hです。
次にRefreshTokenですが、AccessTokenおよびIdToken(今回は利用していない)の再発行に利用するためのTokenです。
こちらの有効期限は前述の トークンの有効期限を更新(日)で設定した期間になります。
ちなみに取得したtoken達はLaravelのsessionで保管をしています。

AccessTokenを使ってユーザー情報を取得するのはこんな感じになります。

getuser.php
$accesstoken = session('access_token');
$result = $client->getUser([
    'AccessToken' => $accesstoken,
]);

まとめ

自身が調査しているときは情報が中々出てこなくて多少時間がかかりましたが、この記事を書いてる時点でととても詳しく書かれているサーバーサイド✕Cognitoの記事もいくつか見つけられました。
システム構成上しかたなくでしたが、スクラッチで作るのであればやはりamplifyの方がもっと便利で簡単にできるかもしれません。
ある程度出来上がってしまっているシステムに対してCognitoを導入する際の参考に少しでもなれれば幸いです。

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

Laravelの認証機能ui(外部パッケージライブラリ)導入

ターミナルでインストール

composerを使ってuiライブラリをインストールする

$ composer require laravel/ui

Package manifest generated successfully. と表示されたらOK

artisanコマンドでvueを追加

$ php artisan ui vue --auth

スタイルシートが壊れた場合は、npmから不足するパッケージをインストールすることで修復する

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

Using an encrypted column for auth login in Laravel.

Everybody wants to encrypt personal information.
That is the way of the world isn't that?
Anyway, I wrote down how to make it come true.

Introduction

This time, I encrypt 'email' column and use it for auth login.

Encryption is easy because you use just encrypt method, however you can't login because values don't be matched in attempt method.

So that I override attempt method to decrypt email column which was encrypted in its.

Environment

Target Version
PHP 7.3.12
Laravel 6.2

Encrypt when create

app/Http/Controllers/Auth/RegistraterController.php
    protected function create(array $data)
    {   
        return User::create([
            'email' => encrypt($data['email']),
            'password' => Hash::make($data['password']),
        ]); 
    }   
}

Add Attributtion method on Model

You always get decrypted value when you use below.

appModels/User.php
      public function getEmailAttribute($value)
      {                        
          return decrypt($value);
      } 

Override attemptLogin method

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

namespace App\Http\Controllers\Auth;

use App\Models\User; // Add
use Illuminate\Http\Request; // Add
use Illuminate\Support\Facades\Hash;  // Add
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller

...

    // Override to use decrypt column for attempt.
    protected function attemptLogin(Request $request)
    {            
        $users = User::all();
        $isUserValidated = false;
        $field = $request->email;
        foreach ($users as $user) {
            try {
                if ( $field === $user->email && Hash::check($request->password, $user->password) ) { 
                    $isUserValidated = true;
                    $this->guard()->login($user, false);
                    break;
                }
            } catch (DecryptException $e) {
                // do something you want
            }
            return $isUserValidated;
        }
    }
}   

References

Encrypted email validation in laravel

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

Laravel ルーティング設定

Laravelのルーティング設定

web.phpの設定

ディレクトリ内の routes → web.php

まず、useの処理を追記

<?php

// useはディレクトリにショートカットを作成するという意味
use App\モデル名;
use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| 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('テンプレートファイル(blade)の最初の単語名');
});

デフォルトは、return view('welcome');となっているので、

テンプレートファイル(blade)の最初の単語名に書き換える。

変数の定義

1.データベースからデータを取得して値を返す処理を追記
2.Bladeテンプレートで参照する変数名を引数に設定

Route::get('/', function () {
    $変数名 = モデル名::all();
    return view('テンプレートファイル(blade)の最初の単語名', ['変数名' => $変数名 ]);
});

フォームから入力したデータを受け取る

Route::post('formのactionに指定したURI(パス)', function (Request $request) {
    $validator = Validator::make($request->all(),[
        'name' => 'required|max:255',
    ]);

    // データベースに登録する値の変数を宣言して、モデルのクラス定義からオブジェクトを作成
    $変数名 = new モデル名;
    $変数名->title = $request->name;
    $変数名->save();

    return redirect('/');

登録したデータを削除する

implicit binding(暗黙のバインディング)
→オブジェクトのID番号を返す処理

URIとidを同じ名前にすると、一致するインスタンスを返してくれる

Route::delete('URI/{id}', function(モデル名 $変数名){
    $変数名->delete();

    return redirect('/');
});

スクリーンショット 2019-11-30 9.50.41.png
※Laravelマニュアルより引用

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