はじめに
さて、今年のアドベントカレンダーは何を書こうかと、、、。
ちょうどLaravelでBacklogとNotionを連携する処理を書いていたので、それ関連で書こうと考えた。
本当はNotionに連携するところまで書こうと思っていたのだが、
まだ実装途中で時間がかかりそうなので、
Backlogから値を取得してcsvで出力するまでの記事を書くことにした。
プロジェクトの情報が漏洩しないように、外部の情報は、サニタイズして記述するので、
動かなかったらごめん🙇♀️
対象読者
- Web系エンジニアの人
- プログラミングやってる人
- Laravelやってる人
- Backlog使ってる人
backlogとは?
チームのタスク管理ツールのこと。
URL:https://backlog.com/ja/
大まかな手順
- Laravel環境構築
- League\Csvのインストール
- BacklogAPIキーの取得
- 実装
Laravel環境構築
使用技術
※ MySqlは現在は使われていないが、今後使用するかもしれないので、一応入れておく。
Name | version |
---|---|
PHP | 8.1.12 |
Laravel | 9.41.0 |
Laravel-sail | 8.1 |
MySql | 8.0 |
League/csv | 9.8 |
とりあえずsailで環境構築。csvはLeague/csvで出力する。
事前準備
-
Docker Desktop (Docker for Mac) をインストールしておく
公式:https://www.docker.com/products/docker-desktop/ -
手元のマシンに php@8.1 と composer 2.x を入れておく
brewでインストールするのが早い(brewのインストールとかは抜粋します。)
$ brew install php@8.1
$ brew install composer
1. 任意のディレクトリでLaravelアプリケーションを作成する
example-appの箇所は任意のプロジェクト名を入れる
$ curl -s "https://laravel.build/example-app" | bash
2. ディレクトリを移動して、sailを立ち上げる
$ cd example-app
$ ./vendor/bin/sail up
エイリアスを作成すると下記だけでいける。
$ sail up -d
エイリアスの設定の仕方
bashの場合 → .bashrc
zshの場合 → .zshrc
.bashrcか、.zshrcに下記を記述する
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
4. docker-compose.ymlを修正する
使わないサーバーを削除(mysqlは使用しないがなんとなく残している)
# For more information: https://laravel.com/docs/sail
version: '3'
services:
app:
build:
context: ./vendor/laravel/sail/runtimes/8.1
dockerfile: Dockerfile
args:
WWWGROUP: '${WWWGROUP}'
image: sail-8.1/app
extra_hosts:
- 'host.docker.internal:host-gateway'
ports:
- '${APP_PORT:-80}:80'
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
environment:
WWWUSER: '${WWWUSER}'
LARAVEL_SAIL: 1
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
volumes:
- '.:/var/www/html'
networks:
- sail
depends_on:
- mysql
mysql:
image: 'mysql/mysql-server:8.0'
ports:
- '${FORWARD_DB_PORT:-3306}:3306'
environment:
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- 'sail-mysql:/var/lib/mysql'
- './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
networks:
- sail
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
retries: 3
timeout: 5s
networks:
sail:
driver: bridge
volumes:
sail-mysql:
driver: local
servicesの名前を変更したので、.envにAPP_SERVICE="app"を記述。
APP_SERVICE="app"
ymlファイルを変更したので、sailを立ち上げ直す
$ sail down
$ sail up -d
League\Csvのインストール
インストール
$ composer require league/csv
BacklogAPIキーの取得
バックログにログインして、
Backlog > 個人設定 > APIにて、わかりやすいメモを記入し発行ボタンを押して、
APIキーを発行します。
(本当はスクショして見せようかと思ったのですが、社内情報が漏れるのが怖いので、文字だけで勘弁。)
実装
1..envにAPIKEYを記述する
BACKLOG_API_KEY=${取得したAPIKEY}
2.config配下にbacklog.phpを作成する
<?php
return [
'base_uri' => ${プロジェクトのURL},
'api_key' => env('BACKLOG_API_KEY'),
'project_id' => ${プロジェクトのID},
];
3.今回はバッチ処理のなので、config/filesystem.phpにbatchを追加する
'batch' => [
'driver' => 'local',
'root' => storage_path('app/batch'),
],
4. app/Console/Commands配下にバッチ処理を作成する
<?php
namespace App\Console\Commands;
use App\Actions\ExportBacklogCsv;
use App\Actions\GetBacklogDatas;
use Illuminate\Console\Command;
class ImportAndExportCsvForEvmsBacklog extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'import-and-export-csv-for-backlog';
/**
* The console command description.
*
* @var string
*/
protected $description = 'バックログ取り込み・CSV出力バッチ';
/**
* Execute the console command.
*/
public function handle(GetBacklogDatas $getBacklogDatas, ExportBacklogCsv $exportCsv)
{
$this->info('開始');
$baseUri = config('backlog.base_uri');
try {
// バックログにAPIを投げて値を取得
$backlogDatas = $getBacklogDatas->exec($baseUri);
// csvに出力
$exportCsv->exec($backlogDatas, $baseUri);
} catch (\Exception $e) {
$this->info($e);
}
$this->info('終了');
}
}
処理は下記のクラスに分けました。
GetBacklogDatas → バックログから値を引っ張ってくるクラス
ExportBacklogCsv → データをCSVに吐き出すクラス
5. app/Actions/GetBacklogDatasを作成する
今回は課題の一覧を取得したかったので、下記を使用します。
条件
ステータスは、未対応、処理中、処理済で絞る
課題は100件以上ないことを前提に処理を組んでいる(backlogのAPIは一度に100件しかデータが取れないため。)
<?php
namespace App\Actions;
use Illuminate\Support\Facades\Http;
class GetBacklogDatas
{
private const NO_COMPATIBLE = 1; // 未対応
private const PROCESSING = 2; // 処理中
private const PROCESSED = 3; // 処理済
/**
* exec
* @param string $baseUri
* @return array
*/
public function exec($baseUri): array
{
$apiKey = config('backlog.api_key');
$params = $this->getParams($apiKey);
$response = Http::get("$baseUri/api/v2/issues", $params);
return $response->json();
}
/**
* getParams
* @param string $apiKey
* @return array
*/
protected function getParams(string $apiKey): array
{
/**
* backlogのAPIが1回に100件しか値を取れないので、
* それ以上取得する場合は、offsetをうまく利用した実装に変更する必要があるが、
* 一旦は100件を超えることは考えにくいため、そのままとする
*/
return [
'apiKey' => $apiKey,
'projectId[]' => config('backlog.project_id'),
'statusId[]' => self::NO_COMPATIBLE,
'statusId[1]' => self::PROCESSING,
'statusId[2]' => self::PROCESSED,
'count' => 100,
];
}
}
6. ExportBacklogCsvを作成する
League/csvを使用して出力する
条件
課題名、担当者、期日、のデータを出力する
<?php
namespace App\Actions;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use League\Csv\CharsetConverter;
use League\Csv\Writer;
class ExportBacklogCsv
{
protected string $inputEncode = 'UTF-8';
protected string $outPutEncode = 'SJIS-win';
protected string $disk = 'batch';
protected string $uri;
protected array $backlogDatas;
/**
* exec
* @param array $backlogDatas
* @param string $uri
*/
public function exec(array $backlogDatas, string $uri)
{
$this->uri = $uri;
$this->backlogDatas = $backlogDatas;
$this->exportCsv($backlogDatas);
}
/**
* exportCsv
*/
protected function exportCsv()
{
[$header, $records] = $this->getHeaderAndRecords();
// エンコーディングの設定
$converter = (new CharsetConverter())
->inputEncoding($this->inputEncode)
->outputEncoding($this->outPutEncode);
$csv = Writer::createFromString()
->addFormatter($converter);
// Headerの作成
$csv->insertOne($header);
// Recodeの作成
$csv->insertAll($records);
if (Storage::exists($this->disk)) {
Storage::makeDirectory($this->disk);
}
$nowFormat = Carbon::now()->format('YmdHis');
$fileName = "backlog_issue/backlog_issue_$nowFormat.csv";
// ファイルの生成
Storage::disk($this->disk)->put($fileName, '');
// ファイルパスの取得
$path = Storage::disk($this->disk)->path($fileName, true);
// ファイルに記述
file_put_contents($path, $csv);
}
/**
* getHeaderAndRecords
* @return array
*/
protected function getHeaderAndRecords(): array
{
$header = [
'課題名',
'担当者',
'期日',
];
foreach ($this->backlogDatas as $data) {
$records[] = [
$data['summary'] ?? '', // 課題名
$data['assignee']['name'] ?? '', // 担当者
$data['dueDate'] // 期日
? Carbon::parse($data['dueDate'])->format('Y/m/d')
: '',
];
}
return [$header, $records];
}
}
7. バッチを実行してみる
$ sail artisan import-and-export-csv-for-backlog
storage/app/batch/backlog_issue配下にファイルが出力されるはず!
まとめ
とりあえずこれで動いた。
作ってから、記事にしたので、この通りやっても動かなかったらごめん。。。
良いお年を。