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

artisanコマンドの自作で引数付きのSeederを作成する

あらすじ

Laravelにはseederがありテスト時などにサンプルデータを気軽に作ることができます。
ただartisanコマンドを用いて呼び出すのですが、この時にshellscriptのように作成するデータ数などを引数として与えられたらなあ...と思っていました。
そんなところartisanのコマンド自作で行けそうといアドバイスをいただいたので、コマンド作成の練習もかねて作成してみることにしました。

通常のSeeder

今回は例として、Laravelのプロジェクトに最初からついてくるUserテーブルのSeederをもとに作っていきます。似た構造のレコードを複数追加するのでFactoryを記述して(されていて)、

Userfactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use Illuminate\Support\Str;
use Faker\Generator as Faker;

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
    ];
});

これをSeederから呼び出します。
factoryの第二引数を変えることで生成するレコードの数を変えられるので、ここにartisanコマンドで受け取った引数を渡すことにします。

UsersTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(User::class, 10)->create();
    }
}

artisanコマンドで引数を使用する

引数の扱いは、
https://qiita.com/nenokido2000/items/abbf70c87c9ad86a2b89
などを参考にしました。

まずはターミナルから雛形を生成します。

$ php artisan make:command BulkUserSeed

作成されたファイルを変更して、
・呼び出すためのコマンド
・引数
・呼ばれた際の処理
を記述していきます。
雛形そのままで動く部分は省略してあります。

BulkUserSeed.php
<?php

namespace App\Console\Commands;

use App\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Schema;

class BulkUserSeed extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:BulkUserSeed {number=10 : number of users}';

    // 〜〜(中略)〜〜

    public function handle()
    {
        print("Start seeding.\n");
        $numberOfUsers = (int)$this->argument("number");
        factory(User::class, $numberOfUsers)->create();
        print("Seeding complete.\n");
    }
}

また、オプションとしての引数も与えられるので、今回は試しにmigrate:refreshのようにテーブルをまず全消去するかを選べるようにしたいと思います。

BulkUserSeed.php
// ... ...
protected $signature = 'command:BulkUserSeed
    {number=10 : number of users}
    {--refresh : drop all existing users}';

// ... ...

public function handle()
    {   
        $refresh = $this->option("refresh");
        if ($refresh){    
            print("Are you sure you want to delete all users? [Press y] \n");
            $input = trim(fgets(STDIN));
            if ($input === 'y') {
                print("User table truncated.\n");
                User::truncate();
            } else {
                print("Seeding canceled.\n");
                return;
        }
        print("Start seeding.\n");
        $numberOfUsers = (int)$this->argument("number");
        factory(User::class, $numberOfUsers)->create();
        print("Seeding complete.\n");
    }
}

ログなどはお好みで。
これで

> php artisan command:BulkUserSeed 
> php artisan command:BulkUserSeed 100
> php artisan command:BulkUserSeed 100 --refresh

などのコマンドが選択できるようになりました。

おまけ 連番でデータを作成する

実は元々作っていたプロジェクトでは(と言っても練習用の仮想の物ですが)、日にちごとに担当者を決めて管理するという機能を付けようとしていました。そこで用いるテーブルには以下のようなものがあります。
スクリーンショット 2020-06-30 20.52.07.png

例えば1月1日から12月31日までの仮データを一気に作りたい!と思った時は、先ほどと同じ方法ではうまくいかず、dateの値を加算しながら与える必要があります。

1日ずつずれた日付を与えるにはfor文を回しながら
・基準となる日にちを決め、+i日加算した日付を与える
・ループごとに変数を1日ずつインクリメントする
などで達成できます。下のコードでは後者を用いています。

なお、+1 monthなど月単位で加算する場合、月末の日付のズレなどに気を使う必要があるようです。
(参考: https://www.p-nt.com/technicblog/archives/11 など)

BulkAssignmentSeed.php
<?php

namespace App\Console\Commands;

use App\Assignment;
use Illuminate\Console\Command;

class BulkAssignmentSeed extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:BulkAssignmentSeed
    {number=10 : number of assignments}
    {first_date=1970-01-01 : the date of first row}
    {--refresh : drop all existing assignments}';
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Seeding arbitrary number of assignments.';

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

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $refresh = $this->option("refresh");
        if ($refresh){    
            print("Are you sure you want to delete all Assignments? [Press y] \n");
            $input = trim(fgets(STDIN));
            if ($input === 'y') {
                print("Assignment table truncated.\n");
                Assignment::truncate();
            } else {
                print("Seeding canceled.\n");
                return;
            }
        }

        print("Start seeding.\n");
        $numberOfAssignments = (int)$this->argument("number");
        $base = $this->argument("first_date");
        for($i = 0; $i < $numberOfAssignments; $i++){ 
            // Log::debug($baseDate);
            $date = date("Y-m-d", strtotime($base . "+($i) day"));
            factory(Assignment::class)->create([
                'date' => $date,
            ]);

        }

        print("Seeding complete.\n");
    }
}

AssignmentFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Assignment;
use Faker\Generator as Faker;

$factory->define(Assignment::class, function (Faker $faker) {
    return [
        'staff' => $faker->name,
    ];
});

終わりに

コマンド拡張の楽しさを知ったので、これからも色々やってみたいと思います。

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

【Laravel】Route/web.phpで構文ミスするとartisanコマンドが動かなくなる問題

はじめに

プロジェクトファイルをgitからクローンしてartisanコマンドを入力したら以下のエラーがでた。

PHP Fatal error:  Uncaught Error: Class 'CommonHelper' not found in /home/vagrant/{アプリ名}/app/Logging/CustomJsonFormatter.php:13
Stack trace:
#0 /home/vagrant/{アプリ名}/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php(40): App\Logging\CustomJsonFormatter->format()
#1 /home/vagrant/{アプリ名}/vendor/monolog/monolog/src/Monolog/Logger.php(323): Monolog\Handler\AbstractProcessingHandler->handle()
#2 /home/vagrant/{アプリ名}/vendor/monolog/monolog/src/Monolog/Logger.php(541): Monolog\Logger->addRecord()
#3 /home/vagrant/{アプリ名}/vendor/laravel/framework/src/Illuminate/Log/Logger.php(176): Monolog\Logger->error()
#4 /home/vagrant/{アプリ名}/vendor/laravel/framework/src/Illuminate/Log/Logger.php(87): Illuminate\Log\Logger->writeLog()
#5 /home/vagrant/{アプリ名}/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(547): Illuminate\Log\Logger->error()
#6 /home/vagrant/{アプリ名}/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(121): Illuminate\Log\LogManager->error()
#7 /home/vagrant/{アプリ名}/app/Exceptions/ in /home/vagrant/{アプリ名}/app/Logging/CustomJsonFormatter.php on line 13

解決するためにした事

1,composer dump-autoload

クラスがない系のエラーは大体composer dump-autoloadで直ると思っていたら同じようなエラー内容が表示された。

> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
PHP Fatal error:  Uncaught Error: Class 'CommonHelper' not found in /home/vagrant/{アプリ名}/app/Logging/CustomJsonFormatter.php:13
以下同文

どうやらクラスの読み込みが問題ではなく、そもそもartisanコマンド自体が受け入れられていないようだ。

2,routes/web.phpの構文ミス

あれこれ悩んだ結果、結局routesのファイルで構文ミスをしていただけで、内容は括弧が一行分足りていなかった。
これとCommonHelperのクラスがみつからなくなる因果関係が未だわかっていないが、おそらくLaravelのアーキテクチャに関わるデザインパターンのファイルを間違えるとそもそもクラスの読み込みまで到達してくれないのではないか、その為Stack traceで全く関係のないlog周りの内容が表示されているのだと思います。

おわりに

同様なエラーが発生された方がいましたら、Laravelのアーキテクチャに関わるデザインパターンのファイルを調べてみてください。

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

【Laravel】laravel new のあとにやること

環境

  • macOS
  • PHP7.4
  • Laravel7.x
  • SQLite3

やること

新しくアプリケーションを作成する時、laravel new [アプリ名]を実行した後にすることをまとめました
あくまで筆者の場合なので、参考程度に読んでみてください

  • git周辺
  • アプリ名、タイムゾーン、言語の設定
  • データベースの設定
  • メールの設定
  • Auth機能の導入
  • 日本語化

git周辺

はじめに、BitbucketやGitHubのリポジトリを作っておきます
以下のコマンドを実行して、リポジトリの初期化、BitbucketやGitHubへの接続を行います
ちなみに筆者は執筆現在Bitbucketを使っています

# LaravelAppディレクトリに移動
% git init
% git add -A
% git commit -m "Initialize repository"
% git remote add origin [BitbucketやGitHubのリポジトリ名]
% git push -u origin master

アプリ名、タイムゾーン、言語の設定

  • .envファイルのAPP_NAME
  • config/app.phpのname、timezone、local

を設定します

.env
APP_NAME=アプリ名
config/app.php
'name' => env('APP_NAME', 'アプリ名'),
...
'timezone' => 'Asia/Tokyo',
...
'locale' => 'ja',

データベースの設定

今回はSQLiteを使います

  • databaseフォルダにdatabase.sqliteを作成
  • config/database.phpのdefaultを設定
  • .envファイルのDB_変数を設定
% touch database/database.sqlite
config/database.php
'default' => env('DB_CONNECTION', 'sqlite'),
.env
DB_CONNECTION=sqlite

.envにデフォルトで設定されている他のDB_変数は削除します

メールの設定

今回はGメールを使うので、あらかじめGメール側でアプリパスワード(16桁)を生成しておきます
アプリパスワードは基本メモを取ったり他人と共有しませんが、環境変数に設定するので覚えておきましょう

  • config/mail.phpのdefault
  • .envファイルのMAIL_変数

を設定します

config/mail.php
'default' => env('MAIL_MAILER', 'smtp'),
env
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=Gmailアドレス
MAIL_PASSWORD=アプリパスワード(16桁)
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=Gmailアドレス
MAIL_FROM_NAME="${APP_NAME}"

Auth機能の導入

以下のコマンドを実行します
※Laravel5以下はコマンドが異なります

// laravel/uiをインストール
% composer require laravel/ui

// Auth関連ファイル(基本的なスキャフォールド)を生成
% php artisan ui vue --auth

// フロントエンドに必要なパッケージをインストール&必要なファイルをコンパイル・ビルド
% npm install && npm run dev

日本語化

Auth機能を導入したときに自動生成されるファイルがあります
フォームやバリデーションやメールで日本語で表示されるように変更します
日本語化は以下のURLのブログを見たほうがいいです(丸投げ)

https://blog.capilano-fw.com/?p=289#i-3

おわり

現在ポートフォリオ作成中で同時に記録として記事を書いていこうと思います

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

Laravel 1対多のリレーション先の項目の集計値(最小値, 最大値, 平均値, 合計値 など)で親テーブルを並べ替えたい

テーブルの例

たとえば、商品基本情報と詳細が1対多の関係で、データベースを構成したとします。
一覧表示する時に、価格順で並び替えたい時はどんな感じにクエリビルダーを使えばいいか、いろいろ試してこれかなぁというのを投降します。

【商品情報テーブル (products)】
id 商品ID
name 商品名

【商品詳細テーブル (detail)】
id 商品詳細ID
productID 商品詳細ID
size サイズ
price 価格
visible 表示・非表示

コードの例

$aggregates = DB::table('detail')
    ->select('productID',
    DB::raw('MIN(price) as min_price'),
    DB::raw('MAX(price) as max_price')
)
    ->where('visible', 1)
    ->groupBy('itemID');

$db = DB::table('products')
    ->joinSub($aggregates, 'aggregates', function ($join) {
        $join->on('products.id', '=', 'aggregates.itemID');
    })
    ->orderBy('min_price01', 'desc')
    ->get();

これなら、並び替えできます。ここ参考にしました。
https://readouble.com/laravel/6.x/ja/queries.html
最初は、joinでリレーションを作って、groupByでまとめ、価格の最大・最小をselectに入れて、を並び替えてみようとしたのが、できなかったので、こうなりました。

もっと上手な書き方があれば、こんな方法はどうだい?ってコメントいただけると、ありがたいです。
よろしくお願いいたします。

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

【Laravel】任意のExceptionをthrowする。

メモとして残します。

■やり方

throw new \Exception("エラーメッセージ");

<?php

try{
    //処理
    if(!$hoge){
        throw new \Exception("testです");
    }
} catch (\Exception $e) {
    echo $e->getMessage();// testです
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでCSVする方法

はじめに

FuelPHPには Format:forge($array)->toCsv() という感じで配列からかんたんにCSV用の文字列を生み出すことができたのですが、Laravelにはありません。
http://fuelphp.jp/docs/1.9/classes/format.html

それくらい foreach でぐるぐる回せやってのはあるのでしょうけど、めんどくさいじゃないですか。
そこでいい方法がないかなって探してみました。

soapbox/laravel-formatter を入れる

まあ、だいたいこういうものはpackagist漁ればいいのがありそうなもんなんですよ。
Laravel用じゃなくてもいいのですが、Laravel用だと5.5以降ならServiceProviderにいちいち登録しなくても使えるのがいいですよね!

というわけで今回は soapbox/laravel-formatter というのを使ってみます。
https://packagist.org/packages/soapbox/laravel-formatter

こいつを composer require しましょう。

$ composer require soapbox/laravel-formatter

これでLaravel内で Formatter として使えるようになっているので、あとは以下の感じで行けるはずです。
基本的には連想配列のキーがそのままラベル行として使われるので、DBから引っ張ってくるときにカラム名をas使って日本語とかにすると使いやすいと思います。

use SoapBox\Formatter\Formatter;

class HogeService
{
    public function makeCsvData()
    {
        $list = $this->hogeRepository->listForDownload();
        $formatter = Formatter::make($list->toArray(), Formatter::ARR);

        return $formatter->toCsv();
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel ログの格納場所を変更する

目的

  • Laravelのログの内容によって出力先を変更する第一歩として、デフォルトのログ出力状態で格納場所を任意の場所に変更する方法をまとめる

実施環境

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

前提条件

  • 下記、または下記に準ずる方法でMacのネイティブ環境(DockerやAWSを使わない)にLaravelの環境構築が完了していること。

読後感

  • Laravelのログファイルの場所をアプリ名ディレクトリ/storage/logs/直下からアプリ名ディレクトリ/storage/my_logsに変更できる。

詳細

  1. .envファイルの編集

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

      $ vi .env
      
    2. LOG_CHANNELの記載を下記の様に修正する。

      アプリ名ディレクトリ/.env
      LOG_CHANNEL=single
      
    3. 修正後の.envファイルの例を下記に記載する。

      アプリ名ディレクトリ/.env
      APP_NAME=Laravel
      APP_ENV=local
      APP_KEY=base64:1IFTNHZwVQ6AwDC+U7XfApuoSf5s02NK49auu7okT/E=
      APP_DEBUG=true
      APP_URL=http://localhost
      
      LOG_CHANNEL=single
      
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_DATABASE=laravel_auth_login
      DB_USERNAME=root
      DB_PASSWORD=****
      
      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. アプリ名ディレクトリで下記コマンドを実行してログの設定ファイルを開く

      $ vi config/logging.php
      
    2. 開いたファイルを下記の様に修正する。

      アプリ名ディレクトリ/config/logging.php
      <?php
      
      use Monolog\Handler\NullHandler;
      use Monolog\Handler\StreamHandler;
      use Monolog\Handler\SyslogUdpHandler;
      
      return [
      
          /*
          |--------------------------------------------------------------------------
          | Default Log Channel
          |--------------------------------------------------------------------------
          |
          | This option defines the default log channel that gets used when writing
          | messages to the logs. The name specified in this option should match
          | one of the channels defined in the "channels" configuration array.
          |
          */
      
          'default' => env('LOG_CHANNEL', 'stack'),
      
          /*
          |--------------------------------------------------------------------------
          | Log Channels
          |--------------------------------------------------------------------------
          |
          | Here you may configure the log channels for your application. Out of
          | the box, Laravel uses the Monolog PHP logging library. This gives
          | you a variety of powerful log handlers / formatters to utilize.
          |
          | Available Drivers: "single", "daily", "slack", "syslog",
          |                    "errorlog", "monolog",
          |                    "custom", "stack"
          |
          */
      
          'channels' => [
              'stack' => [
                  'driver' => 'stack',
                  'channels' => ['single'],
                  'ignore_exceptions' => false,
              ],
      
              'single' => [
                  'driver' => 'single',
                  'path' => storage_path('my_logs/laravel.log'),
                  'level' => 'debug',
              ],
      
              'daily' => [
                  'driver' => 'daily',
                  'path' => storage_path('logs/laravel.log'),
                  'level' => 'debug',
                  'days' => 14,
              ],
      
              'slack' => [
                  'driver' => 'slack',
                  'url' => env('LOG_SLACK_WEBHOOK_URL'),
                  'username' => 'Laravel Log',
                  'emoji' => ':boom:',
                  'level' => 'critical',
              ],
      
              'papertrail' => [
                  'driver' => 'monolog',
                  'level' => 'debug',
                  'handler' => SyslogUdpHandler::class,
                  'handler_with' => [
                      'host' => env('PAPERTRAIL_URL'),
                      'port' => env('PAPERTRAIL_PORT'),
                  ],
              ],
      
              'stderr' => [
                  'driver' => 'monolog',
                  'handler' => StreamHandler::class,
                  'formatter' => env('LOG_STDERR_FORMATTER'),
                  'with' => [
                      'stream' => 'php://stderr',
                  ],
              ],
      
              'syslog' => [
                  'driver' => 'syslog',
                  'level' => 'debug',
              ],
      
              'errorlog' => [
                  'driver' => 'errorlog',
                  'level' => 'debug',
              ],
      
              'null' => [
                  'driver' => 'monolog',
                  'handler' => NullHandler::class,
              ],
      
              'emergency' => [
                  'path' => storage_path('logs/laravel.log'),
              ],
          ],
      
      ];
      

メモ

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