著者アーカイブ 杉浦

著者:杉浦

お手軽に機能付きテーブルを作るJavaScriptライブラリDataTables

DataTables | Table plug-in for jQuery
 DataTablesはJavaScriptのみでテーブルにページネーション、検索、並び替えを付与するライブラリです。
bootstrap4のスタイルをベースにdatatablesを爆速で利用してみた(CDN利用) – Qiita
に載っているコードをJSFiddleで動かしてみたのが次です。

 きれいなインデントのHTMLコード64行だけで構成されています。要は簡単ということです。tableタグと中身を用意してidをつけて次のコードで適用してもうおおよそ完成です。

$(document).ready( function () {
    $('#myTable').DataTable();
} );

 注意点として大量のデータを扱うのには向いていない点があります。基本ブラウザで閉じて扱うことを前提としており、対象のデータ全体をJavaScriptだけで扱おうとしています。このため大量のデータ(1万件でもう辛い)を扱おうとした場合、テーブルのデータをブラウザに渡すための最初のデータ読み込みでとても時間がかかります。ajaxを用いて外部サーバと連携することで逐次読み込みも可能なのですが、そちらはフォーマットの調整が面倒な上、サーバ上で渡すデータを加工すればDataTablesを用いるまでもありません。
 欠点をあげましたが少なめのデータで機能付きのテーブルを作る際にはとてもいいものです。

著者:杉浦

投影における経緯度座標及び平面直角座標相互間の座標換算についてのより簡明な計算方法

河瀬和重, Gauss-Krüger 投影における経緯度座標及び平面直角座標相互間の座標換算についてのより簡明な計算方法, 国土地理院時報 No.121 (2011), pp 109-124.
 平面直角座標系は地球上の狭い範囲のみを扱うという条件を付けて、ある範囲を平面として考える座標系です。一度に扱う範囲が狭いため地球を平面とみなしても問題が起きません。平面であるが故に扱いが楽でGIS(地理情報システム)など様々な場所で使われています。
 一方でGNSS(衛星測位システム)は緯度経度を基礎として扱っているシステムです。GNSSは地球全体を統一して扱うため緯度経度の方がより合っています。そのためGISとGNSSを用いるシステムを作る、扱う際には緯度経度と平面直角座標の変換が必須です。
 国土地理院には便利なプログラム・データとして平面直角座標への換算とその計算式があります。
河瀬和重, Gauss-Krüger 投影における経緯度座標及び平面直角座標相互間の座標換算についてのより簡明な計算方法, 国土地理院時報 No.121 (2011), pp 109-124.
はこの計算式の原著で、なぜこの式にまとまるのかの話と実装例が載っています。勉強としても参考としてもおすすめの一本です。

著者:杉浦

Pythonのrequestsでmultipart/form-data形式通信を行う

 multipart/form-dataは文書、ファイル、またはバイト列の性質や形式を示す標準の一つです。multipart/form-dataは複数の部分から成るHTMLフォームのデータです。
MIME タイプ – HTTP | MDN #multipartform-data
 multipart/form-dataはごちゃまぜの種類のデータをまとめて送る時に便利です。これをどうやって使うかですがJavaScriptならば描画しないフォームを作り、それを送ることができます。次のコードはある画像ファイルとその画像ファイルの加工データを送信するのコードの一部です。JavaScriptはHTMLを扱う機能が数多く備わっており、フォーム形式の通信もJavaScript単独でパパっと書けます。

const formData = new FormData();// フォーム作成
formData.append('original_file', this.file);// フォームに値を入力
formData.append('cropped_file', croppieResponse);// フォームに値を入力

// multipart/form-data形式を指定して通信
axios.post(this.uploadDistUrl,
    formData,
    {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    }
).then((uploadImgResponse) => {
  // 通信成功時処理
}).catch(() => {
  this.vodalMsg = '通信に失敗しました。';
});

 pythonにはrequestsというHTTP通信用ライブラリがあります。これは人間に優しいを謳い文句にするくらい簡単に動かせることが特徴です。どのくらい楽かというと次の読みやすいインデント、改行の10行ないコードだけのファイルでpostを投げられるくらいです。

import requests

request_data = {
    'id': 'hoge',
    'password': 'fuga',
}

response = requests.post('http://127.0.0.1:8000/api/hoge', params=request_data)
print(response)

 便利なrequestsでmultipart/form-dataで送る方法を調べたところ次のコードで実現できました。
Quickstart — Requests 2.21.0 documentation#post-a-multipart-encoded-file

import requests

request_data = {
    'id': 'hoge',
    'password': 'fuga',
}
files = {'image': ('hoge_image',
                   'data:image/png;base64,000000000',
                   'image/png')}

response = requests.post('http://127.0.0.1:8000/api/hoge', params=request_data, files=files)
print(response)

 filesの様に色々形式毎の引数を用意して渡せば、まとめてよしなに送信してくれます。とてもとても便利ですね。

著者:杉浦

Null object patternによる未定義エラー防止

 存在しない場所を参照することはエラーの原因です。下図はそれぞれPHP、JavaScriptで未定義箇所を参照した時のエラーです。この手のエラーは時にプログラムが止まる致命的エラーになるので防ぐ必要があります。


 防ぎ方でよくあるパターンが、もし未定義か否かで条件文を用いるパターンです。


 この方法は稀に用いる程度ならば問題ないですが、ネストの深いオブジェクトでこの未定義問題を気にする必要がある時もあります。そのような時に用いられるコーディングのパターンのがNull object patternです。
Null object pattern – Wikipedia
 Null object patternはメソッド、プロパティの未定義によるエラーを防ぐために空のメソッド、null値のプロパティを用意します。もしそのメソッド、プロパティを参照しても未定義ではないので致命的にエラーにならないわけです。


 サンプルは簡単なコードなので実感はわかないでしょうが、対象が巨大になる程、抜けがある場所が増える程、参照する場所が増える程、便利なコーディングのパターンになります。

著者:杉浦

テストを読みやすくする

一般的な設計原則として、「大切ではない詳細はユーザから隠し、大切な詳細は目立つようにする」べきだ。

Dustin Boswell; Trevor Foucher. リーダブルコード (Kindle の位置No.2601-2602). 株式会社オライリー・ジャパン. Kindle 版.

 コードのテストコードを記述する際、様々なテストケースでテストコードを作る必要があります。上記の引用文にある大切な詳細とはどの様なケースでテストをしているかの詳細で、大切でない詳細はどの様にテストをしているかです。テストとして実行される最も表面的な部分(メソッド)はテストケースを示す内容以外の記述が極力ないことが理想です。この原則を守るとテストケースを追加するユーザは次の様なテストコードの表面部分のみを読めばよくなります。

 このコードはある検索ページのテストです。検索項目の種類(自由入力、日付入力、ドロップダウン、ラジオボタン)ごとにテンプレート化することでテスト内部を共通化しています。この様にした場合、機能追加、テスト実装漏れがあったとしてもテストを追加することが苦にならなくなります。また何をテストしたかもすぐにわかります。このコードを実装するために必要なのは入力部、出力部の共通化です。必要とされる引数、操作の種類が同じならば、テストも共通化できます。これはテストが行うことが、入力部にある値を投げて、期待通りの出力が返ってくるか確かめることだからです。多くのプログラミング言語が持つインタフェースの仕組みを用いると入出力を共通化したコーディングが自然にできます。

著者:杉浦

PHPStormでartisanコマンドの予測入力をする

 PHPフレームワークLaravelにはartisanコマンドがあります。artisanはアプリケーション開発全体で役に立つ、数多くのコマンドを提供しています。例えば必須ないしあった方がよいコードを持ったモデル、テスト、ミドルウェアの作成、データベースのまとまった操作、Laravelが今持つことになるルーティングリストの表示です。もちろん他にも様々なものがあります。
 artisanの一覧を見ることはphp artisan listからできますが、いちいち確認するのは面倒です。PHPStormのコマンドラインツールにはSymfonyのコマンドライン用クラス\Symfony\Component\Console\Command\Commandを元に作成されたコマンドのコマンドライン入力とサポートするプラグインCommand Line Tool Supportがあります。LaravelのartisanコマンドはSymfonyのCommandクラスから継承されて作られています。次の図はCommandクラスの継承先のリストの一部です。各aritsanコマンドの名が冠されたクラスが並んでいます。
 
 プラグインは設定->プラグインのマーケットプレイスから検索できます。インストールできたなら設定->ツール->コマンド・ライン・ツールのサポートを選び、追加からTool Based on Symfony Consoleを選択、php実行ファイルとartisanファイルのパスを設定して完成です。


 これでartisanコマンドの予測ができるようになりました。artisanを介することでnamespaceや必須メソッド、継承元などをいちいち記述せずとも各基本クラスのひな型を作ることができ、データベースの操作もまとめてでき、開発がはかどります。

著者:杉浦

ORDER BY句で指定するカラムはFROM句の頭のテーブルの方が高速

ORDER BY 句と別の GROUP BY 句がある場合、または、ORDER BY または GROUP BY に結合キュー内の最初のテーブルと異なるテーブルのカラムが含まれている場合は、一時テーブルが作成されます。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 8.4.4 MySQL が内部一時テーブルを使用する仕組み

 この一時テーブルを作る動作が案外重く、FROM句の最初のテーブルで指定できるならば、そちらで指定しましょうという話です。具体的には下図です。結合に用いている同じ値を持つmembers.primary_keyとpoints.member_keyそれぞれによるソートを用いたクエリの実行時間です。10数回程度しか試していませんが、ここまで差があるならば違いがあることは明らかです。

 一時テーブルが作られているかどうかを確かめるためにはEXPLAIN句、その結果が次の図等です(どちらもtype=Allなのは気にしないでください)。それぞれ上がmembers.primary_keyによるソート、下がpoints.member_keyによるソートです。上側のEXtraにのみあるUsing temporaryは一時テーブルを作るという意味です。これがある場合FROM句の最初のテーブル中のカラムでソートを指定するとかで、どうにか消すと高速化が望めます。

著者:杉浦

Vue.jsの単一コンポーネント用styleタグ属性scoped

 Vue.jsには単一コンポーネントという仕組みがあります。これは単一のファイルの中にHTML構造テンプレート、JavaScriptによる動的操作、CSSによるデザイン指定を書き込む仕組みです。単一コンポーネントを使うことによって、あるファイルを差し込むだけで完成されたパーツを差し込めます。

 コンポーネントのデザインを単一にまとめるために必要なのがscoped属性です。

<style scoped>

</style>

 これが何をやっているかというと、次の画像の様なdata-\d+の様なVue.jsが設定するカスタム属性を指定した上でスタイルを指定しています。

 scopedが無いとbodyタグ末尾にjsファイル呼び出しを置く時、あるコンポーネントがページ全体を崩します。あるライブラリを入れたらデザインが崩れたなんて時はscopedの有無を疑うのがいいでしょう。

著者:杉浦

長方形の画像をCSSだけで真円に表示

 画像を円形にして出力するためにはborder-radius:50%のようにborder-radiusが指定されがちです。画像が正方形の場合はこれだけでもきれいな円形になります(サンプル1)。しかし画像が長方形の場合、角が丸くなりますが画像サイズに合わせた楕円になります(サンプル2)。どのような画像であっても真円にしたい、という時は背景画像の仕組みを用いることで実現できます(サンプル3)。
 このサンプル3は次のCSSコードで実現できます。

.img-box {
  background-image:url(https://lorempixel.com/400/300/animals/1/est);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center,center;
  width: 100%;
  height: 0;
  padding-bottom: 100%;
  border-radius: 50%;
}

 width,height,padding-bottomの部分で親要素と同じ横幅、親要素の横幅と同じ高さの空白で埋め尽くされた(paddingのパーセントは親要素の”横幅”基準)divを作ります。このdivをborder-radius:50%をかけることで親要素の横幅と同じ長さの直径を持つ円形のdivにします。

 backgroundが接頭辞のコードで背景画像として、縦横比が崩れず端が切り取られる1枚の画像を上下左右中央に配置します。
background-size | MDN

cover
画像の縦横比を崩すことなく、画像ができるだけ大きくなるよう拡大縮小します。画像の縦横比が要素と異なる場合、空き領域が残らないように上下または左右のどちらかを切り取ります。

background-repeat | MDN

no-repeat 画像は繰り返し描画されません (したがって背景画像描画領域が完全に埋め尽くされるとは限りません)。背景画像の位置は background-position CSS プロパティで定義されます。

background-positiont | MDN

center のキーワード値の場合は、画像を中央揃えにします。


 この二つを組み合わせて真円の形の画像を表示できます。

著者:杉浦

ソースコード間の関わりをシンプルにするFacadeパターン

Facade パターン – Wikipedia
 Facadeパターンはプログラミングのデザインパターンの一つです。Facadeは建物の正面を意味する語で、Facadeパターンは建物全体でなく正面だけを見ればよいようにコーディングを行う方法です。もし多数のクラス、関数、データが協調するプログラムを記述する際、すべてを同等に扱ったり散らかったままにすると、どこから何をどの手順で取ってくるのが良い方法か分かりにくくなります。ひどいことに、このクラスを呼ぶ前にはこの関数とあの関数をあらかじめ使っておき、データをどうこう加工しておく必要がある、なんてことにもなります。そういった事態を避けるために、モジュール間のやり取りをシンプルにする窓口を作るというのがFacadeパターンです。次の図は増補改訂版Java言語で学ぶデザインパターン入門に載っているFacadeパターンのクラス図です。ClientがシンプルなFacadeクラスという窓口を介することでClassA~Dを楽に扱えます。
 
 Laravelにはファサードという仕組みがあります。
ファサード 5.7 Laravel
 このファサードの中身を見ていくとFacadeパターンを体現しています。例えばAuthファサードは次です。雑多なAuth関連クラスである@see以下の4つをまとめ上げて、21の関数のアクセス方法を見せています。これを使うことが出来ると大変わかりやすく、便利です。これを使わず雑に、@see以下を読んでは継承で上書きしてテスト、読んでは上書きしてテスト、を繰り返すコーディングをするとあっという間に難解なコードになり後の修正やリファクタリングがとても面倒になります。

/**
 * @method static mixed guard(string|null $name = null)
 * @method static void shouldUse(string $name);
 * @method static bool check()
 * @method static bool guest()
 * @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
 * @method static int|null id()
 * @method static bool validate(array $credentials = [])
 * @method static void setUser(\Illuminate\Contracts\Auth\Authenticatable $user)
 * @method static bool attempt(array $credentials = [], bool $remember = false)
 * @method static bool once(array $credentials = [])
 * @method static void login(\Illuminate\Contracts\Auth\Authenticatable $user, bool $remember = false)
 * @method static \Illuminate\Contracts\Auth\Authenticatable loginUsingId(mixed $id, bool $remember = false)
 * @method static bool onceUsingId(mixed $id)
 * @method static bool viaRemember()
 * @method static void logout()
 * @method static \Symfony\Component\HttpFoundation\Response|null onceBasic(string $field = 'email',array $extraConditions = [])
 * @method static null|bool logoutOtherDevices(string $password, string $attribute = 'password')
 * @method static \Illuminate\Contracts\Auth\UserProvider|null createUserProvider(string $provider = null)
 * @method static \Illuminate\Auth\AuthManager extend(string $driver, \Closure $callback)
 * @method static \Illuminate\Auth\AuthManager provider(string $name, \Closure $callback)
 *
 * @see \Illuminate\Auth\AuthManager
 * @see \Illuminate\Contracts\Auth\Factory
 * @see \Illuminate\Contracts\Auth\Guard
 * @see \Illuminate\Contracts\Auth\StatefulGuard
 */
class Auth extends Facade