著者アーカイブ 杉浦

著者:杉浦

【Laravel】Eloquent自動生成ライブラリeloquent-model-generatorの紹介

krlove/eloquent-model-generator: Eloquent Model Generator
 LaravelのEloquentモデルをORM(オブジェクト関係マッピング)として自在動かすにはDB中の1テーブルに対して1モデルを定義、更にテーブル間の関係をメソッドに記述、とする必要があります。これはいつも同じことを記述する上、大量に必要となるため面倒です。eloquent-model-generatorはこれを解決します。
 eloquent-model-generatorはDB中の定義からEloquentモデルを生成するライブラリです。例によってcomposerでインストール、

composer require krlove/eloquent-model-generator --dev

 artisanから起動できます。基本は次です。

# モデル名を指定(テーブル名はモデル名の複数形)
php artisan krlove:generate:model User
# モデル名とテーブル名を指定
php artisan krlove:generate:model User --table-name=user

 これだけでリレーション、fillableをはじめとした様々なオプション付きのモデルが生成されます。
 テーブル名らをコピペして、コマンドを量産して叩くだけでも便利ですがconfigを使うとさらに便利になります。

<?php

return [
    'namespace' => 'App\Models', // namespaceの冠詞
    'base_class_name' => \Illuminate\Database\Eloquent\Model::class, // Modelの継承元
    'output_path' => '/full/path/to/output/directory', // Modelの出力先ディレクトリ
    'no_timestamps' => true, // created_at, updated_atを使わないか使うか
    'date_format' => 'U', // datetimeカラムからデータを取得する時のフォーマット。nullだとCarbon
    'connection' => 'other-connection', // 不明
    'backup' => true, // 元々あるモデルを壊さないか否か
];

 Eloquent共通の設定が出来ます。
 さらに、この記事を書いている時点(2019/06/10)のプルリクに全てのモデルを生成するコマンドがあります。
Create Command To Generate All Models by tprj29 · Pull Request #46 · krlove/eloquent-model-generator
 このプルリクにあるコマンドファイルをコ用いて、 artisan krlove:generate:all-modelsを実行すると、DB中のテーブルを全て読み取り、全てに対応するEloquentモデルが生成されます。
 さらにこれをartisan ide-helper:modelsartisan test-factory-helper:generateと組み合わせることで素のEloquentが更に充実します。

著者:杉浦

Chromeのtextareaでスクロール位置の変更が上手くいかないことがある

 次のデモはVueを使ったtextarea中のクリック、キーアップ、キーダウンに反応してscrollTopを取得、表示するデモです。
 
 これを動かすと少々直感的でない動作とそのscrollTopの値が現れます。例えば次です。デモはclick, key up, key downそれぞれについていますが動画はclick, key downをまとめた一つのみです。

 screenTop:339になってからEnterを押して改行を実行すると、textareaのカーソルが画面内のてっぺんになり、screenTopが更新されません。どういうわけか度々この現象が起きます。マウスでスクロールぐりぐり、上下に余裕のある場所へクリックでカーソル配置、改行で度々再現します。
 これに近い正常な現象は次の様にすると見えます。カーソルをセット、画面外に追いやってからEnterで改行をすると、textareaのカーソルがtextarea内のてっぺんになります。こちらの場合はscrollTopが更新されます。

 この二つから推測されるのは、「何らかの原因で期待するscrollTopがとれず、仮にscrollTop=0と扱って改行を実行。正常な動作同様にtextareaのカーソルが画面内のてっぺんになるようにscrollTop変更。」という動作です。Chrome自体のコードは非公開ですし、詳しい内容はわかりませんがなかなか面白い現象です。
 余談ですがChromeのscrollTopは少数以下も取っていますがFireFox, Microsoft Edgeは少数以下を切り捨ててInt型の様に振る舞っていました。また、Operaは少数ありでした。加えてOperaもカーソルの挙動が不穏でした。少数絡みで何かしらあるのかもしれませんね。

著者:杉浦

【Laravel】モデル結合ルートを使ってコントローラの記述を簡略化

 サーバサイドプログラムのリクエスト処理部は、あるURLに対するリクエストを受け取り、リクエストに応じたデータを探して、もしなければ404を返し、と処理を行う必要があります。これを素で書くと例えば次の様になります。

// ルーティングファイル
Route::post('user/{id}', 'UserController@update');


// コントローラファイル
class UserController {
    public function update(){
        $id = request()->id ?: abort(404);
        $user = User::findOrFail($id);
        
        // $userに関連するレコードを更新する処理
        
        return $user;
    }
}

 Laravelのモデル結合ルートはこの処理を簡略化します。使い方は次の通りの名前と型の指定です。

// ルーティングファイル
Route::post('user/{user}', 'UserController@update');


// コントローラファイル
class UserController {
    public function update(User $user){
        // $userに関連するレコードを更新する処理
        
        return $user;
    }
}

 この様にすると、例えばuser/1365とpostを投げた時、User::findOrFail(1365);と同等の動作が実行されupdate関数にはUser::findOrFail(1365);の返り値のUserインスタンスが渡されます。毎度同じ手順で処理を行う必要がなくなり、楽になります。
ルーティング 5.8 Laravel

 上記まではモデル結合ルートの基本的な使い方であり、Laravelのデフォルトです。これは主キーで見つけるfindと同等の動作を仕組みを用いていますが、実際には主キーでない何らかのカラムでwhereをかけてモデルを取得したいパターンがあります。そのような時には次の様にモデル内でルートのwhereに使うカラムの名前を指定します。

// vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
    /**
     * @inheritDoc
     */
    public function getRouteKeyName()
    {
        return 'モデル結合ルートで使いたいカラム名';
    }

 このgetRouteKeyName()は次の様にルーティング用のクエリ構築メソッドresolveRouteBindingty中で使われています。このメソッドで返されるModelインスタンスがモデル結合ルートによって渡されるModelのインスタンスになります。

// vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
    /**
     * Retrieve the model for a bound value.
     *
     * @param  mixed  $value
     * @return \Illuminate\Database\Eloquent\Model|null
     */
    public function resolveRouteBinding($value)
    {
        return $this->where($this->getRouteKeyName(), $value)->first();
    }

 getRouteKeyNameを書き換えることでwhere句の指定するカラム名を書き換えました。もっと深く掘り進めてresolveRouteBindingを書き換えることによって更に大きくモデル結合ルートを改造できます。例えば次です。

    public function resolveRouteBinding($value)
    {
        return $this->where($this->getRouteKeyName(), $value)
            ->where('status', '<>', config('const.status.freeze'))
            ->first();
    }

 凍結状態のレコードが選ばれた時404を返す様にします。ちょっとしたグローバルスコープのパターンです。

65

著者:杉浦

【JavaScript】for…inであるオブジェクトにできることを探す

 JavaScriptのfor…inは列挙可能プロパティを総なめする構文です。

for…in – JavaScript | MDN
 for…inはforEachと異なり数え上げが不可能なオブジェクトに対しても行えます。例えばブラウザのwindowです。

 これを用いてグローバル汚染を見つけるブックマークレットがあります。

javascript:(function () {
    var r = '', w = open('', '', 'width=600,height=700');
    for (var p in window) if (!(p in w)) r += p + '<br>';
    w.document.write(r);
})();

 
グローバル汚染を見つける – Qiita

使用例はワードプレスの編集画面です。jQueryを始めとして混雑しています。
 このコードは新規にまっさらなwindowを作り、それに含まれない元のwindow以下についているプロパティはグローバル領域に追加されたものと判断し、新規windowに記述していく、というものです。

 JavaScriptにおいてfunctionはオブジェクトでありプロパティです。このため、ある機能的なライブラリの出力したオブジェクトについてfor…inをかけるとfunctionを探し、できることを探せます。例えば次です。テストツール’@vue/test-utils’のできることを探しました。実行可能な関数の名前が列挙されています。これだけでも説明書なしでできることがまあまあ分かります。

著者:杉浦

ある要素のセレクタの取得を簡易にするChrome拡張Get Unique CSS Selectorの紹介

 Chromeをはじめとしたwebブラウザの開発者ツールにはHTMLコードからある要素のセレクタを抜き出す機能が備わっています。これだけでも便利なのですが、いちいちHTMLコードの中を探すのは面倒ですし、時には開発者ツールを開くことすら億劫な場合があります。Get Unique CSS Selectorはそういった時の助けになります。

Get Unique CSS Selector – Chrome ウェブストア

AntonLapshin/getselector: Get Selector Browser Extension
 使い方は簡単で、右上の拡張アイコンをクリックしてセレクタコピー準備、対象の要素の上にマウスを移動、右クリックからCopy Unique Selector to Clipboardを実行、だけです。一連の動作が次の動画です。

 これを合わせるとセレクタを使ったUIテストやwebページの説明書作成などが素の状態よりいくらか楽になります。

著者:杉浦

【Python】誤った引数に値を渡さないためのキーワード引数

 キーワード引数は造語という程ないにしろ正式な語ではありません。可変長引数の一種です。ですがググれば多く出るのでそう間違いでない通称の一つです。

 長大な引数の関数を用いる際、時々引数の順や数え間違いで事故が起こります。何かしらの構造体を使うのが一つの解決策です。構造体中に引数への正しい接続を埋め込み、使用する際には簡単にするAdapterパターンは構造体を用いる接続方法の典型的な解決策の一つです。

// 使いたい関数hogeMember。これを使うためにhogeMember('hoge', 'fuga', 'foo', 'bar',...)と大量の引数を正しい順序で用意することになる。何度も繰り返したり、変更が行われればミスが起きる。
function hogeMember($name, $code, $email, $number, $location, $birthday_year, $birthday_week, $birthday_day, $old)

// $member = new Member();として初期化した後に、$member->name='tarou';などと名前毎にプロパティをセット、$member->callHogeMember()とすることで誤りなくhogeMember()を呼び出せる。
class Member{
  public $name;
  public $code;
  public $email;
  public $number;
  public $location;
  public $birthday_year;
  public $birthday_week;
  public $birthday_day;
  public $old;
  
  public function callHogeMember(){
    hogeMember(
      $this->name,
      $this->code,
      $this->email,
      $this->number,
      $this->location,
      $this->birthday_year,
      $this->birthday_week,
      $this->birthday_day,
      $this->old,
    );
  }
}

 ですが、ただ誤りなく関数を呼ぶためだけのオブジェクトは無駄な気がしてしまいます。特にパラメータが疎な時はなおさらです。キーワード引数はそういった問題を解決します。
 Pythonのキーワード引数は例えば次の様に使います。

HEADERS = {
    "x-api-token": 'apiToken'
}
PARAMS = {
    "mailAddress": randomname(10) + "@example.com",
    "password": "password",
}

response = requests.post(url='https://hogehoge/api/member', headers=HEADERS, params=PARAMS)# 引数順不同
response = requests.post(params=PARAMS, headers=HEADERS, url='https://hogehoge/api/member')# ↑と同じ結果になる

 値に名前をつけて関数を呼ぶことによって、対応したキーの引数に値が渡せます。大体のライブラリは適切にキー名をつけてくれています。キーワード引数を用いることによって、わざわざ準備することなく適切に多様な引数を持つ関数を呼び出せます。

著者:杉浦

【Python】PowerPointのファイルをpythonで作るpython-pptxの紹介

 Pythonは様々なライブラリを組み合わせて、多様な作業をプログラミングできます。pptxファイル(PowerPointのファイル)の作成も、またできます。python-pptxはこれを実現します。

python-pptx — python-pptx 0.6.17 documentation
 何ができるかはpptxファイルの構造を知ると少しわかりやすいです。pptxファイルはその実zipファイルと同じ形式です。pptxをzipに変えると例えば、次の様になります。

 解凍に成功しました。pptxファイルとは特定のファイル構造のファイル群を圧縮したzipファイルのことであり、zipファイル⊃pptxファイルとなっているわけです。この中から、どのページに、どのテンプレートが使われて、テンプレート中のある空欄に、何が、どう埋まっているか、が読み取れます。python-pptxではこの構造の指定に従ってページを構成していきます。
 テンプレート構造から細かく作っていく方法はGUIからpptxを作る場合、おそらくあまりしないでしょう。必然python-pptxは普段使いからかけ離れた理解の難しいものになります。そういった際にはpython-pptxを用いたhogehogeな関数を作った、というwebページを探すとよいでしょう。わかりやすいです。
 python-pptxは例えば、次の様に記述できます。

from pptx import Presentation
import datetime
from glob import glob
from pptx.util import Pt

def makeManual():
    prs = Presentation('template.pptx')
    prs = addContentPage(
        prs, '検索画面使用方法',
        '上部の検索ボックスに検索対象の語を入力し、検索ボタンを押すことで検索ができます。\nいずれの検索画面でも同様に検索ができます。',
        [], 'Tests_Browser_TestCase_Admin_Member_SearchTest_testSearchHogeHoge_000.png'
    )
    prs.save('管理者画面操作マニュアル.pptx')


# スライドにタイトル、サブタイトル、本文、画像ファイルを追加
def addContentPage(prs, title, subtitle, texts, fn):
    fnms = glob('%s/%s' % (__screenshots_dir, fn)) # 画像ファイル読み込み
    slide = prs.slides.add_slide(prs.slide_layouts[1]) # pptxファイルにスライド追加
    # slide = addTextToSlide(slide, title, subtitle, texts) # スライドにタイトル、サブタイトル、本文を追加
    addPictureToSlide(prs, slide, fnms[0]) # スライドに画像追加
    return prs


# スライドに画像を追加
def addPictureToSlide(prs, slide, fnm):
    pic = slide.shapes.add_picture(fnm, 0, 0)

    # 縦0.8倍。画像の比率変えず
    base = pic.height
    pic.height = int(prs.slide_height * 0.7)
    pic.width = int(pic.width * (pic.height / base))
    # 左右中央揃え
    pic.left = int((prs.slide_width - pic.width) / 2)
    # 下揃え
    pic.top = int((prs.slide_height - pic.height))

    return slide

 上記のコードはtemplate.pptxというテンプレートに従ってpptxファイルオブジェクトを生成。それに画像を追加して保存する、というコードになります。これだけでも画像を更新する度にpythonを走らせるだけでpptxを更新できます。特定の形式のpptxを高頻度で変更する場合、多少わかりにくくともpptx生成をプログラミング化すると作業が大きくはかどります。

著者:杉浦

【Laravel】Laravelのdumpメッセージをより便利に使うブックマークレット

 Laravelのdump, dd関数は与えた値をきれいに表示してくれます。しかし、ネストの深いオブジェクトであるとクリック数が多くなってしまいます。

 この記事で紹介するブックマークレットはこれを解決します。
ページ中のaタグを全部クリック
中身を整理すると次です。

(function () {
    document.querySelectorAll('a').forEach(function (e) {
        e.click();
    })
})()

 document以下のaタグを全てクリックするのみです。dump, dd関数の結果の折り畳みの開閉はaタグで行われています。

 このブックマークレットはそれを全てクリックすることによって、開閉を一気に操作します。これによってちまちまクリックすることなく全貌を知ることが出来ます。

著者:杉浦

デザイン法則を体感しながら確認できるwebページLaws of UXの紹介

Home | Laws of UX
 Laws of UXはユーザインタフェースを作る時に考慮するとよい法則を実際に表現しながら紹介してくれるページです。
 例えば近いもの同士をまとまっていると感じる近接の法則は、

 の様に画像付きで紹介されています。より詳しくは単体ページLaw of Proximity | Laws of UXにあり、概要、起源、参考文献などを示してくれます。
 デザイン法則は、そんなことは当然だ、と思わされる法則がいくつもあります。これはデザイン法則が人間の直感を明示的に言語化することによって生まれるためです。元々なんとなくはわかっている法則が多いのです。その様なモノを改めて全て考慮するというのは難しいです。しかし考慮せずに作られたものと考慮して作られたものを比べると、秩序の有無がはっきりします。デザイン法則を利用できるのであれば、利用するべきでしょう。

著者:杉浦

【Laravel】null値のプロパティ参照を避けるoptional関数

ヘルパ 5.5 Laravel#optional
 optionalはLaravelのヘルパ関数のひとつです。次の様な使い方でnull値のプロパティを参照しようとする致命的エラーを防げます。

return optional($user->address)->street;// address===nullでもstreetを参照せずにnullをretuenする

{!! old('name', optional($user)->name) !!}// $user===nullでもnameを参照せずにnullをoldの第二引数にする

 これは特にプロパティの連鎖するリレーションで強力です。このヘルパ関数は一見素晴らしいですがその実用性は程々です。issetよりいくらかまし程度です。具体的に何が問題化というと

optional($user->address->city)->name // addressがnullだと引数生成時点でnullのプロパティ参照を行おうとして致命的エラー
optional($user->address)->city->name // cityがnullだとnull値のnameプロパティ参照を行おうとして致命的エラー

 の様に3段先を見る時点で安全性が担保されなくなります。

optional(optional($user->address)->city)->name

 とすると無駄に長くなりissetと変わりません。これはoptional関数が行っていることが、Optionalクラスのインスタンスを生成し、そのマジックメソッドを用いて通常のアクセスに代わって直下のメソッド、プロパティの参照ミスをnull値に書き換える、という処理のみであることが原因です。
 Optionalクラスのファイルはvendor/laravel/framework/src/Illuminate/Support/Optional.phpにあります。任意の型の引数を自由に操るためのクラスというだけあってなかなかの抽象度ですが、100行程の短いクラスにまとめられています。これを参考に自前で再帰optional関数を作るのもよいかもしれません(おそらく呼び出された時点のコードから先を実行前に読み取るか、プロパティもoptional型に加工することになる)。