Help us understand the problem. What is going on with this article?

Laravel TBL(ちょっと便利になるライブラリ)の紹介

More than 1 year has passed since last update.

はじめに

GitHubにあげるまでもない。作った!とドヤるほどでもない。
誰が作ったかしらんけど、しれっとある便利機能って、みなさんのプロジェクトでもありますよね。
今回は弊社で作った、Laravelで開発するにあたりちょっと楽になるプログラムを掲載したいと思います。

クエリーログ

EloqentORMは非常に便利ですし、QueryBuilderも直感的に使えます。しかし、「実際はどんなSQLになってるんだろう」「意図した結果取れないけど生SQL見たいな」ってこともあると思います。
Laravelには laravel-debugbarもありますが、たとえばPHPUnitでモデルのテストを書いているときに、いちいち

$builder = $this->where('status', $status)
    ->when($role, function ($query) use ($role) {
        return $query->where('role_id', $role);
    })
    ->orWhere(function ($query) {
        $query->where('votes', '>', 100)
            ->where('title', '<>', 'Admin');
    })
    ->orderBy('name', 'desc');

var_dump($builder->toSql());

みたいなことしたくないです。

QueryLogger

クエリーをログに書き出すマンです。コピペでどうぞ。
あとはこのServiceProviderをLaravelならconfig/app.phpprovidersに追加すればいいし、Lumenならbootstrap/app.php$app->register(App\Providers\QueryLogServiceProvider::class);と書けば、自動でクエリーログがどしどし出力されます。

<?php
declare(strict_types=1);

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Class QueryLogServiceProvider
 * @package App\Providers
 */
class QueryLogServiceProvider extends ServiceProvider
{
    /**
     * @var array ログ出力する環境
     */
    private $loggingEnvironments = [
        'local',
        'testing',
    ];

    /**
     * queryログ出力設定
     */
    public function boot()
    {
        DB::listen(function ($query) {
            if (in_array(env('APP_ENV'), $this->loggingEnvironments, true)) {
                $queryLog = 'QueryLog[' . $query->time . 'ms] ' . $query->sql;
                if (!empty($query->bindings)) {
                    $queryLog .= ' (bindings:' . implode(', ', $query->bindings) . ')';
                }
                Log::info($queryLog);
            }
        });
    }
}

シーダーでCSVを使いたい

Laravelでデータベースのテストを書くとき、モデルファクトリは非常に便利な機能です。
しかし、ときにはExcelなどでテストデータをつくり、それを使ってテストをしたいときもあるでしょう。その際、シーダーをPHPUnit内でコールしてデータをINSERTすると楽ちんです。

BulkCsvSeeder

CSV形式のテストデータをよしなにバルクインサートしてくれるマンです。これを基底クラスとしてseederを書きます。コピペでどうぞ。

<?php
declare(strict_types=1);

namespace Database\seeds;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use SplFileObject;

/**
 * Class CsvBulkSeeder
 */
abstract class CsvBulkSeeder extends Seeder
{
    /**
     * @const string 変換前文字コード
     */
    private const CHARACTER_CODE_SHIFT_JIS = 'SJIS';

    /**
     * @const string 変換後文字コード
     */
    private const CHARACTER_CODE_UTF = 'UTF-8';

    /**
     * @const int カラム名が入っている行数
     */
    private const COLUMN_NAME_ROW_NUMBER = 1;

    /**
     * @const string CSVファイルが置いてあるディレクトリ
     */
    private const CSV_PATH = '/csvFiles/';

    /**
     * @var string 対象のCSVファイル
     */
    protected $csvFileName = '';

    /**
     * @var string INSERTするテーブル名
     */
    protected $tableName = '';

    /**
     * @var array nullに変換するカラム番号群
     */
    protected $nullConvertNumber = [];

    /**
     * @var null|string コネクション名
     */
    protected $connectionName = null;

    /**
     * CSVをバルクインサートする
     * @param bool $isShiftJis
     */
    protected function seed(bool $isShiftJis = false)
    {
        $filePath = database_path() . self::CSV_PATH . $this->csvFileName;
        $splFileObject = new SplFileObject($filePath);
        $splFileObject->setFlags(SplFileObject::READ_CSV);
        $rowCounter = 0;
        $columnNames = [];
        $insertRecords = [];

        foreach ($splFileObject as $line) {
            $rowCounter++;
            if (empty(array_filter($line))) {
                break;
            }
            if ($rowCounter === self::COLUMN_NAME_ROW_NUMBER) {
                $columnNames = $line;
                continue;
            }
            foreach ($this->nullConvertNumber as $columnNumber) {
                if (
                    $line[$columnNumber] === 'NULL' || $line[$columnNumber] === 'null' || $line[$columnNumber] === ''
                ) {
                    $line[$columnNumber] = null;
                }
            }
            if ($isShiftJis) {
                mb_convert_variables(self::CHARACTER_CODE_UTF, self::CHARACTER_CODE_SHIFT_JIS, $line);
            }
            $insertRecords[] = array_combine($columnNames, $line);
        }
        if (!empty($insertRecords)) {
            if (!is_null($this->connectionName)) {
                DB::connection($this->connectionName)->table($this->tableName)->insert($insertRecords);
                return;
            }
            DB::table($this->tableName)->insert($insertRecords);
        }
    }

    /**
     * 実行処理のabstract
     */
    abstract public function run();
}

実際のseederはこんなかんじ。

<?php
declare(strict_types=1);

namespace Database\seeds;

/**
 * Class SampleSeeder
 */
class SampleSeeder extends CsvBulkSeeder
{
    /**
     * @var string
     */
    protected $csvFileName = 'sample.csv';

    /**
     * @var string
     */
    protected $tableName = 'samples';

    /**
     * @var array
     */
    protected $nullConvertNumber = [3, 4];

    /**
     * シード実行
     */
    public function run()
    {
        $this->seed();
    }
}

あとはPHPUnit内でArtisan::call('db:seed', ['--class' => 'SampleSeeder']);
とかくだけで、INSERTされます。

まとめ

みなさんのプロジェクトでもTBL(ちょっと便利になるライブラリ)はありますか?
ないなら、ちょっと作ってみる事をおすすめします。意外とフレームワークの中身の勉強になったりすることが多いです。

素敵なLaravelライフを!

k_takaya
Laravel大好きおじさん
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした