もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

cakephpの日本語ドキュメントに貢献できた

はじめに

こんにちは。

簡単なヤッター!ブログです。

最近CakePHPの案件をお手伝いしており、日本語のドキュメントを読むようになったのですが、英語と日本語のドキュメントで差分がある場所がありました。

せっかくなのでPR送ってみました。

今回は英語版のドキュメントをもとに、日本語版のドキュメントを修正しました。

github.com

1日立たずにマージしてもらえたので、びっくりましたw

book.cakephp.org

ドキュメントの簡単な修正ですが、大きなOSSに貢献できたこと、かつ、お世話になっているOSSだったことでかなりテンション上がりました。

去年から感じてますが、OSSへの貢献は、想像よりもハードルは低いと感じれる様になりました。

これからも自分が使っているツールに貢献できればと思います。

現場からは以上です。

CleanArchitecture #11を行いました

はじめに

こんにちは。

CleanArchitecture輪読会の11回目の輪読会を行いました。

今回の担当箇所は30, 31, 32章でした。

内容

今回は、22章の詳細を話しかなとおもいました。

各詳細に対して非依存であることが重要で、特にフレームワークについては、他の2章に比べてより距離を取るべきという気持ちが強いのかな?と思いました。

32章のフレームワークの結婚の話が、なかなか良い例えでしたね。

終わりに

次回でクリーンアーキテクチャ輪読会はラストになります! また別の輪読会の予定も決まっているので、もっと勉強していきたいとおもいます。

phpSpreadsheetでセルの書式にユーザー定義を適用する

はじめに

こんばんは。

phpSpreadsheetでファイルを作成する際に、日付のセルを(YYYY-MM-DD)の形式で登録したい場合があったので、簡単ですが残しておきます。

何も設定しない場合は、以下みたいに YYYY/MM/DD の形でフォーマットされます。

gyazo.com

これを YYYY-MM-DD で入力できるようにしてみました。

github.com

本題

<?php

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();
$sheet       = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', 'ID');
$sheet->setCellValue('B1', '会社名');
$sheet->setCellValue('C1', '電話番号');
$sheet->setCellValue('D1', '設立日');
$sheet->getStyle('D')->getNumberFormat()
    ->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDD);

header('Content-Disposition: attachment; filename="download.xlsx"');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');

gyazo.com

これで フォーマットが YYYY-MM-DD になりました。

終わりに

ほかの書式も設定できそうなので、今度試してみます。 簡単ですが以上です。

phpの祝日ライブラリはYasumiが良かった

はじめに

こんばんは。

以前 GoogleCalenderから祝日データを取得する 記事を上げたのですが、Yasumiというライブラリを使用して、祝日データをとるような処理をしたので記録を残しておきます。

github.com

本題

1. 現在が祝日かどうか

<?php
use Yasumi\Yasumi;

function isHoliday(\DateTimeInterface $currentTime, $country = 'Japan', $locale = 'ja_JP'): bool
{
    $holidays = Yasumi::create($country, (int)$currentTime->format('Y'), $locale);
    return $holidays->isHoliday($currentTime);
}

isHoliday(new \DateTime('2020-03-20'));
  => true

2. その年の祝日一覧

<?php
use Yasumi\Yasumi;

function holidays(\DateTimeInterface $currentTime, $country = 'Japan', $locale = 'ja_JP'): array
{
    $holidays = Yasumi::create($country, (int)$currentTime->format('Y'), $locale);
    $results  = [];
    foreach ($holidays->getHolidays() as $holiday) {
        $results[$holiday->format('Y-m-d')] = $holiday->getName();
    }
    return $results;
}

holidays(new \DateTime('2020-03-20'));
  => array(18) {
  '2020-01-01' =>
  string(6) "元日"
  '2020-01-13' =>
  string(12) "成人の日"
  '2020-02-11' =>
  string(18) "建国記念の日"
  '2020-02-23' =>
  string(15) "天皇誕生日"
  '2020-02-24' =>
  string(24) "天皇誕生日 observed"
  '2020-03-20' =>
  string(12) "春分の日"
  '2020-04-29' =>
  string(12) "昭和の日"
  '2020-05-03' =>
  string(15) "憲法記念日"
  '2020-05-04' =>
  string(15) "みどりの日"
  '2020-05-05' =>
  string(15) "こどもの日"
  '2020-05-06' =>
  string(24) "憲法記念日 observed"
  '2020-07-23' =>
  string(9) "海の日"
  '2020-07-24' =>
  string(18) "スポーツの日"
  '2020-08-10' =>
  string(9) "山の日"
  '2020-09-21' =>
  string(12) "敬老の日"
  '2020-09-22' =>
  string(12) "秋分の日"
  '2020-11-03' =>
  string(12) "文化の日"
  '2020-11-23' =>
  string(18) "勤労感謝の日"
}

3. 特別な祝日を設定

<?php

use Yasumi\Holiday;
use Yasumi\Yasumi;

function isHoliday(\DateTimeInterface $currentTime, $country = 'Japan', $locale = 'ja_JP'): bool
{
    $holidays = Yasumi::create($country, (int)$currentTime->format('Y'), $locale);
    $holidays->addHoliday(new Holiday(
        'special_holiday_1',
        ['en' => 'Special Holiday', 'ja' => '特別休日'],
        new \DateTime('2020-03-21'),
        'ja_JP',
        Holiday::TYPE_OTHER
    ));

    return $holidays->isHoliday($currentTime);
}

isHoliday(new \DateTime('2020-03-21'));
  => true

終わりに

3番は意外と使い勝手いいなと思いました。 ほかにも色々あるっぽいので、使い込んでみたいです。

現場からは以上です。

SentryでUser情報を送る

はじめに

Sentryを使用していて、フロントからユーザー情報を送るようにしてみました。

ちなみに送らない場合は、ユーザー情報にはIPアドレスしか送られません。

f:id:kojirooooocks:20200305194835p:plain

ちなみに、フロントはVueを使っているので、Vuexで保存しているユーザー情報を送る想定です。

docs.sentry.io

本題

import * as Sentry from '@sentry/browser';
import * as Integrations from '@sentry/integrations';
Sentry.init({
  enabled: process.env.VUE_APP_ENVIRONMENT === 'production',
  dsn: process.env.VUE_APP_SENTRY_DNS,
  environment: process.env.VUE_APP_ENVIRONMENT,
  integrations: [new Integrations.Vue({ Vue, attachProps: true })]
});


import Vue from 'vue';
import App from '@/App.vue';
import router from '@/router';
import store from '@/store';

try {
  if (store.getters['AUTH/LOGGED_IN']) {
    const user = store.getters['AUTH/USER_DATA'];
    Sentry.setUser({
      id: user.id,
      email: user.email,
      username: user.username
    });
  }
}catch (error) {
  Sentry.captureException(error);
}

new Vue({
  el: '#app',
  store,
  router,
  components: { App },
  template: '<App/>'
});

これでSentryにユーザー情報を送れるようになりました。

f:id:kojirooooocks:20200305195737p:plain

終わりに

簡単でした。バックエンドの方ももっと簡単なのかな? 今度やってみます。

CleanArchitecture #6を行いました

はじめに

こんにちは。

CleanArchitecture輪読会の6回目の輪読会を行いました。

今回の担当箇所は15, 16, 17章でした。

内容

章を通して言っていたのが 選択肢を残す ということでした。

技術詳細はまでは考えず、もっとフォーカスすべきところに目を向けるという感じです。

結構刺さったのが次の一文

DBやフレームワークなどの詳細な技術の決定を初期段階で下す必要はない。 会社ですでに決定しているものがあった場合、優秀なアーキテクトならば、まだ決まっていないと主張する

この主張ってなかなか難しいと思いますが、やらないとだめですよね...

終わりに

実は、すで#7, #8 は開催されており、ただ単純に自分がブログ化するのが遅れておりました...

次回からは遅れずあげようと思います。

laravel-responsecacheを試す

はじめに

こんばんは。

今回は laravel-responsecache

を試してみました。

バージョン

Laravel Framework : 6.4.1
spatie/laravel-responsecache : 6.3

本題

インストール

$ composer require spatie/laravel-responsecache
Using version ^6.3 for spatie/laravel-responsecache
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing spatie/laravel-responsecache (6.3.0): Loading from cache
Writing lock file
Generating optimized autoload files
ocramius/package-versions: Generating version class...
ocramius/package-versions: ...done generating version class
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
Package manifest generated successfully.

configファイル生成

config/responsecache.php
$ php artisan vendor:publish --provider="Spatie\ResponseCache\ResponseCacheServiceProvider"
Copied File [/vendor/spatie/laravel-responsecache/config/responsecache.php] To [/config/responsecache.php]
Publishing complete.
Publishing complete.

middleware

app/Http/Kernel.php

※ 明示的に このルートにのみCacheを適用させたい 場合

protected $routeMiddleware = [
   ...
   'cacheResponse' => \Spatie\ResponseCache\Middlewares\CacheResponse::class,
];

route

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('example', \App\Http\Controllers\ExampleController::class);

Controller

<?php

namespace App\Http\Controllers;

class ExampleController
{
    public function __invoke()
    {
        return view('example');
    }
}

View

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    現在の時間は {{ date('Y-m-d H:i:s') }}です。
</body>
</html>

f:id:kojirooooocks:20200215033841p:plain

表示されました。

この状態から、 /example のRouteのレスポンスをキャッシュします。

Route修正

※ この設定は 300秒 のキャッシュをする設定です。

<?php

use Illuminate\Support\Facades\Route;

Route::group(['middleware' => 'cacheResponse:300'], function (): void {
    Route::get('example', \App\Http\Controllers\ExampleController::class);
});

この状態で一度アクセスしてキャッシュを作ったあと再度リロードすると、表示が変わらないことがわかります。

キャッシュ結果はデフォルトでは file指定で、 framework/cache/* に作られます。

f:id:kojirooooocks:20200215033854p:plain

番外編

このresponsecacheは、このあたりの設定で、ajax通信には適用できません。

コレを無理やり適用するには、 CacheAllSuccessfulGetRequests.php をオーバライドして、あらたなProfile を作成する必要があります。

CustomCacheAllSuccessfulGetRequests.php

<?php
declare(strict_types=1);

namespace App\Http;

use Illuminate\Http\Request;
use Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests;

class CustomCacheAllSuccessfulGetRequests extends CacheAllSuccessfulGetRequests
{
    /**
     * 元のメソッドではajax通信の際はキャッシュを行わない仕様になっていた。
     * それを無理やり可能にした
     * @param Request $request
     * @return bool
     */
    public function shouldCacheRequest(Request $request): bool
    {
//        if ($request->ajax()) {
//            return false;
//        }
    
        if ($this->isRunningInConsole()) {
            return false;
        }

        return $request->isMethod('get');
    }

    /**
     * もとのメソッドではログイン時にはAuthのIDをsuffixとして使おうとしていた。
     * ユーザーごとに差異がないレスポンスのキャッシュをしたい場合は、このメソッドも以下のようにオーバーライドする必要がある
     *
     * @param Request $request
     * @return string
     */
    public function useCacheNameSuffix(Request $request): string
    {
        return '';
    }
}

configファイル修正

config/responsecache.php

cache_profile 部分を作成したCustomProfileに変更

<?php

return[
    ...
    'cache_profile' => \App\Http\CustomCacheAllSuccessfulGetRequests::class,
    ...
];

これでweb以外もレスポンスキャッシュが適用できます。

終わりに

Master系のデータをキャッシュしたりもできるので、レスポンス以外にも使えるみたいです。

今の現場では使わなかったですが、汎用性が高いので、実際にプロジェクトでも使ってみたいです。

現場からは以上です。