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

PHP、Laravel学習 2

ポートフォリオ作成

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

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

解決したい問題

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

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

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

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

欲しい機能

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

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

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

エラー内容を調べても解決できない時に確認したいこと

nekocyan458A4183_TP_V.jpg

エンジニアになって最初の1ヶ月、エラーが発生したらとにかく調べる習慣を付けました。

調べてどうしても解決できそうにない時には聞くようにしています。

以前、あるエラーで3時間くらい悩み、色々試した結果、

”バージョンが違っていた” ことがありました。

バージョンが違っていて、使用しているパターンが使えない可能性があるので、検索して見つけた記事が、バージョンいくつのものなのかを確認すると良いかもです。

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

PHPで短絡評価をうまく使って条件分岐をすっきり書く

論理演算をしっかり理解している人にとっては当たり前の内容だと思います。

PHPで複数の条件がある条件分岐を書くとき、、、

example.php
$con_a;//条件A boolean
$con_b;//条件B boolean
if($con_a){
  if($con_b){
    //処理1
  }else{
    //処理2
  }
}else{
  //処理2
}

このように書くと処理2を二度記述することになります。
処理2が短い処理であればこれで良いですが、長くなるとさすがに見づらくなるのでどうにかしたいです。

ここで、PHPのリファレンスを読んでみます。
https://www.php.net/manual/ja/language.operators.logical.php

document.php
<?php

// --------------------
// foo() は決してコールされることはありません。これらの演算子は短絡評価を行うからです。

$a = (false && foo());
$b = (true  || foo());
$c = (false and foo());
$d = (true  or  foo());

短絡評価とは、
「≪左辺≫ ≪論理演算子≫ ≪右辺≫」というような、論理演算子による式(論理演算子式)があるとする。左辺(第一引数)を評価した段階で式全体の値が定まらない場合のみ右辺(第二引数)を評価する評価法。
らしいです。(wikipediaより)

つまり、条件が2つ以上ある場合、1つ目の条件で式全体の結果が確定する場合は2つ目以降は考慮しない(する必要がない)ということです。
これを踏まえてexample.phpを書き換えると、

example.php
$con_a;//条件A boolean
$con_b;//条件B boolean
if($con_a && $con_b){
  //処理1
}else{
  //処理2
}

と書くことができます。

論理演算についてしっかり分かっていれば当然っちゃ当然ですが、条件が複雑になったりnot(!)がついたりするとややこしくなりますね。

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

自作したウイルススキャンのライブラリがあまりにも残念だったので、interfaceを使って少しでもましにしたい

みなさんこんにちは。

昔clamav使ったファイルのウイルススキャンをやったことがあって、それを思い出そうと思って適当なライブラリを作ったのですが、本当に適当すぎる作りになっちゃって、いや、これこのままだとさすがに恥ずかしすぎるやろ、って思いまして、ちょいと手直しすることにしたのです。
手直ししているときに、どんなふうに直すか、だけでなく、どんな風に使ってもらえるかを考えていたら、やっぱりinterface用意しておくのが都合がいいね、って感じになりました。

というわけで、interface を使ってよりユーザフレンドりーなライブラリを目指してみましょう。

interface

はじめにinterfaceの解説をするので、不要な人は飛ばしてください。

interfaceとは

PHPにおいてinterfaceとは、あるクラスを定義するときに必要な公開メソッドをまとめたもの、です。
こんな風に書きます。

Sequence.php
<?php

interface Sequence
{

    public function factorial(int $num): int;

    public function permutation(int $num, int $factors): int;
}

ここには、単純な階乗と順列の計算をするメソッドが定義されていますが、動作の中身はありません。中身は別に実装する必要があります。

RecursiveSequence.php
<?php
require_once './Sequence.php';

class RecursiveSequence implements Sequence
{

    public function factorial(int $num): int
    {
        if ($num === 0) {
            return 1;
        }

        return $num * $this->factorial($num - 1);
    }

    public function permutation(int $num, int $factors): int
    {
        return $this->factorial($num) / $this->factorial($num - $factors);
    }
}

interfaceの中身を実装したクラスに対しては、implements Sequence のように書かきます。
このような class <class_name> implements <interface_name>と書いたとき、<class_name> は <interface_name> の実装であるといいます。
あとは、これを使うだけ。

test.php
<?php

require './Sequence.php';
require './RecursiveSequence.php';

$obj = new RecursiveSequence;

echo $obj->factorial(1), "\n";
echo $obj->factorial(3), "\n";
echo $obj->factorial(10), "\n";

echo $obj->permutation(3, 2), "\n";
echo $obj->permutation(5, 3), "\n";
echo $obj->permutation(10, 5), "\n";

動作させると

# php test.php 
1
6
3628800
6
60
30240

となります。

interfaceの特徴

大雑把には以下のような特徴があります。

  • 型宣言に使える
  • interfaceをimplementsしたクラスは、interfaceで宣言したメソッドをすべて実装していないとエラーが出る
  • interfaceも継承できる
  • implementsに複数のinterfaceを指定できる

interfaceを型宣言に使える

普通のクラス名と同じく、interfaceも型宣言に使えます。

メソッドの返り値

Factory.php
<?php
require_once './Sequence.php';
require_once './RecursiveSequence.php';

class Factory
{
    public function create(): Sequence
    {
        return new RecursiveSequence;
    }
}

一方で、引数でも使えて

test.php
<?php

require './Sequence.php';
require './Factory.php';

$obj = (new Factory)->create();
test($obj);

function test(Sequence $obj)
{
    echo $obj->factorial(1), "\n";
    echo $obj->factorial(3), "\n";
    echo $obj->factorial(10), "\n";

    echo $obj->permutation(3, 2), "\n";
    echo $obj->permutation(5, 3), "\n";
    echo $obj->permutation(10, 5), "\n";
}

こんな風になります。

PHP7.4以降で追加されるプロパティの型宣言でも、interfaceを使うことができます。

<?php

require_once './Sequence.php';
require_once './RecursiveSequence.php';

class Some
{
    private Sequence $obj;

    public function __construct()
    {
        $this->obj = new RecursiveSequence;
    }

    public function getObj()
    {
        return $this->obj;
    }
}

$some = new Some;

echo get_class($some->getObj()), "\n";
// RecursiveSequence

宣言されたメソッドがそろっていないとエラー

interfaceをimplementsしたクラスは、すべてのメソッドを不足なく実装する必要があります。
以下のような場合はコンソールで実行するとエラーが出ます。

dame1.php
<?php

require_once './Sequence.php';

/**
 * Sequence の permutationが実装されていないパターンん
 */
class Dame1 implements Sequence
{
    public function factorial(int $num): int
    {
        return 1;
    }
}

$obj = new Dame1;
// Fatal error: Class Dame1 contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Sequence::permutation) in /var/www/dame1.php on line 5
dame2.php
<?php

require_once './Sequence.php';

/**
 * Sequence の permutationが実装されていないパターンん
 */
class Dame2 implements Sequence
{
    public function factorial(int $num): int
    {
        return 1;
    }

    public function permutation(int $num, int $factors): string
    {
        return "1";
    }
}

$obj = new Dame2;
// Fatal error: Declaration of Dame2::permutation(int $num, int $factors): string must be compatible with Sequence::permutation(int $num, int $factors): int in /var/www/dame2.php on line 15

継承できる

interfaceを継承することもできます。
別のクラスを継承しつつ、implementsを指定することもできます。

test2.php
<?php

require_once './Sequence.php';
require_once './RecursiveSequence.php';

interface OtherSequence extends Sequence
{

}

class Test2 extends RecursiveSequence implements OtherSequence
{

}

$obj = new Test2;
echo $obj->permutation(10, 3), "\n";
// 720

implementsに複数のinterfaceを指定できる

クラス定義のimplementsには、複数のinterfaceを指定することができます。

test4.php
<?php

interface Factorial
{
    public function factorial(int $num): int;
}

interface Permutation
{
    public function permutation(int $num, int $factors): int;
}

// 複数のinterfaceを実装できる
class Some implements Factorial, Permutation
{
    public function factorial(int $num): int
    {
        return 1;
    }

    public function permutation(int $num, int $factors): int
    {
        return 1;
    }
}

$some = new Some;

test($some);//  Factorial
test2($some);// Permutation

function test(Factorial $obj)
{
    echo 'Factorial', "\n";
}

function test2(Permutation $obj)
{
    echo 'Permutation', "\n";
}

ここでSomeは、FactorialであってPermutationでもあるということになります。

自作ライブラリにinterfaceを!

clamAVを使ったウイルススキャン

これですね。
https://github.com/niisan-tokyo/web-clamav-php/
前回の記事用に作ったリモートのclamdサーバにウイルススキャンしてもらうライブラリです。
https://qiita.com/niisan-tokyo/items/798c945ab4da26c31d16

旧実装の問題点

何が問題だったかって、ここですよね

$manager = new Niisan\ClamAV\Manager(['url' => 'clamav']);

このManagerの中身はリモートのclamdサーバにファイルを分割して投げるという、実装になっています。しかし、ウイルススキャンの方法は前回の記事でみたように、少なくとも2つはあります。

リモートでスキャン
ローカルでスキャン
つまるところ、このようなManagerの実装をそのまま使うコードだと、別実装を使おうとしても、すべてのコードの中で、このManagerを使っている部分を探し出し、置き換え、さらに動作も初めから見直す必要があります。
さらに自作せにゃならんとなれば、その実装まではいるので、ことさら面倒です。

interfaceを使って実装の陰を消す

とりあえず、実装を直に使っている状況はよろしくないということで、interfaceを通して使うようにしましょう。interfaceを通しておけば、そのinterfaceでやりたいこと、つまり、存在するメソッドとその使い方がわかります。
今回はウイルススキャンなので、こんな感じに作ってみます。

src/Scanner.php
<?php
namespace Niisan\ClamAV;

interface Scanner
{

    /**
     * Ping to scanner server or socket
     * 
     * When the scan server is not connected,
     * throw RuntimeException.
     *
     * @return boolean
     * @throws \RuntimeException 
     */
    public function ping() :bool;

    /**
     * Scan a file.
     * 
     * When the file have some virus, this method return false,
     * otherwise return true.
     *
     * @param string $file_path
     * @return boolean
     */
    public function scan(string $file_path): bool;
}

接続確認のpingと、ファイルスキャンするscanがあればいいといった感じですね。
次にこれを生成するクラスとしてFactoryを実装します。

src/ScannerFactory.php
<?php
namespace Niisan\ClamAV;

use Niisan\ClamAV\Scanners\RemoteScanner;

class ScannerFactory
{
    public static function create(array $config): Scanner
    {
        return new RemoteScanner($config);
    }
}

createの返り値をScannerにしておきます。

さらに、前回作成したManagerとやらを、Scannerを実装したRemoteScannerとして定義しなおします。

src/Scanners/RemoteScanner.php
<?php
namespace Niisan\ClamAV\Scanners;

use Generator;
use Niisan\ClamAV\Scanner;

class RemoteScanner implements Scanner
{

    private $port = 3310;
    private $url;

    public function __construct(array $option)
    {
        if (empty($option['url'])) {
            throw new \RuntimeException('ClamAV server host is not input.');
        }

        $this->url = $option['url'];
        $this->port = $option['port'] ?? $this->port;
    }

    /**
     * @inheritDoc
     */
    public function ping(): bool
    {
        // 何かの処理
        return true;
    }

    /**
     * @inheritDoc
     */
    public function scan(string $file_path): bool
    {
        // 何かの処理
        return $this->checkMessage($message, 'OK');
    }
}

そして、先にManagerをnewしていた部分をFactoryで置き換えます。

$scanner = \Niisan\ClamAV\ScannerFactory::create([
    'host' => 'example.com'
]);

一見して、newしていたところをScannerFactory::create()に変えただけですが、この関数の返り値の型がScannerとなっていますので、このコード中の$scannerは、とりあえずpingscanが実装された何か、という状態になります。

実装を追加して好きなほうを選べるようにする

さて、このライブラリはリモートのclamdサーバを使うことを前提にしていましたが、サーバ一台しかないとかで同じサーバにclamdを動作させている場合もあると思い、それ用の実装を用意してみましょう。

src/Scanners/LocalScanner.php
<?php
namespace Niisan\ClamAV\Scanners;

use Niisan\ClamAV\Scanner;

class LocalScanner implements Scanner
{

    private $path;

    public function __construct(array $options = [])
    {
        if (! isset($options['path'])) {
            throw new \RuntimeException("Socket path not given, i.e. ['path' => /var/run/clamav/clamd.ctl");
        }

        $this->path = $options['path'];
    }

    /**
     * @inheritDoc
     */
    public function ping(): bool
    {
        // いろいろな実装
        return true;
    }

    /**
     * @inheritDoc
     */
    public function scan(string $file_path): bool
    {
        // いろいろな実装
        return $this->checkMessage($message, 'OK');
    }

この実装もScannerの実装になっているので、もともとあったRemoteScannerと同じように使えるはずです。
新しくできた実装を使うために、Factoryを改造します。

src/ScannerFactory.php
<?php
namespace Niisan\ClamAV;

use Niisan\ClamAV\Scanners\RemoteScanner;
use Niisan\ClamAV\Scanners\LocalScanner;

class ScannerFactory
{
    public static function create(array $config): Scanner
    {
        return ($config['driver'] === 'local') ? new LocalScanner($config): new RemoteScanner($config);
    }
}

あとは、Factoryを使っているところで

$scanner = \Niisan\ClamAV\ScannerFactory::create([
    'driver' => 'remote',
    'host' => 'example.com'
]);

こんな感じで使えます。
このFactoryからはLocalScannerRemoteScannerの二つをとりうるわけですが、同じinterface Scanner の実装になっているので、少なくとも pingscanのみを使っている限りは、使い方は保証されています。

最終的な使用法

この前とあまり変わらないですが、こんな感じです。

composer require niisan-tokyo/web-clamav-php:v0.2.1

でライブラリをインストールするなり更新するなりしてから

index.html
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <h1>ファイルを送信</h1>
    <form action="file.php" enctype="multipart/form-data" method="POST">
        <input type="file" name="upfile" /><br>
        <button type="submit" name="go">送信する</button>
    </form>
</body>
file.php
<?php
require 'vendor/autoload.php';

use Niisan\ClamAV\ScannerFactory;

$file = $_FILES['upfile'];

$scanner = ScannerFactory::create([
    'driver' => 'remote',
    'url' => 'clamav'
]);

$result = true;
if ($file) {
    $start = microtime(true);
    $result = $scanner->scan($file['tmp_name']);
    $resTime = microtime(true) - $start;
}

?>
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
</head>
<body>
<h1>問題<?php if ($result) {?> なし <?php } else {?> あり <?php } ?></h1>
時間: <?php echo $resTime ?><br>
    <a href="index.html">戻る</a>
</body>

でいけます。
もしも、ローカルのclamdサーバで使うときは

$scanner = ScannerFactory::create([
    'driver' => 'local',
    'path' => '/var/run/clamav/clamd.ctl'
]);

としてやればよいです。

まとめ

こんな感じで、interfaceを使ってライブラリの使い勝手を少しだけ上げてみました。
ここまでくればDIとかもできるので、そっちへの応用も記事化しようかなって思います。

今回はこんなところです。

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

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で続きを読む

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で続きを読む

PHP コードレビュー嵐に苦しむ社内 SE が,最近感じること。ちょっとしたお作法で,コードをグーンと読みやすくするエチケット

はじめに

社内 SE という職業柄,社外へシステムを発注することは多く,ビジネスパートナーのエンジニアが書いたコードをレビューする機会が日常的にあります。

その中で,「こう書いてくれた方が,もっと,楽に読めるんだけどなぁ」と感じる,口惜しいポイントがいくつかあることを日頃から感じており,記事としてアイデアをシェアしてみようと思いました。

コードレビューでは NG 判定にするまでもない小さなことですが,ちょっとしたお作法で,読み手の負担を下げられる事例の紹介になると思います。

余分な変数は切らない

コードをどれだけ速く読めるかは,エンジニアの器量にも大きく依存するところですが,コードの質,とりわけ,変数の個数は,思考作業の大きな負荷となります。

なぜなら,変数が一度見つかると,スコープが終わるまでは,その挙動を頭の片隅に置く必要があるため,考えることが増えてしまうからです。

出番が一回しかないような変数は作らないというルールを守るだけでも,コードの印象が,ものすごくスッキリしたものになります。

【追記】読者の皆さんのアドバイスを盛り込んで,改善案を追加しました(ありがとうございました)

// 2行に分けて書く方法(出番が一回しかない変数 $emp_id がある)
$emp_id = $this->input->post('emp_id');
$emp_name = $this->getEmpName($emp_id);

// 1行でまとめて書く方法(一行に収まるが,横太になって,ちょっと読みづらくなる)
$emp_name = $this->getEmpName($this->input->post('emp_id'));

// 1行でまとめて書く方法(改善版)
$emp_name = $this->getEmpName(
    $this->input->post('emp_id')
);

無意味な初期化はしない

変数を初期化するコードをよく見かけますが,意味のないケースが散見されます。

数値型だったらゼロ,文字列型だったら null で初期化しておけば,なんとなく安心できる心理効果はあるものの,消しても挙動に影響を与えない初期化は,使わないようにします。

// 意味のない初期化
$age = 0;
$age = $this->getAge();

// こうすればいいだけ
$age = $this->getAge();

連想配列は「母数」の分かる書き方を

連想配列って,多くの定義方法がありますね。

これも,書き方ひとつで,コードを読むときの思考負荷が大きく変わります。

読み手が分かりやすいよう,「母数はこれだけ」というメッセージを込めた定義の仕方が,スッキリします。

// 思考負荷が高まってしまう書き方(要素がまだまだ増えるかもしれないと,不安になる)
$employee['name'] = 'Yamada';
$employee['age'] = 24;
$employee['address'] = 'Tokyo';

// 思考負荷を低減できる書き方(要素が3つだというメッセージが込められている)
$employee = [
    'name' => 'Yamada',
    'age' => 24,
    'address' => 'Tokyo',
];

まとめ

本記事でご紹介した例は,ちょっとした工夫ですが,積み重なると,コードの読みやすさを大きく改善できる好例だと思いますが,いかがでしたでしょうか?

コードの読み手に与える負荷を少しでも低減するという配慮は,将来の自分自身のためにもなります(一か月後の自分は,他人のようなものですから)。

日々の小さな積み重ねで,幸せなコーディング体験が得られるようにしたいですね。

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