著者アーカイブ 杉浦

著者:杉浦

【Laravel】一プロセス内限定のSQLのキャッシュを作る

 ORMは便利ですが、何度も重複するクエリを発行しがちです。複数のモデルオブジェクトeachしてリレーション先を参照する時は特に危険です。どれくらい重複してるかは
 barryvdh/laravel-debugbar: Laravel Debugbar (Integrates PHP Debug Bar)
 などで調べるのが楽です。duplicatedとuniqueに分けてくれます。

 やり方の方針はクエリが発行される度にそのクエリに対応するキャッシュはあるかチェック、あるならばキャッシュを結果として取得、なければ実際に発行して結果を取得してキャッシュに保存、です。
 一つのクエリに使う場合は次です。

        $key = __FILE__.' '.__FUNCTION__.' '.__LINE__ .' '.$id;
        app()->offsetExists($key) ?: app()->singleton($key, static function () use ($id){
            return Member::where('id',$id)->get();
        });

 コードの場所と引数に関して一意になるキーを設定。クエリの結果をシングルトンに登録することによって、同じ場所が同じ引数で繰り返し呼ばれる際、同じクエリを発行することなく、Laravel内にバインドされたシングルトンを呼ぶようになります。変数なのでプロセスが終わったら揮発します。
 作った後しばらくして気づきましたがarrayドライバ(array型の変数内にキャッシュを保存する機能)でキャッシュ使った方がシンプルですね。
 全体のクエリに使う場合はDBのselect等データ取得時のリスナーに同じ処理を入れます。updateした時のリスナーにキャッシュ消す処理忘れるとバグの元になります。

著者:杉浦

【Vue】jQueryとVueを共存させる

 デザインはjQueryで作られる、実装はVue.jsで行う。なんやかんやあってこの方式でwebページを作る必要が出てくることがあります。一貫していた方が作りやすいはずなのですが、なんやかんやあってこうなります。
 デザインの実装の際、jQueryとVue.jsを読み込ませるとデザイン時に動いていたイベントが消える時があります。これはVueがテンプレートエンジンも兼ねていることが原因で起きがちです。例えば次の様なシナリオで起きます。

// デザイン時
HTML読み込み完了
↓
jQueryによるイベント付与実行
↓
全てのHTMLに付与されるべきイベントが付与されている

// 実装時
HTML読み込み中、Vue構築中
↓
HTML読み込み完了
↓
jQueryによるイベント付与実行
↓
Vue構築完了。Vue内で定義されたHTML構築を持ってきて描画

 特にVueでAPIの結果をHTMLの一領域に描画する仕組みを作った時はイベントの再定義の絡みで大体こうなります。こうなる時にはVueの中でVueが読み込み完了した場合に実行されるイベントでjQueryによるイベントを定義すると良いです。

export default {
  components: {
  },
  props: {
  },
  data() {
    return {
      
    };
  },
  mounted() {
    // jQueryのイベント付与処理
  },

 このように記述するとコンポーネントが読み込まれるたびにmounted関数が実行され、テンプレート内にjQueryのイベントが付与されます。
 Vue.jsとjQueryの同時実行はJavaScriptファイルが巨大になり初期読み込みが遅くなるため積極的に行うべきでないのですが、デザイナーからいただけるデザインにjQueryでアニメーションがついていることがしばしばあります。そういった際にこのやり方を知っておくと、Vue.jsの便利さから離れないままアニメーションを崩すことも再構築することもなくデザインを再現できます。

著者:杉浦

【PHP】DI(依存性注入)を用いたコードの追い方

 DI(Dependency injection)(依存性注入)とはコード内で外部への依存を直書きするのでなく、外部から引数、セッターなどで依存する部分を与える実装のことです。例えば、次です。

use Illuminate\Contracts\Hashing\Hasher as HasherContract;

class DatabaseUserProvider implements UserProvider
{
    /**
     * The hasher implementation.
     *
     * @var \Illuminate\Contracts\Hashing\Hasher
     */
    protected $hasher;

    /**
     * Create a new database user provider.
     *
     * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher
     * @return void
     */
    public function __construct(HasherContract $hasher)
    {
        $this->hasher = $hasher;
    }
    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        return $this->hasher->check(
            $credentials['password'], $user->getAuthPassword()
        );
    }
}

 DatabaseUserProviderクラスがインスタンス化された際にHasherContract型の引数$hasherを要求し、$hasherを保持します。保持した$hasherをvalidateCredentialsメソッド内で用います。
 外部から依存するモノを与えるため、テストし易かったり変更し易かったりするのですが、あまりに自由で今稼働しているコードでは何が実体化しているのかわかりにくく、追うのが少し難しいです。上記コードなら$this->hasherって何?となってIDEの機能で型を追ってもHasherContractというインターフェースが現れて終わりです。HasherContractを満たすどのHasherクラスが使われているか分かりません。
 hasherの中身、ひいては実際の動作を知りたい時は、まず依存性が実際に注入された際のコードを動かし、その時のhasherの中身に何が入ってるのか調べる必要があります。PHPならば次の様に出来ます。

    public function __construct(HasherContract $hasher)
    {
        var_dump(get_class($hasher));exit;
        $this->hasher = $hasher;
    }

PHP: get_class – Manual
 get_class関数でインスタンスそのものに、あなたのクラス名は何ですか、と尋ねます。得られたクラス名が今稼働しているシステムで注入されているクラスの名前です。例えば、ここでBcryptHasherという実装クラスが呼ばれているとわかったら次の様に型付けをします。

class DatabaseUserProvider implements UserProvider
{
    /**
     * The hasher implementation.
     *
     * @var \Illuminate\Hashing\BcryptHasher
     */
    protected $hasher;

    /**
     * Create a new database user provider.
     *
     * @param  \Illuminate\Hashing\BcryptHasher  $hasher
     * @return void
     */
    public function __construct(BcryptHasher $hasher)
    {
        $this->hasher = $hasher;
    }
    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        return $this->hasher->check(
            $credentials['password'], $user->getAuthPassword()
        );
    }
}

 こうするとIDEのジャンプ機能はインターフェースHasherContractでなくクラスBcryptHasherを参照するようになり、静的にコードを追うことが格段に楽になります。注意点として直し忘れがあります。DIを駆使したコードほど直し忘れると型エラーで派手に事故ります。

著者:杉浦

【PHP】Adapterパターンであるカラムの値によって大きく振る舞いを変えるテーブルの扱いを多少マシにする

 ときおり扱い難い定義のデータベースを扱う必要が出てきます。特定のカラムによって値の意味や他テーブルとの関係が大きく変わるテーブルはその一つです。
 例えば、usersテーブルにtypeカラムがあり、typeによって仮会員か一般会員か特別会員か区別するとします。それぞれ似た様で確かに違うモノです。仮会員はメールアドレス等いくつかのデータがnullでも良し、の様に別のふるまいが要求され、それが増えてくると、usersテーブルを扱うのはだんだん面倒になってきます。早い時点でuserテーブルをリファクタリングできていれば問題にならないのですが、そうならないまま既存データやコードが積み重なり今更変える時間がない、ということもあります。アダプターパターンを用いることでデータベース定義を変えないままこれを緩和できます。
 アダプターパターンは増補改訂版Java言語で学ぶデザインパターン入門 | 結城 浩 |本 | 通販 | Amazonでは次のように説明されています。

「すでに提供されているもの」と「必要なもの」の間の「ずれ」を埋めるようなデザインパターン、これが
Adapterパターン
です。

 この考えに従えば「すでに提供されているもの」はtypeによってデータの意味が大きく変わるテーブル、「必要なもの」はデータの意味ごとのテーブルです。多くのフレームワークで採用されているORM(オブジェクト関係マッピング)は1テーブルに1モデルの原則で使いやすい様に作られています。これを1つの意味に対して1モデルになる様に用います。
 具体的にはマッピングの時点でwhere句を用いて、あるテーブルの特定のtype1つに対して1モデルの様にします。
 例えばLaravelならば

/** User.php */
class User{
  // 仮会員、一般会員、特別会員に関する大量のメソッド
}

となっている1つのモデル定義ファイルを明示的なテーブル指定とグローバルスコープによって

/** TemporaryUser.php */
class TemporaryUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.temporary'));
        });
    }
    // 仮会員に関するメソッド
}
/** GeneralUser.php */
class GeneralUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.general'));
        });
    }
    // 一般会員に関するメソッド
}
/** SpecialUser.php */
class SpecialUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.special'));
        });
    }
    // 特別会員に関するメソッド
}

と分割した3ファイルに分割します。完全に共有する部分は、ユーザ特性としてtraitを用意し、3会員モデルクラスそれぞれにuseさせることで整理できます。
 テーブルを分割する例を挙げましたが逆に複数のテーブルや設定等をまとめた上でカプセル化されたEntityクラスを作るというのもまた別のAdapterパターンの活用です。

著者:杉浦

Gitで”最も一般的に使用されているgitのヒントとテクニック”を紹介しているリポジトリgit-tips/tips

 Gitでコード管理をするだけならコード管理を目的としたGUIアプリやIDE備え付けの機能を使った方が楽です。一方コマンドラインからGitを用いるならば、見た目や操作性は微妙ですがより自由自在にGitの機能を使えます。アプリやIDE自体、ボタンやモニターにGitのコマンドと装飾された結果を結びつけているものですから、コマンドラインの方がより自由なモノになります。コマンドラインの方が自由とはいえ、使い勝手が悪いのは事実で面白い使い方の例などが欲しくなります。git-tips/tipsにはその様な例が載っています。
git-tips/tips: Most commonly used git tips and tricks.
isotai/git-tips: 最もよく使われるgitの小技と裏技(少し翻訳停滞中)
 例えば、’Group commits by authors and title’という説明でgit shortlogというコマンドが載っています。これは次の様な結果を見せます。

 コミット数と名前とコミットメッセージの一覧です。こういった結果を元にシェルでちょちょいと加工するだけで興味深いモノが出力できます。例えば次です。

git shortlog | grep ^[^\ ] | awk -F [\(\)] '{print $2,$1}' | sort -h -r

 grepで行頭に空白のない文字列であるコミット数と名前だけに絞り込み、awkで()区切りで文字列操作してコミット数が行頭になる様に文字列加工、sortで人間にとって自然な順番の反対で並び替え、とすると

の様にユーザごとのコミット数降順が分かります。同様に変更した行数やらマージされたプルリク数やら色々解析できます。また、git help -aで全てのgitコマンドのリストが出るのでそこから自分で探るのも手です。

著者:杉浦

起動したら自動で何かをするRaspberry Piを簡単に作る

 Raspberry Piはマイコンの一つです。
Teach, Learn, and Make with Raspberry Pi – Raspberry Pi
Raspberry Pi – Wikipedia
 マイクロなコンピュータであり、その使い方の一つに何かしらのセンサーを接続してデータを取得する、というものがあります。Raspberry Pi本体のサイズが手のひらサイズでありセンサーを起動したまま、どこかに置く、動かすといったことに苦労しません。せっかく持ち運びが容易なのですからRaspberry Piには単独で完結してもらいたいです。そのため本体だけで動くように、起動したら自動で何かをする様に設定するととても便利になります。
 設定の仕方は次のスライドが詳しいです。
 あなたのお家に眠るラズパイを救出したい – Speaker Deck
 このスライドはRaspberry PiとPCをSSH接続するまでの手順書です。これを読みながら設定を整えれば数分でSSHを繋げられます。スライド中に出てくる無線LAN接続用ファイル生成ページwpa_supplicant.conf作成は気持ち引っかかりやすい部分があります。
 それは次の画像の赤丸で囲った追加ボタンを押して現在作られる予定のファイル情報を更新する部分です。画像の入力欄にはSSID:aaaaとありますが、この状態でファイルを生成してもaaaaにつなげるための設定は作られません。

 これでSSHで接続できるところまで行けました。大体簡素なLinux系のOSが入っています。自動でプログラミングを立ち上げるにはsystemdという仕組みを使います。
systemd – Raspberry Pi Documentation
 好きに実行したいプログラム(例えばセンサーから値を取得してとあるサーバに流すとか)を書いて配置。最後に次の様に記述したhoge.serviceを/etc/systemd/systemに配置。

[Unit]
Description = My service

[Service]
ExecStart=起動したいプログラムの実行コマンド
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

 配置が完了したらsystemctl enable hoge.serviceとコマンドを実行してサービスを制御に登録.これだけで電源を入れる度にExecStart=以下のコマンドがsimpleにフォアグラウンドで実行されます。もし実行されているコマンドが終了してもalways再起動します。

著者:杉浦

Laravelの契約とDIとStrategyパターン

 あるフレームワークを対象にしたライブラリというものはフレームワーク内のある機能を拡張したり、まるで違う中身で似た役割を果たす、といったものが少なくありません。その様なことをしても不具合を起きにくくするためにinterfaceの仕組みを用います。Laravelでは契約という名でまとめられています。
契約 5.8 Laravel
 例えばハッシュ処理なら次です。
 contracts/Hasher.php at 5.8 · illuminate/contracts

<?php
namespace Illuminate\Contracts\Hashing;
interface Hasher
{
    /**
     * Get information about the given hashed value.
     *
     * @param  string  $hashedValue
     * @return array
     */
    public function info($hashedValue);
    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @param  array   $options
     * @return string
     */
    public function make($value, array $options = []);
    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = []);
    /**
     * Check if the given hash has been hashed using the given options.
     *
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function needsRehash($hashedValue, array $options = []);
}

 ハッシュ処理用の機能を作るならば、このインターフェースを実装しなさい、という契約です。Laravelの内部機能でハッシュ処理を行う際はこの契約によって作られることが保証されたメソッドのみを呼び出します。
 機能ができたならばどうにかそれを適用する必要があります。DI(依存性注入)というやり方によってこれを容易にします。依存性注入とはあるクラスへの依存を既に存在しているインスタンスや今まさに作られるインスタンスに注入するようなものです。例えば、次です。

<?php

namespace App\Http\Controllers;

use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * ユーザーリポジトリの実装
     *
     * @var UserRepository
     */
    protected $users;

    /**
     * 新しいコントローラインスタンスの生成
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * 指定ユーザーのプロフィール表示
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        $user = $this->users->find($id);

        return view('user.profile', ['user' => $user]);
    }
}
<a href="https://readouble.com/laravel/5.8/ja/container.html">サービスコンテナ 5.8 Laravel</a>より引用

 __construct(UserRepository $users)の部分でUserControllerクラスのインスタンスの持つプロパティであるusersはUserRepositoryインターフェースという契約を満たすことを強制されています。UserRepositoryインターフェースには必ずfindメソッドが実装する契約が記述されています。このため

        $user = $this->users->find($id);

 の部分は決して未定義エラーにならず、UserRepositoryを満たした何らかのクラスのインスタンスusers独自のfindメソッド処理を働かせます。
 DIは、ある目的を達成するためのアルゴリズムをごっそり代えることを容易にするプログラミングパターンであるStrategyパターンそのものです。既にDIを用いている部分は後から好き勝手コードを追加、変更したとしても注入口の記述を変えるだけで簡単にバグなく動かせます。

著者:杉浦

StrategyパターンとSQL文の構築

 Strategyパターンはコーディングのやり方の一つです。
Strategy パターン – Wikipedia
 
 画像は増補改訂版 Java言語で学ぶデザインパターン入門 | 結城 浩 | コンピュータ・IT | Kindleストア | Amazonより引用。
 Strategyパターンはとある目的を達成するためのアルゴリズムをごっそり代えることを容易にするプログラミングパターンです。業務においては仕様がごっそり変わっても、コードへの影響を小さく抑えるために用います。場当たり的な変更を繰り返すと仕様が変わって、関係を持つクラスやメソッドが変わって、更に共通箇所が変わって、変わった共通箇所に合わせて全く関係ない場所が変わって……なんてことも起き得ます。Strategyパターンを実現する方法の一つは、変わることが予想される領域をあらかじめまとめて、まとめた部分の入出力の型を固定する、というものです。変わることが予想される領域は主に業務知識(八百屋さんのプログラムなら八百屋さんの行う業務についての知識)によって把握します。業務知識に重点を置く設計をドメイン駆動設計と言い、ドメイン駆動設計の説明はエリック・エヴァンスのドメイン駆動設計 | Eric Evans, 和智右桂, 牧野祐子, 今関剛 | 工学 | Kindleストア | Amazonに詳しく載っているらしいです。私は買ったはいいですが未だに半分も理解できていません。
 ドメイン設計ほど大仰でなくとも簡単にStrategyパターンを用いることのできる部分があります。それがSQL文の構築です。データベースからデータを取得する時が特に典型です。データ取得のSQL文の中身は、任意の種類の値を任意の条件で任意の順で取得する、という言葉で抽象化できます。種類がselect, join, group by句あたり、条件がwhrere, join句あたり、順がorder by句になりやすいです。joinが二つあるのは関係するデータを引っ張ってくる理由に見たい、絞り込みたいになりやすいからです。見えない値による順番で値を引っ張ってきたいことはレアです。例えば次の様にクエリ構築をまとめた関数を作れます。

/** 次の様にニュースモデルに記述 */
public function scopeNewsListSelectQuery(QueryBuilder $query){
    return // 表示したいカラム、計算結果等を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListWhereQuery(QueryBuilder $query){
    return // 表示したいデータの条件を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListOrderQuery(QueryBuilder $query){
    return // 表示したいデータの順を決定する部分を構築するクエリビルダの集合
}
/** 次の様にコントローラからクエリの集合体メソッドを呼び出し */
$news_list = query()->scopeNewsListSelectQuery()
                    ->scopeNewsListWhereQuery()
                    ->scopeNewsListOrderQuery()
                    ->get();

 これだけでは一見、単にクエリを分解しただけに見えます。実際は大きな変更や追記が必要になった時、次の様に部分的にまとまりを入れ替えることで変更部を減らせます。
 例えば、ニュースと同じ仕組みでレビューを表示したい、どのニュースとレビューを表示するかはif文で記述すると複雑になるくらい微細に違う条件で決める、となった場合は次の様にできます。

/** 次の様にページモデル(ニュースモデルを抽象化したモデル)に記述 */
public function scopePageListSelectQuery(QueryBuilder $query){
    return // 表示したいカラム、計算結果等を決定する部分を構築するクエリビルダの集合
}

public function scopeNewsListWhereQuery(QueryBuilder $query){
    return // 表示したいニュースの条件を決定する部分を構築するクエリビルダの集合
}

public function scopeReviewListWhereQuery(QueryBuilder $query){
    return // 表示したいレビューの条件を決定する部分を構築するクエリビルダの集合
}

public function scopePageListOrderQuery(QueryBuilder $query){
    return // 表示したいデータの順を決定する部分を構築するクエリビルダの集合
}
/** 次の様にコントローラからクエリの集合体メソッドを呼び出し */
$news_list = query()->scopePageListSelectQuery()
                    ->scopeNewsListWhereQuery()
                    ->scopePageListOrderQuery()
                    ->get();
$review_list = query()->scopePageListSelectQuery()
                    ->scopeReviewListWhereQuery()
                    ->scopePageListOrderQuery()
                    ->get();

 すっきりです。さらに突飛な要求が多く来たとしてもページという枠組みに収まっているならば、そうそう困りません。もし工夫なく作ろうとした場合、変更時に漏れのありがちなコピペコードになるか、複雑で読み難いうえにインデックスも張りにくい謎クエリビルディングをすることになります。
 もし、SelectもOrderも違うとしたら、それはそれで入力部と表示部は同じだがクエリはごっそり代わるというStrategyパターンにするべきでしょう。

著者:杉浦

【PhpStorm】ショートカットキーのリストを表示するショートカットキー

 PhpStormのできることは膨大でそのできることに対応しているショートカットキーの数もまた多いです。その様に多いショートカットキーを覚えられる人は稀でしょう。これの対策なのかいくつかのショートカットキーをグループ化したショートカットキーのリストのポップアップを表示するショートカットキーが用意されています。このリストをクイックリストと言います。
クイック・リスト – 公式ヘルプ | PhpStorm
クイックリストの設定 – 公式ヘルプ | PhpStorm
 私がよく使うのは大体次の三つです。
 Alt+Enterによる今カーソルが当たっているコードを改善する提案の表示。厳密にはクイックリストと違いますが感覚的に似ているので挙げました。

 Ctrl+Shift+Alt+Tによるリファクタリング操作の表示。

 Ctrl+Nによる何かしらの目印の名前検索の表示。これはTabで選択を動かせます。

著者:杉浦

【PhpStorm】deep-assoc-completionプラグインの妙な機能

 deep-assoc-completionはPHPの配列のキーを探索するプラグインです。これのおかげで連想配列を予測補完でき、配列を扱うのが楽になります。
deep-assoc-completion – Plugins | JetBrains

 あっさりしたいいプラグインなのですが配列に関係なさそうなPhp->JSの翻訳機能がついていました。次の様な翻訳が働きます。

 昔のエキサイト翻訳ばりの怪しさですが、JavaScriptのコードになっています。少なくともクラス、ファイルあたりの各依存関係を解決する必要があり、微妙な手直しも必要なのでフレームワークのコード全体をコピーすることはあまり得でないでしょう。一方でLaravelのhelper関数、バリデーション関数の様な小さな閉じたコード、緯度経度と平面直角座標を変換する長い四則演算のコード、といったものを変換するのには便利です。
 この機能は実験的機能であり実現するにしても随分先の話ですが、JavaScriptへの変換精度を上げる、webpackかnpm runか何かのビルド用スクリプトに組み込む、の二つが出来ればJavaScript、PHPの両方をサポートする一つのプログラムファイルを作るなんてこともできそうです。

 python to javascript transpilerやpython to php transpilerでググるとまあまあ件数が出てきました。pythonを第3言語ないし中間言語として扱えば現時点でもJavaScript、PHPの両方をサポートする一つのプログラムファイルを作ることは現時点でも現実的?