Twe-td client for iPhoneを作った

というか、作ったのはTwe-td client for Androidよりも前なんだけど、ようやく公開されたんで。

機能としてはAndroid版とほぼ同じで、画面左右端あたりをタップするとアイテムの前後移動。画面下にある言語とタイプの左右の「<」「>」をタップすると、言語やタイプが切り替わる。画面右上にあるゴミ箱をタップするとそのアイテムが非表示になる。

Web版だと過去の日付を指定して、「○○語で○月×日に話題になった○○」とかを見ることができるけれども、アプリ版は「○○語で現在話題になっている○○」しか見ることができない。

単にユーザーインターフェースをシンプルにするために、それだけに機能を絞っているだけで、作れば簡単に作れるんだけど。

ところで、Android版にもiOS版にもGoogle Analyticsのアプリ版を仕込んでいるんだけど、プラットフォームごとにGoogle Analyticsのプロパティをそれぞれ別に設定してある。けど、同じアプリの別プラットフォームバージョンを作る場合って、別プラットフォームでも同じプロパティIDを使うのが普通なのかな?

せっかくGoogle Analyticsがその辺を識別して集計してくれるのに、わざわざ別IDに分けてしまうのはアホらしい気がする。と、公開されてから思った。

それにしても、Androidアプリの公開までのスピード感と比べると、iOSアプリのスピード感のなさは、自分で軽量の(作成に一週間もかからない)アプリを作るときには、致命的なくらいきついな。

Androidアプリは実験的なWebサービスを公開するのと大して変わらないスピード感でいけるけど、iOSアプリの10日近いタイムラグは、公開されるまで待っている間に、次のバージョンを開発するモチベーションがなくなるくらいだ。

こんなに間が空くと、既存のアプリをアップデートするよりも、別の新しいアプリを作ったほうがましな気分になってしまう。

Twe-td client for Androidを作った

Twe-td(http://twe-td.net/)のAndroidクライアントアプリを作りました。Google Play Storeからダウンロードできます。

howto

機能としては単純で、起動すると前回表示した言語/種類の最初のアイテムが表示されます。画面左右の端にある「<」「>」で表示するアイテムを前後に切り替えることができます(「<」「>」が表示されている部分だけでなく、画面端のほぼ全体でタッチに反応します)。

画面下側にある「Engilish」「Tweets」の部分が、現在表示しているアイテムの「言語」「種類」の設定で、その左右にある「<」「>」で切り替えることができます。種類は「Tweets(ツイート)」「Movies(ムービー)」「Pictures(写真)」「Goods(商品)」「All Types(すべてのURL)」が選べます。

スマートフォンでWeb版を表示すると、一度に表示するアイテム的に厳しい端末もあるのですが、アプリ版だと一度に1アイテムずつを素早く切り替えることができるので、たいていの端末で軽快に動作するはず。

iPhone版も1週間くらい前にAppleStoreに申請に出しているんだけど、いまだにWaiting For Reviewのままだなー。iPhone版を作った後に作り始めたAndroid版の方が先に公開されるとは。

【PC遠隔操作事件】第2回公判傍聴メモ・最初の検察側証人は「ファイルスラック領域」を強調(江川 紹子) – 個人 – Yahoo!ニュース

【PC遠隔操作事件】第2回公判傍聴メモ・最初の検察側証人は「ファイルスラック領域」を強調(江川 紹子) – 個人 – Yahoo!ニュースを読んでの感想をTwitterにいくつか書いたんだけど、なんか端的に書くと中途半端感が否めないんで、もうちょっとちゃんとまとめておく。

警察側の証言の要旨は、『押収したPCを調査した結果、OSレベルで見えるファイルシステムからは、事件に関連する情報はほとんど得られなかったが、HDDのセクターレベルに残された情報からは、事件に関連すると思われる情報がいくつか見つかった。それらは、そのPC上の暗号化された仮想Fドライブにおいて、問題のウイルスを開発した可能性が高い痕跡だった』と言うもの。

この仮想Fドライブの存在に関しては、片山氏も身に覚えがあるが、そこにウイルスのソースコードが置かれていたことには気がつかなかった、と言っている。そして、それらの情報は自分のPCが犯人によって遠隔操作されてできた、偽の証拠だろうと主張している(のだと思う)。

しかし犯人が、氏を犯人に仕立て上げるために偽の証拠を残そうとしたのだとしたら、どうして『暗号化ドライブ』のような『証拠が残りにくい』場所に証拠を残そうとしたのだろう。

暗号化されているため捜査側がそこから証拠を取り出すことは難しいし、実際警察は暗号化ドライブ自体から証拠を取り出すことはできていないようで、OSやプログラム開発環境が非暗号化ドライブ上でテンポラリ的に使用するファイルの痕跡から、暗号化ドライブ上での作業内容を推測している。わざわざ偽の証拠を残そうとしたにしては、あまりにも証拠が弱すぎる。

もちろん『犯人はわざとそういう遠回しな証拠を残したのだろう』という主張もできなくはない。しかし『自分のPC上で開発した痕跡をできるだけ隠そうとした』と考える方が自然だ。このあたりは犯人の能力をどのくらい大きく評価するか、でどちらの説が有力に思えるかが変わってくるだろう。

あるいは『犯人は、自分のPCでウイルスを開発して自分のPC上に痕跡を残すことを恐れて、すべて他人のPC上でウイルスを開発したのだ』という考え方も、あり得るかもしれない。偽の証拠を残そうとしたのではなく、片山氏のPCが犯人のメイン開発環境であった、という主張だ。

その場合は、できるだけ(普段PCを使用している片山氏にも)痕跡が見つからないように、暗号化ドライブ上で開発を行うことも十分にあり得るだろう。

ただその場合、Visual Studioのような比較的重い開発ツールを使って(しかもVisual Studioのインストール自体も犯人がリモートでやった? この辺片山氏はどう主張しているんだろう)、ウイルス関連の様々なプログラムの開発およびテストを、完全に氏のPCの遠隔操作のみで作る、というなかなか難易度が高い作業を犯人が行ったということになる。

また、Visual Studioを平日午前中に使用した形跡が残っていることを考えると、その日片山氏がそのPCで作業していたかどうかはわからないが、普通に会社が営業しているであろう時間帯にも、犯人は遠隔操作でウイルス開発作業を行っていた、という推測されるため、かなり状況の妥当性に無理が出てくる(それとも片山氏も、Visual Studio自体は(C#以外の言語で)使っていたと主張しているのかな?)。

推測も多いし、どちらにしろ決定的な判断基準となるような証拠はないわけだけど、今までマスコミ報道で出てきた怪しげな証拠と比べると、今回の話はとても真っ当な内容で、警察が片山氏を犯人と考えている理由は納得できた。それが裁判で有罪になるくらいの証拠なのかどうかはわからないし、ましてやコンピュータおよびプログラミングについて深い知識がない人間にどこまで通じるのかわからないけど。

あと、「遠隔操作ではファイルスラックのスペースは自由に残せない」というのは、警察が証拠を見つけ出したのはOSのインストールドライブからだと思われるので、遠隔操作するためのリモート接続プログラムは、OS上のアプリケーションやサービスとして動作している以上、遠隔操作している状況下でOSインストールドライブに対してHDDの低レベルアクセスはできないから、ということだと思われる。

chkdiskみたいなHDDへの低レベルアクセスが必要なプログラムを実行しようとすると、いったんWindowsを終了し、オンメモリで動作するDOS互換な環境で起動し直してから、そこで実行されるよね。あんな感じ。

Twe-td – what is tweeted in the world ? -を作りました。会社を辞めました

Twe-td(ツイーティド) [http://twe-td.net]というサービスを作りました。

これは何かというと、Twitter上でつぶやかれているツイートに含まれるURLを収集・解析し、世界中でどんなWebサイト、ツイート、ムービー、写真、グッズなどの話題が注目を集めているのかを、シンプルなインターフェースでみることができるサービスです。

「世界中で」というところがポイントで、Twe-tdでは収集した情報をつぶやかれた「言語」を軸に区分しています。

たとえば、トップページから「Let’s go!」で飛んだ先は「表示する種類:ツイート(Tweets)」+「表示する言語:全言語(All languages)」となっており、この記事を執筆している時点では、以下のような内容になっています。

表示する内容は、画面上下にあるナビゲーションから変更することができます。

twe-td-selector

一番左が表示する種類の選択肢で、「All(全種類)」「Tweets(ツイート)」「Movies(ムービー)」「Pictures(写真)」「Goods(グッズ)」から選べます。

真ん中が、対象となる言語の選択肢で「All languages(全言語)」「English(英語)」「Japanese(日本語)」を始め、Twitter上でツイートされている様々な言語が選択できます。一応投稿されているツイートが多い順に並んでいます。

一番右は対象の日付(GMT)となります。データ収集プロセスを動かし始めたのは2014-02-20くらいなのですが、いろいろ試行錯誤があり、まともにデータが取れ始めているのは2014-03-02以降となっています。

たとえば「表示する種類:ツイート(Tweets)」+「表示する言語:日本語(Japanese)」にしてみると、以下のような内容になります。

これをスペイン語(Spanish)のムービー(movies)に変えると、以下のようになります。

「ツイート」などは知らない言語だと内容がさっぱりわかりませんが、「写真」や「ムービー」ならば違う言語でもなんとなくわかるもの。言語圏の違いによって、流行にどのような違いが出てくるのか、わからないなりに眺めていても楽しいものです。

また、もうちょっと情報がたまってきたら、同じ言語でも「何月何日頃にはどういうものが流行っていたっけ?」みたいな探索にも使えるかもしれません。

ちなみに、ツイーター上に流れる大量のツイートから無作為にデータを収集しているため、収集結果にはいわゆる「公序良俗に反するもの」が含まれている場合があります。その場合、各URLのそばに表示されているゴミ箱マークをクリックしてもらえると、そのコンテンツを画面から削除することができます。

この削除行為は運営側にも通知が来るようになっていて、ユーザー削除が多いコンテンツはサーバー側でも削除されますので、やばそうなコンテンツを見かけたらまめに削除していただけると助かります。

「公序良俗に反するもの」の規定は明確に定めていませんが、ひとまずは「日本の法律的にNGなもの」は削除していこうかと思っています。また、「自動生成アカウントによる大量操作」「釣りアプリによる権限奪取を使った大量操作」なども、スパムとして削除対象にするつもりです。

まだ開発し始めたばかりですが、自分で使ってみてなかなか面白そうな感じにできてきつつあるので、興味を持った方は是非使ってみてください。

あと私事ですが、長い間お世話になった株式会社ジョルスを3月一杯で退職することになりました。在職中にお世話になった方々、今までありがとうございました。

充電しながら使えるBluetoothレシーバー+ケーブルが絡まないイヤフォン

Bluetoothレシーバーのバッテリーが切れるのにうんざりしている人、ポケットとかに適当にしまったイヤフォンを取り出すたびに、いちいち絡まったケーブルをほどくのにうんざりしている人にオススメ。最近買って大当たりだった組み合わせ。

まずは、micro USBケーブルで充電しながら使えて、バッテリー持続時間も8時間程度と長めのBluetoothレシーバー『BTTC-200』。値段も3000円ちょっとと、Bluetoothレシーバーとしてはお安め。

IMG_0106

micro USBで充電しながら使えるBluetoothレシーバー『BTCC-200』

私は普段移動しながらiPhone/Androidの音声を、仕事をしながらPC/Macの音声をイヤフォンで聞いている。そうやって一日中音声を聞くとなると、どんなにバッテリー持続時間が長いBluetoothレシーバーでも、短くて4時間、長くて8時間程度でバッテリーが切れてしまう。

しかもたいていのBluetoothレシーバーは、充電しながら使用することはできないので、せっかくBluetoothレシーバー経由で音声を聞いていても、バッテリーが切れるとBluetoothレシーバーを充電器にセットして、iPhoneやPCにイヤフォンを指し直す必要が出てくる。

でもBluetoothレシーバーで音声を聞くのに慣れてしまうと、iPhoneに直接イヤフォンをつないだ状態でのケーブルの取り回しがうざいし、PCなんかだとちょっと席から離れるときなんかに、いちいちイヤフォンを外すのも面倒くさい。Bluetoothレシーバーならばトイレに行くくらいの距離ならば、そのまま音楽を聴き続けることもできるのに。

というわけで、このBluetoothレシーバー『BTTC-200』。これならば普通に使っていても比較的バッテリーの持ちがいい上に、使っている最中にもmicro USBケーブルをつないで充電することができるので、PCで使っているときに時々micro USBケーブルをつないで充電しておけば、バッテリー切れの心配がなくなる。

小さくて軽いのでどんなに小さいポケットに入れても大丈夫。イメージとしてはちょー軽い懐中時計サイズ。

充電しながらの利用は、バッテリーの寿命にあまり良くない影響を与えるという定説があるわけだけど、まあ値段も大して高くはないんで、バッテリーが劣化したら買い直せばいい。

おまけとして、この『BTTC-200』はレシーバーとしての機能だけでなく、トランスミッター(送信機)としての機能も持っている。Bluetooth機能を持たないミニコンポとかのイヤフォン端子にこれをつなぐと、他のBluetoothレシーバーでその音声を受信することができる。バッテリーが劣化してきたら、micro USBケーブルつなぎっぱなしにして、そういう用途で使うのもいいかもしれない。

ずいぶんBluetoothレシーバー『BTTC-200』の紹介が長くなってしまったが、もう一つのオススメ商品は靴ひも風のケーブルを使ったイヤフォン『アルペックス クツヒモコードイヤフォン SLYP』。

ポケットにぐしゃぐしゃに突っ込んでから取り出した状態

普段から外出中も仕事中もイヤフォンを使っているんで、常時イヤフォンを持ち歩いていて、普段使っているイヤフォンは使わないときはポケットに突っ込んでしまうことが多い。そうすると、使おうとしてポケットから取り出すと、イヤフォンが絡んでしまっていて、それをほどいてから使わなければならなくなる。

じゃあ、ケーブルが絡まないような、巻き取り式のイヤフォンとかを使えばいいんじゃないか、と思っていくつか買ってみたのだが、どうも巻き取り式イヤフォンは、巻き取り部分の機構がなめらかに動かなかったり、最初のうちはちゃんと動いていても、そのうちだんだん動きがしぶくなっていったり、巻き取りの仕組みのためか普通のイヤフォンよりも断線などの故障率が高い(3、4個使ってみての経験談)など、今ひとつ満足いくものがなかった。

ポケットから取り出した状態からでも、すぐに使える状態に

そこで、この『アルペックス クツヒモコードイヤフォン SLYP』。「絡まないようにすることが難しいならば、絡んでもほどきやすくすればいいじゃない(マリー・アントワネット風に)」。

実際、ポケットにぐしゃぐしゃに突っ込んでおいても、ケーブルが太いためか絡んでいることが少ないし、多少絡んでいたとしても、すぐにほどける。これならば使わないときにポケットに突っ込んだり、カバンに突っ込んだりと、気を遣わずにしまっておいても大丈夫。

これをBluetoothレシーバー『BTTC-200』と組み合わせて使うと、いつでもポケットに突っ込んで持ち運び、使いたいときに取り出して接続したいデバイスとリンク、micro USBケーブルが確保できる場所では使いながら充電、といった感じの使い方で、ほぼ充電切れの心配なしに一日中使い続けられる。

というわけで、ここ数年間日常的に微妙なフラストレーションを感じていたBluetooth+イヤフォン関連の問題がすっきり解消されて幸せになれたので、久しぶりに物欲日記を書いてみた。

まだ耐久試験が終わっていないから、それによってある程度評価の上下はあるけれども、Bluetoothレシーバー『BTTC-200』の方は1年程度もってくれれば上等、『アルペックス クツヒモコードイヤフォン SLYP』の方は半年程度もってくれれば十分だ。もちろんそれ以上使えるならそれにこしたことはないけど。

LaravelをWindows環境で使う

最近メインマシンをMacbookに移行したんだけど、Laravel4で作ったアプリケーションをWindows環境で動かさなければならない要件があったんで、Windows環境にセットアップして問題ないかどうか試してみた。手元にあるのがほぼまっさらなWindows8.1(on Parallels)だったので、それをベースに。

まずXAMPP for Windows をインストール。今だとXAMPP 1.8.3(Apache 2.4.7/MySQL 5.6.14/PHP 5.5.6)だった。

インストール後、XAMPPコントロールパネルからApacheを起動して、http://localhost/にアクセスできるかどうか確認。

続いてLaravelをcomposerを使ってインストールするために、まずはPHPにパスを通す。コントロールパネルのシステムの詳細設定-環境変数から、システム環境変数のPathにPHPのパス(C:\xampp\php)を追加する。

試しにコマンドプロンプトを起動して、php -iとか実行してちゃんとPHPにパスが通っているかどうかを確認。すでにコマンドプロンプトが起動済みだった場合、環境変数の変更を反映させるためにコマンドプロンプトを再起動。

続いて、composerをインストール。コマンドプロンプトで、

cd C:\xampp\php
php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"

を実行してcomposer.pharをダウンロード。これだけだと使い勝手が悪いので、C:\xampp\php内にcomposer.batファイルを作成。内容は、

@echo off if "%PHPBIN%" == "" set PHPBIN=C:\xampp\php\php.exe "%PHPBIN%" "C:\xampp\php\composer.phar" %*

にしておく。これでコマンドプロンプトから、

composer update

みたいな感じでcomposerを実行できる。

でcomposer create-project laravel/laravelしようと思ったら、途中でgitがなくてこけた。gitも必要だったっけ。

Git for Windows(msysgit)をインストール。コマンドプロンプトから使いたいんで、インストールオプションで実行スタイルを選択するところで、「Run Git from the Windows Command Prompt」とか、gitにPathを通してくれるオプションを選択する。

もう一回コマンドプロンプトを再起動。Laravelアプリケーションプロジェクトを作りたいディレクトリを適当に作成する。

mkdir c:\laraveltest
cd c:\laraveltest

あとは、

composer create-project laravel/laravel .

今度はエラーも出ずにプロジェクトの作成に成功。Apacheからこのプロジェクトディレクトリを参照したい場合は、C:\xampp\apache\conf\httpd.confから、

DocumentRoot "C:/xampp/htdocs"
<Directory "C:/xampp/htdocs">

となっている部分を、

DocumentRoot "C:/laraveltest/public"
<Directory "C:/laraveltest/public">

なんて変えてから、Apacheを再起動すると、http://localhost/で今作ったプロジェクトにアクセスできる。まあC:\xampp\apache\conf\extra\httpd-vhosts.confとC:\Windows\System32\drivers\etc\hostsを使って、バーチャルホストにしておいた方が便利だろうけど。

前にWindows環境でLaravelを動かしたときは、mcryptとかのPHPモジュールを有効にしたり、timezone設定を追加したりとかしないといけなかったような気がするんだけど、今はそういう問題はないんだな。

このくらい手軽に動くなら、ピュアPHPで作れるようなアプリならば、Windows環境でも作りやすそうだ。

Laravel 4.0.10からLaravel 4.1.5にアップグレード

PHPフレームワークLaravelの4.1がリリースされた。手元に作りかけのLaravel 4アプリケーションがあったんで、今後のことを考えてとっととLaravel 4.1にアップグレードすることにした。手元のLaravel 4フレームワークのバージョンは4.0.10。

アプリケーションのスペックとしては、

  • 使用DBはsqliteのみ。ちょっとしたデートストア程度にしか使っていない。
  • JeffreyWay / Laravel-4-Generatorsで生成したマスタメンテ用Scaffoldページがいくつか。
  • JavaScriptアプリケーションのコンテナWebページが5ページほど。サーバーロジックはほとんどないのでroutes.phpでView::make()しているだけ。
  • メインのJavaScriptアプリケーションから呼ばれるJSON APIが10個ほど。DBとのやりとりがほとんどなので、routes.php内で直接Eloquent ORMを使って処理をして、JSONで結果を返すだけ。

といった感じ。小規模だしLaravel 4の機能は最低限しか使っていないから、移行のハードルは低い。

まずは、4.0から4.1へのアップグレードガイド日本語訳にざっと目を通す。私はLeanpubで日本語ドキュメントを買ってるんで、PDFで読んでるけど。

変更点としては、いくつかのファイルの差し替え、設定ファイル関連の変更がいくつか、コントローラの変更がちょっと。パスワードリマインダーも変更されたらしいけど、使ってないから内容は未チェック。作業ステップは一桁程度か。

まず、composer.jsonのrequireセクションにあるlaravel/frameworkのバージョンを4.0.*から4.1.*に変更。アップグレードガイドの手順とは違うけど、まずはそのままcomposer updateしてみる。

大量のremove&installの後で、Fatal Errorで終了。エラーは、

Error Output: PHP Fatal error: Call to undefined method Illuminate\Foundat
 ion\Application::redirectIfTrailingSlash() in /Users/ishinao/NetBeansProjec
 ts/webgldance/laravel/bootstrap/start.php on line 16

なんで、アップグレードガイドにある「Trailing Slashリダイレクトの削除」に引っかかったらしい。bootstrap/start.phpから,

$app->redirectIfTrailingSlash();

を削除。もう一度composer updateしたら、今度はエラーなしで正常終了した。

この段階で、試しにWebアプリケーションルートをブラウザで開いてみたところ、200 OKは返るけどコンテンツは何も返らない状態だった。

ということで、アップグレードガイドに書いてある作業を一つずつつぶしていくことにする。

まずはWebアプリケーション起動ファイルを新バージョンの内容に置き換え。public/index.phpを
https://raw.github.com/laravel/laravel/master/public/index.phpからダウンロードして差し替え。

同様に、artisanコマンドの内容もhttps://raw.github.com/laravel/laravel/master/artisanからダウンロードした新バージョンの内容に置き換え。

続いて、設定ファイルを新バージョンの内容に追随させていく。

app/config/app.phpの内容をhttps://raw.github.com/laravel/laravel/master/app/config/app.phpの内容を元に書き換える。変わっているのはprovidersとaliasesのところなので、自分で追加/編集した内容がなければ、丸ごと新しい方で置き換える。自分で追加した内容があればそれを残しつつ。

新しく追加されたhttps://raw.github.com/laravel/laravel/master/app/config/remote.phpをapp/config/remote.phpとして保存。

セッション設定ファイル app/config/session.phpに

'expire_on_close' => false,

を追加。あとで、app/storage/logsを見てみたら、

'Undefined index: expire_on_close' in vendor/laravel/framework/src/Illuminate/Session/Middleware.php:171

が残っていたんで、これが起動時に画面が表示されない原因だったっぽい。

新機能の失敗したジョブキューからの復帰用設定を追加。app/config/queue.phpに

  'failed' => array(
    'database' => 'mysql', 'table' => 'failed_jobs',
  ),

を追加。

これでWebアプリケーションルートにブラウザからアクセスしてみたところ、無事画面が表示された。

他にもコントローラ関連の修正項目が書かれていたけれども、Generatorsで自動生成したコントローラクラスは、特に修正する必要がなかった。

アップグレードしたバージョンは4.1.0かと思っていたら、php artisanしてみたら、

Laravel Framework version 4.1.5

になっていた。

追記@2013/12/14

アップグレードガイドがアップデートされ、アップグレード手順で一つ重要な内容が欠けていたので、その部分を追記。

$app->redirectIfTrailingSlash() 相当の処理を.htaccessで行うようになったため、bootstrap/start.phpからそのコードを削除したにもかかわらず、上記手順では.htaccessをLaravel 4のままで使っている。public/.htaccessをhttps://raw.github.com/laravel/laravel/master/public/.htaccessに差し替える必要がある。

ちなみに修正内容として、以下の内容が追加されている。

# Redirect Trailing Slashes...
RewriteRule ^(.*)/$ /$1 [L,R=301]

また、Laravel Framework本体もアップデートされており、composer updateすると、

Laravel Framework version 4.1.8

になっていた。アップデートペース、はやっ。

Laravel 4のエラーメッセージとフォームエラー

Laravel 4では、

Redirect::to()->withErrors()

をすると、自動的にリダイレクト後のビュー内で$errorsに展開されるなど、エラーメッセージは特別な扱いになっている。このエラーメッセージは共通レイアウトなんかで、

@if ($errors->count() > 0)
<ul class="error">
@foreach ($errors as $error)
<li>{{{ $error }}}</li>
@endforeach
@endif

なんて感じに表示するのが普通だろうけど、フォームエラーなんかで、入力欄のそばにエラーメッセージを表示したい場合は、どういうふうに使い分けるといいんだろう。

(エラー表示コードを含む)共通レイアウトは使わず、フォーム表示ビューの方で、

{{ Form::open() }}
{{ Form::label('name') }}
{{ Form::text('name') }}
{{ $errors->first('name', '<p class="error">:message</p>') }}
{{ Form::close() }}

みたいな感じに書くのがいいのか。でもそうなると、(エラーメッセージ表示をしたい)フォームを含むかどうかで、レイアウトファイルを(エラー表示コードを含む物と含まない物で)切り替えることになるのが、あんまり気持ちよくない。

あるいは

Redirect::to()->with('formErrors', $validator->messages)

なんて感じで、標準エラーメッセージを使わずに、独自のフラッシュセッションに保存して、

if (Session::has('formErrors')) {
    View::share('formErrors', Session::get('formErrors');
}

とかやる方がいいんだろうか。これはこれでありだろうけど、せっかく標準エラーメッセージ機能があるのに、別でハンドリングするのも今ひとつのような気もする。

標準エラーメッセージを使いつつ、フォームビューの方でエラーメッセージを表示した場合は、何らかの方法でフラグをセットして、レイアウトの方のエラーメッセージを抑止するという方法も考えたんだけど、(コンテンツ)ビューから(レイアウト)ビューにデータを渡す、ほどよい方法が見当たらないんで、ペンディング中。

$errorsの実装クラスであるMessageBagにMessageBag::clear()とかが実装されていれば話が早かったんだけど、MessageBagは値を追加したりマージしたりはできても、削除とかリセットとかはできない模様。

今のところ、独自フラッシュセッションを使う方法が一番ましかなーとは思っているんだけど、もっときれいな方法があるという方、情報をお待ちしております。

追記@2013/07/18)

って話は、Laravel 4のwithErrors()は、レイアウトファイルなどを使って、サイト全体で共通表示するフラッシュメッセージ的なものと捉えた上で書いていたんだけど、なんか違うような気がしてきた。

withErrors()で渡すエラーは、共通フラッシュメッセージ的なものではなく、あくまでも特定機能(ページ)でのエラーを手軽にハンドリングするための入れ物なのか? 共通フラッシュメッセージは、with()とかを使った独自処理として書くべき?

たとえば、Laravel-4-Bootstrap-Starter-Site/app/views/notifications.blade.php at master · andrew13/Laravel-4-Bootstrap-Starter-Siteなんかを見ると、共通フラッシュメッセージ的な物としては、success、warning、error、infoを直接Session::flash()(=with())経由でやりとりして表示し、withErrors()(=$errors)はフォームエラー表示専用として扱っているようだ。

withErrors()はあくまでフォームエラー専用の入れ物であって、フラッシュメッセージとは別物、というのが妥当なアプローチなのかな。なんかフォームエラー専用の代物を、フレームワーク標準で自動的にビュー変数にまで代入しちゃうのは、やり過ぎ感が否めないんだけどなー。

Laravel 4でfacebookログイン機能も作ってみる

Twitterログイン機能を作ってみたついでに、facebookログイン機能も作ってみる。ただfacebookはあんまり好きじゃないんで、APIのドキュメントを眺めてみたことがある程度しか知識がないから、Laravel 4以前に手探りで実装することになるんだけど。

まず、facebookのPHP SDKをインストールする。composer.jsonのrequireに

		"facebook/php-sdk": "dev-master"

を追加して、composer updateするとfacebook PHP SDKがインストールされる。

続いて、facebookアプリケーションの登録。facebook developersで新しいアプリを作成し、アプリのIDとシークレットキーを取得しておく。その設定は、app/config/facebook.phpとして、

<?php

return array(
    'appId' => '[アプリID]',
    'secret' => '[シークレットキー]',
);

って感じで保存しておくと、Laravel 4アプリ内ではConfig::get(‘facebook’)で取得できる。このあたりの設定ファイルの扱いもLaravel 4はシンプルでいいね。環境(本番/開発など)ごとにオーバーライドさせたければ、環境名のディレクトリを掘ってその中に入れればいいだけだし。

続いてDBの準備。Twitterログインと同じように、usersテーブルに直接facebookのユーザー情報を保持する設計にする。

        Schema::create('users', function($table) {
            $table->biginteger('id')->unsigned();
            $table->primary('id');
            $table->string('name');
            $table->string('access_token');
            $table->timestamps();
        });

facebookのユーザーIDは桁数が多いっぽいんでbigintegerにしておいた。あとは名前とアクセストークンを保存するようにしておく。

ログインルーティングは以下のような感じ。

Route::get('login', function() {
    $facebook = new Facebook(Config::get('facebook'));
    $config = array(
        'redirect_uri' => url('/login/callback'),
    );
    return Redirect::to($facebook->getLoginUrl($config));
});

facebook側の認証ページにリダイレクトさせ、その結果をコールバックURLに返してもらう。続いてコールバックのルーティング。

Route::get('login/callback', function() {
    $code = Input::get('code');
    if (strlen($code) == 0) {
        return Redirect::to('/')->with('message', 'ログインできませんでした。');
    }
    
    $facebook = new Facebook(Config::get('facebook'));
    $user_id = $facebook->getUser();
    
    if ($user_id == 0) {
        return Redirect::to('/')->with('message', 'ログインできませんでした。');
    }
    
    $user = User::find($user_id);
    if (empty($user)) {
        $user = new User;
        $user->id = $user_id;
    }
    
    $me = $facebook->api('/me');
    $user->name = $me['name'];
    $user->access_token = $facebook->getAccessToken();
    $user->save();

    Auth::login($user);
    
    return Redirect::to('/')->with('message', 'ログインしました。');
});

facebookログインの仕組みの全貌が把握できていないので、本当にこれでいいのかいまいち確信が持てていないが、少なくとも認証自体はできているはず。

Twitterの場合と同様に、最終的にはAuth::login()でLaravel側での認証情報をセットしている。

ログアウトは、Laravel側のログアウト処理だけ。

Route::get('logout', function() {
    Auth::logout();
    return Redirect::to('/');
});

ホーム画面にfacebookに登録されている名前くらいは表示するようにしておく。

Route::get('/', function()
{
    $data = array();
    
    if (Auth::check()) {
        $facebook = new Facebook(Config::get('facebook'));
        $me = $facebook->api('/me');
        $data['me'] = $me;
    }

    return View::make('home', $data);
});
// app/views/home.blade.php
@if (!empty($me))
    Hello, {{{ $me['name'] }}}
    <a href="/logout">ログアウト</a>
@else
    <a href="/login">ログイン</a>
@endif

今回はLaravel 4側の問題ではなくfacebook APIの仕組みを調べるのに手間取ってしまった。ちゃんと使うんだったら、もうちょっとfacebook APIのドキュメントを読み込まないとだめだなー。

Laravel 4でTwitterログイン機能を作ってみる

Laravel 4で、標準のAuth関連機能とTwitterのOAuth認証パッケージとを連動させて、Twitterでログイン&ユーザー登録機能を作ってみる。

Twitterのユーザー情報をそのまま自前のユーザーテーブルに保存して利用するんで、

php artisan migrate:make create_users_table

をしてmigrationファイルを生成し、以下のようなテーブルスキーマを定義する。

	public function up()
	{
		Schema::create('users', function($table) {
		    $table->integer('id')->unsigned();
		    $table->primary('id');
		    $table->string('screen_name');
		    $table->string('oauth_token');
		    $table->string('oauth_token_secret');
		    $table->timestamps();
		});
	}

	public function down()
	{
		Schema::drop('users');
	}

プライマリーキーのusers.idに、Twitterのuser_idを直接保存して利用するので、$table->increments(‘id’)にはせず(auto incrementにはせず)、unsigned intなプライマリーキーとして定義する。あと、Twitterの認証情報であるoauth_tokenとoauth_token_secretを保存するカラムも用意しておく。

Userモデルは以下のような感じ。

use Illuminate\Auth\UserInterface;

class User extends Eloquent implements UserInterface {

	public function getAuthIdentifier()
	{
		return $this->getKey();
	}

	public function getAuthPassword()
	{
		return null;
	}

}

Authで認証に利用するユーザークラスにはimplements UserInterfaceが必要なので、getAuthIdentifier()とgetAuthPassword()の二つを追加しておく。Twitter認証ではシステム独自のパスワードは必要ないのでgetAuthPassword()ではNULLを返している。

続いて、Twitter認証パッケージのTwitterOAuth Service Provider for Laravel 4をインストールする。インストール手順はドキュメント通り。composer.jsonに依存設定を追加して、composer updateしたり、config:publishしたり。

あと、dev.twitter.comでアプリケーション登録して認証キーの発行をおこない、app/config/packages/philo/twitter/config.phpにその値を登録しておく。

さてここからが実際のコーディング。Twitterログイン用のルーティング定義は以下のような感じになる。ほぼドキュメントに書かれている通り。

Route::get('login', function() {
    if (Auth::check()) {
        return Redirect::to('/')->with('message', 'ログイン済みです。');
    }
    $tokens = Twitter::oAuthRequestToken();
    Twitter::oAuthAuthorize(array_get($tokens, 'oauth_token'));
    die;
});

これで/loginにアクセスすると、Twitterの認証ページにリダイレクトする。Twitter側で認証されると呼ばれるコールバックURLは、dev.twitter.comで設定できるので、サンプルアプリの「/login/callback」に設定しておく。コールバック時の処理は以下のようになる。

Route::get('login/callback', function() {
    $token = Input::get('oauth_token');
    $verifier = Input::get('oauth_verifier');
    $accessToken = Twitter::oAuthAccessToken($token, $verifier);
    
    if (isset($accessToken['user_id'])) {
        $user_id = $accessToken['user_id'];
        $user = User::find($user_id);
        if (empty($user)) {
            $user = new User;
            $user->id = $user_id;
        }
        $user->screen_name = $accessToken['screen_name'];
        $user->oauth_token = $accessToken['oauth_token'];
        $user->oauth_token_secret = $accessToken['oauth_token_secret'];
        $user->save();

        Auth::login($user);
        
        return Redirect::to('/');
    } else {
        return Redirect::to('login')->with('message', 'Twitter認証できませんでした。');
    }
});

コールバックされたパラメータからアクセストークンを取得し、TwitterユーザーID($accessToken['user_id'])が取れていればログイン成功。そのTwitterユーザーIDがusersテーブルに登録されていない場合は、新しいレコードとして登録する。

screen_name、oauth_token、oauth_token_secretに関しては、ログインするたびに新しい情報に更新する。

で、Auth::login($user)がポイントで、これによってそのユーザーでログインしている状態をシステム(セッション)に記憶させる。

今回はusersテーブルに直接Twitterのログイン情報を持たせたんで、Twitter認証情報から直接Userクラスのインスタンスを取得できた。

もしも、Twitter認証情報をusersテーブル以外に持たせたい場合(通常は自前のID、パスワードでログインしつつ、必要ならばTwitterでもログインできるようにする、など)でも、最終的にUserクラス(というかUserIntafaceが実装されたクラス)のインスタンスを取得してしまえば同様にログイン処理が行える。

擬似コードとしては、以下のような感じ。

// Twitter認証テーブルから該当行を取得
$twitterUser = TwitterUser::find($accessToken['user_id']);

// Twitter認証テーブルとリンクしているUserテーブルの該当行を取得
$user = User::find($twitterUser['user_id']); 

Auth::login($user); // 該当ユーザーをログインさせる

一応ログアウト処理のルーティングも書いておく。これはTwitter認証とは関係なく、Laravel 4のログアウト処理のまま。

Route::get('logout', function() {
   Auth::logout();
   return Redirect::to('/')->with('message', 'ログアウトしました。');
});

あと、ホーム画面でログイン状況がわかるように書いておく。

Route::get('/', function()
{
	return View::make('home');
});
// app/views/home.blade.php
@if (Auth::check())
    {{{ Auth::user()->screen_name }}}ログイン中
    <a href="/logout">ログアウト</a>
@else
        未ログイン
    <a href="/login">ログイン</a>
@endif

未ログイン状態の場合は、ログインへのリンクを表示。ログイン中の場合は、ログインユーザーのscreen_nameを表示し、ログアウトリンクを表示している。

ちなみにこのTwitter認証パッケージは、Twitter APIパッケージを利用しているので、このままTwitter APIの機能を利用できる。たとえば、

$user = Auth::user();
Twitter::setOAuthToken($user->oauth_token);
Twitter::setOAuthTokenSecret($user->oauth_token_secret);
$timeline = Twitter::statusesUserTimeline($user->id);

なんて感じで、ユーザーのタイムラインを取得できる。

Laravel 4の認証処理周りはシンプルで、自前でいろいろ処理したい場合でも、なかなか使いやすそうだね。