LaravelのEloquentモデルのプロパティアクセスをCamelにしてみる

久しぶりの投稿が技術ネタ

SlackのPHPユーザーズ(日本語)のLaravelチャンネルで変数の命名規則をどうしているかという議論がちょっとだけあった。
もう結論も出てるのでSlack上で蒸し返してもしかたないし、モデルのプロパティに関してはスネークケースで妥協すると言うことで落ち着いてるのであえてなにも言うつもりもないしそれでいいと思う。

ことの発端はEloquentモデルはDBのテーブルのカラム名でプロパティアクセス出来るように作られてる。
とはいえLaravelのFramework自体は変数はキャメルケースとなっている。
つまりDBのテーブルカラムがスネークケースの場合はプロパティアクセスはどうしてもスネークケースにならざる得ない。
DBのカラム名はPostgresだとキャメルケースで定義しても全部小文字になったりするものだから、こっちをキャメルケースに変えるということもできればしたくない。

1つの解としては、Eloquentモデルのaccessorとmutatorを使って各モデルごとにすべてのプロパティに対して定義すれば、プロパティアクセスもキャメルケースでアクセス可能だ。
これはSlack上でもこーすればできるという例で上げられていた。
しかし、プロパティアクセスをキャメルケースしたいだけで、各モデルにカラムすべてにaccessorとmutatorを定義するとか、拷問でしかない気がする。

この議論を見たときに思いついたのはこれ

Eloquentモデルを継承して、getAttribute($key)とsetAttribute($key, $value)をオーバーライドする。
$keyをスネークケースに変換して親のget/setAttributeを呼べばaccessorやmutatorを個々に定義しなくてもいいんじゃないか。
あとはすべてのモデルは作成したCamelModelを継承すればプロパティアクセスをキャメルケースで書くことができる。
これくらいの手間ならやってもいい気がする。
しかも、個別にaccessorやmutatorをLaravelのお作法通りに定義してもちゃんと動く。

まぁこれやっちゃうと、必ずスネークケースに変換されてしまうのでDBのカラムがスネークケースじゃないものが含まれてたときにプロパティが取れなくなるけど。
そんときはDBのカラムの命名規則違反だからカラム名を修正すべきだろう。

PHPでSQL Serverに接続してみた。

故あってSQL Server(Windows Server)にLinuxOSから接続しなきゃいけなくなったので、とりあえずいろいろ試したメモ。

試した環境は
Windows8.1 SQL Server2014
CentOS7

どちらもMac上でParallels Desktopの仮想環境で立ち上げた。

以下のサイトを参考にSQL Serverを設定。
SQL Server 2014 Expressの外部接続を許可する手順
基本的にリモート接続の許可はデフォルトでチェックが入ってた。
TCP/IPは無効になってたので有効化して、SQL Serverの設定は完了。

ODBCデータソース アドミニストレータを使って確認してもいいし、sqlcmdでIP接続できてればOK。

CentOSのほうにFreeTDSとunixODBCをインストール。
同じことやってる人がちらほらいたけど、ほとんどがソースビルドしてた。
けど、EPELのリポジトリを登録すればFreeTDSもyumでインストールできた。

EPELのリポジトリ追加はこちら
CentOS6はこっち

以下のコマンドでunixODBCとFreeTDSをインストール


#sudo yum -y install unixODBC-devel freetds-devel

インストールが完了したら、以下を参考にしてドライバとデータソースの設定
unixODBCにODBC用のFreeTDSドライバーを登録

あとはPHPのODBC関数を使えばSQL Serverに接続することができた。

問題は、ODBC対応のPHPフレームワークが思い当たらないことか

追記
探してみたらCodeIgniterがODBCサポートしてた。
使うかどうかはまだ検討中だけども

わけあってPHPでSHA256を書いた

PHP4の環境でHMAC-SHA256が必要になったわけだけど当然hash_hmac()はPHP5からなので使えない。

Pearのライブラリもいろいろと探してみたけど、内部でmhash()つかってたり、自分でサーバー環境いじれない状態のところでやらなきゃいけなかったんでしかたなくアルゴリズムからコードに落としてみた。

とりあえずgithubに上げといた。

一応バグはないと思う。
問題はPHP4の環境でまだ動かしてないということ。
PHP5の環境ではとりあえず動いてた。
PHP4でダメなシンタックスがあるかもしれんけど、それはこれから。

パフォーマンス考えてたらデータ全部メモリに一度のっけちゃうのがいいのかもしれないけど64Byte単位で処理するようにした。
それでも内部の演算に乗算、除算はやらずにシフト演算で対応した。

C言語とかやってた時期はバイナリ操作やりまくってたけど、PHP触りだしてから全然やらなくなったから、文字列がバイナリデータだってことに気が回らず文字列をバイナリ化しようと悩んでしまった。

WebPayのPHPライブラリをV2に移行してみた。

WebPayが定期課金に対応して、PHPのライブラリも定期課金対応されたが
ライブラリのメジャーバージョンがアップしてた。

APIのバージョンは据え置きでライブラリのバージョンがあがったのみだけど
ライブラリのV1との互換性がなくなった。

とりあえずバージョン見て処理切り替えるようにコード変更しようと思ったわけだけど
ドキュメント見てもバージョンの取り方は書いてないし、ソース見てもWebPayクラスにバージョン情報がなかった…

V1のコードと見比べてメンバ変数に殆どが”s”がなくなってるのと
幾つか追加されたメンバ変数があるだけでその先のメソッド名の変更はないっぽい(全部は追いかけてないけど)

とりあえず、WebPayクラスのインスタンス生成は互換性あるんで
メンバ変数をproperty_exists()で判定できた。


$wabpay = new Webpay('secret-key');
$version = property_exists($webpay, 'charges') ? 1 : 2;

$charge = ($version === 1) ? $webpay->charges->create($data) : $webpay->charge->create($data);

だいたいこんな感じ

property_exists()が__get()をオーバーライドしてたからいけるか不安だったけど…

Laravelで特定のURL以下にフィルターを適応する

最近Laravelを使い始めていろいろと苦悩しています。

ドキュメントを読んでもさっぱりです。
言いたいことが伝わってきません。
まぁドキュメントについてはLaravelに限ったことではありませんけど…

で、本題です。

Laravelで特定のURLにはAuthとかもろもろ前処理をしたいときに

Route::filter('myFilter', function() {
// なんかのフィルタ処理
});

でフィルタを定義して、

Route::get('path', array('before' => 'myFilter'), function() {
  return 'before filter!';
});

で、URLにフィルタを紐付けるわけです。

まぁこれをいろいろなURLでフィルタを使うときにこれを全部書くとかめんどくさすぎる。
一応まとめてやるには公式ドキュメントにはこんなサンプルコードがあるわけですが

Route::filter('myFilter', function()
{
  //
});

Route::when('user/*', 'myFilter');

これだと、https://hoge.com/userにはフィルタがかからない。
あくまでhttps://hoge.com/user/以下に対してだけになる。
https://hoge.com/userにもフィルタをかけたい場合は

Route::when('user', 'myFilter');
Route::when('user/*', 'myFilter');

と、書けばいいんだけど、これはこれでなんか納得行かない
で、ものは試しにと

Route::when('user*', 'myFilter');

ってやったら、どっちにも対応した。
ただし、https://hoge.com/usersってのがあったらこれにもフィルタが掛かってしまう。

グルーピングを使ったほうが楽なんだろうか?
他にスマートな方法があったら教えて。
まぁこのブログには連絡先は書いてないわけだけど