localdisk

PHP とか Java とか Web とか好きなことを書きます。

Ansible と Vagrant で HHVM をためそう

作ったよ

入れてるもの

  • Ubuntu 12.04 64bit
  • Nginx
  • HHVM
  • Composer
  • Redis
  • MySQL
  • Laravel

Laravel が入ってるのは仕様です。上記は全部 task として分けてあるので必要ないものとかは外しちゃってください。

学んだこと

  • apt-get update するときに cache_valid_time つけておくと毎回走らなくていい感じ
  • HHVM のインストールはかなり楽になった
  • Laravel のインストールで Composer 使ってるんだけど HHVM はすぐタイムアウトするので ResourceLimit.SocketDefaultTimeout を設定しなければならない
    • server.ini を編集すればよいのだろうか? 不明。

不安なこと

  • Redis はソースからインストールするのめんどかったので野良っぽいリポジトリからインストールしてる

不具合

Laravel をインストールした後に chmod -R 777 laravel/app/storage してるんだけど、起動時に生成される laravel.log やセッションが permission denied になる。どうすればいいんだっけ?

最後に

変なとこあったら、このエントリにコメントいただけるか @localdisk まだリプライください。Pull Request でもOK.

Laravel 勉強会福岡に行ってきました

有給とって遠方の勉強会に参加する喜び、プライスレス。

というわけでLaravel勉強会福岡 〜Fukuoka.php企画〜 - Fukuoka.php | Doorkeeperに参加してきました。大阪から @shin1x1 さんと @msng さんが来られてて非常に有意義な勉強会になりました。ネットを介してコミュニケーションを取るのと、実際に会って色々話すのとでは得られるもの(というか性質)が違うなーと改めて思った次第。

Laravel 勉強会福岡では大阪から来たお二人と僕で色々とお話させていただきました。内容は

@shin1x1さん
Laravel のここがいいよねという話
@msng さん
Opauthの話
作ったサンプルアプリの話

僕が作ったサンプルアプリはTuts+に公開されている twitter クローンである Ribbitを Laravel 4.1 で実装したものです。ソースはgithubにありますのでよしなにしてください。何かのお役にたてば幸い。

勉強会での個人的なハイライトとしては「スライド作らずにやろうかな」と言っていたにも関わらず、発表時にはきっちりスライドを仕上げてきた @shin1x1 さんと @msng さんでしょうか。ソースコードをプロジェクタに映して説明した僕だけ、すごい浮いてました。マラソン大会で「一緒に走ろうな」と言われて、ゴール直前で置いて行かれたあの気分を久しぶりに味わいました。ちくしょう。

最後にお知らせですが、4/26 にFuelPHP&CodeIgniter ユーザの集い #4 : ATNDに参加します。CodeIgniterをモダン方面に改造した話をしたいと思います。

Laravel の Model Event


[FuelPHPのORMでinsert前とかをフックして何か処理をする (´・ω・`) - 杏z 学習帳

を見て、Laravel の Model Event を思い出したので書いておきます。

上記みたいに Model をあれやこれやする前後に処理をはさみたい! ということがあると思います。Laravel には Model Event という仕組みが用意されていて楽に書くことができます。

書き方

インサートする前にログを出力したい場合です。

<?php
class Sample extends Eloquent {
    // boot をオーバーライド
    protected static function boot()
    {
        parent::boot();
        // インサート前なので creating メソッドを定義
        self::creating(function($ribbit)
        {
            Log::info('保存するよ');
        });
    }
}

Model Event は、Event クラスの仕組みが使われており、Model 周りはかなり細かくイベントが定義されています。

定義されている Mode Event

creating
インサート前
created
インサート後
updating
アップデート前
updated
アップデート後
saving
インサート前及びアップデート前
saved
インサート後及びアップデート後
deleting
デリート前
deleted
デリート後
restoring
ソフトデリート復帰前
restored
ソフトデリート復帰後

Model Observer

FuelPHP のように Model Observer を作成することもできます。

まず、Observer を用意して…。

<?php
class SampleObserver {
    public function creating($model)
    {
        if (条件) {
            // return false するとインサートは行われません
            return false;
        }
    }
}

Observer を定義します。

<?php
Sample::observe(new SampleObserver);

で OK です。

最後に

4/4 19:30 に Laravel 勉強会が開催されます。

予定が空いてる方はぜひ。

Laravel 勉強会福岡 が 4/4 に開催されます

4/4 19:30 より Fusuc さんで行われます。僕も大分から遊びにいくのでよろしくおねがいします。大阪から 新原さん増永さんが来ます(豪華!)。

きっと楽しくてためになると思います。予定の空いてる方はぜひ。

NetBeans Connector が荒ぶる場合の対処方法

NetBeans8 がリリースされて早一週間。便利に使ってますか? 僕は使ってます。

さて、NetBeans には NetBeans Connector という Chrome 拡張があってこれを導入するとプロジェクト単位で LiveReload できるという便利機能があります。

でまぁ日々便利に使っているわけですが、どうもブラウザの更新が頻繁に走るので ??? となることがあります。このたび原因と対処法がわかったのでブログに書きます。

原因

ログが出力されたりキャッシュができるたびに LiveReload が走ってた。あぁ…ってなりました。あぁ…そういうことね…。

対処法

プロジェクトのプロパティの "無視されたフォルダ" にログ出力されるフォルダを指定する。f:id:localdisk:20140327112859j:plain

オチ

f:id:localdisk:20140327113115j:plain

ちゃんと書いてるじゃないか。俺のバカ!バカ!!

Laravel の Event クラスを使って処理をまとめてみる

Laravel の Event クラスは、任意の Event を発行できます。今回はメール送信処理をまとめてみましょう。

参考
イベント

まずは Event::fire でイベントを発行します。

<?php
public function postRegister()
{
    // ユーザ登録
    ………
    // メール送信
    Event::fire('emails.user.register', ['件名', 'fromメールアドレス', 'toメールアドレス', ['データ']]);
}

この例では、ユーザー登録後、emails.user.register というイベントを発行しています。

そしてイベントの受け取りは以下のようになります。

<?php
// app/start/global.php
Event::listen('emails.*', function($subject, $from, $to, $data)
{
    Mail::pretend(); // デバッグ用
    Mail::send(Event::firing(), $data, function($message) use ($from, $to, $subject)
    {
        $message->from($from)->to($to)->subject($subject);
    });
});

Event::listen の第一引数の emails.*ワイルドカードリスナーといって emails. 以降のどんな文字列のイベントが発行されても受け取ることができます。

そして、Mail::send の第一引数である Event::firing は発行されたイベントの文字列が取得できます(この場合は emails.user.register)。

今回は、イベント発行の文字列とメールテンプレート(app/views/emails/user/register.blade.php)の場所を一致させることによってメール送信処理をこのようにまとめてみました。

こちらからは以上です。

Laravelでプライマリキーを使った1:1関連のテーブル分割で自動採番をしないようにする

を Laravel でも同じようにできるよな。と思って書いてみたらちょっとハマったので Laravel 使っている人は気をつけましょうという話。

問題はカラム毎に charset の指定ができないので、カラム長を 255 にして utf8mb4 を default charset にするといわゆる 767bytes 問題が発生する。

参考
MySQLのPK・UNIQUEのフィールド長制限に立ち向かう - treeのメモ帳

解決方法としては

  1. DB::statement を使用して ALTER TABLE してカラムの charset を指定する。
  2. カラム長を短くする。
  3. utf8mb4 をやめる。

2 と 3 は 根本的な解決にはなりませんよね?*1 というわけで 1 の方法で解決をはかりましょう。

マイグレートファイル

users
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateUsersTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
        Schema::create('users', function(Blueprint $table) {
            $table->increments('id');
            $table->string('email')->unique();
            $table->string('password');
            $table->timestamps();
            $table->index('email');
        });
        DB::statement('ALTER TABLE users MODIFY email varchar(255) CHARACTER SET ascii COLLATE ascii_bin');
        DB::statement('ALTER TABLE users MODIFY password varchar(255) CHARACTER SET ascii COLLATE ascii_bin');
	}


	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
	    Schema::drop('users');
	}

}
profiles
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateProfilesTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
        Schema::create('profiles', function(Blueprint $table) {
            $table->integer('id')->unsigned();
            $table->string('name')->nullable();
            $table->tinyInteger('gender')->nullable(); 
            $table->dateTime('birthday')->nullable();
            $table->timestamps();
            $table->primary('id');
        });
	}


	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
	    Schema::drop('profiles');
	}

}

で OK です。

最後に

migrate の SQL を確認したいときは pretend オプションをつけると確認できます。
上記 2 テーブルのマイグレート SQL はこんな感じです。

$php artisan migrate --pretend

CreateUsersTable: create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null, `password` varchar(255) not null, `created_at` timestamp default 0 not null, `updated_at` timestamp default 0 not null) default character set utf8 collate utf8mb4
CreateUsersTable: alter table `users` add index users_email_index(`email`)
CreateUsersTable: alter table `users` add unique users_email_unique(`email`)
CreateUsersTable: ALTER TABLE users MODIFY email varchar(255) CHARACTER SET ascii COLLATE ascii_bin
CreateUsersTable: ALTER TABLE users MODIFY password varchar(255) CHARACTER SET ascii COLLATE ascii_bin
CreateProfilesTable: create table `profiles` (`id` int unsigned not null, `name` varchar(255) null, `gender` tinyint null, `birthday` datetime null, `created_at` timestamp default 0 not null, `updated_at` timestamp default 0 not null) default character set utf8 collate utf8mb4
CreateProfilesTable: alter table `profiles` add primary key profiles_id_primary(`id`)

(追記)モデルの実装

モデルではこんな感じ。hasOne 及び belongsTo の第2引数がポイントです。

<?php
class User extends Eloquent
{
    public function profile()
    {
        return $this->hasOne('Profile', 'id');
    }
}
class Profile extends Eloquent
{
    public function user()
    {
        return $this->belongsTo('User', 'id');
    }
}

$profile = User::find(1)->profile;

*1:ぜひ、リンクを踏んでイラッとしていただきたい

ルートパラメータをフィルターから取得する

小ネタ。フィルターからルートパラメータを取得する方法。

<?php
// filters.php
Route::filter('param', function() {
    // Route は \Illuminate\Routing\Router のエイリアス
    // なので Route::current で \Illuminate\Routing\Route
    // クラスを取得して parameter メソッドを呼べば取れる
    dd(Route::current()->parameter('name'));
});

// routes.php
Route::get('/user/{name}', ['before' => 'param', function($name) {
    return;
}]);
// http://localhost/user/hoge
// -> hoge

個人ユースに WordPress は必要ない

上記を乱暴にまとめちゃうと「WordPress を個人のブログで使うのはオーバースペックじゃね?」という話でそれはそのとおり。
WordPressって使うのは簡単だけど、使いこなすのは難しい。テーマやプラグインは膨大で自分が必要なものを探すにはある程度の情報収集力を必要とするし、導入したものが安全とは限らない

自由に広告をはりたいとか、SEO で優位に立ちたいという理由で WordPress を選ぶのは悪手でしかない。livedoor Blogはてなブログ の有料版を使えばいいんじゃないかと思います。

お金を掛けたくないんなら、それこそHTMLを書いてFTPでアップロードする古式ゆかしい方法を使う手もある。日替わりタイトルのようにそっけないデザインでスマホにも対応していないのに、かなりのアクセスを誇るサイトもある。*1
面倒だけど難しいことはなにもないのでおすすめ。

最初は頑張れる!という人なら静的サイトジェネレータを検討してみるのもありだ。

それでも WordPress を使いたい人は転んでも泣かないように。

最後にどうでもよい話だけど、WordPressmysql 関数の使用から mysqli 関数への移行を進めてるみたい。なんで PDO じゃないん?

WordPress/wp-includes/wp-db.php at master · WordPress/WordPress · GitHub

*1:積極的にアフィリエイトを活用しているわけでもないのに個人としてはかなりの収入がある。大事なのは面白いコンテンツを継続して提供すること。

NetBeans をうまく使って Grunt とおさらばする

Grunt は確かに便利なんだけど「それ NetBeans でできるよ」という話。

LiveReload

NetBeans Connector 使えばOK.

  1. Chrome Web Store - NetBeans ConnectorChrome にインストール
  2. プロジェクトのプロパティ -> ブラウザ で "NetBeans Connector 組込みの Chrome" を選択する
    • f:id:localdisk:20140225094210j:plain
  3. あとは NetBeans からプロジェクトを実行するだけ

これで LiveReload を使う必要がなくなります。

SCSS/LESS のコンパイル

NetBeans は標準で対応してます。
参考: NetBeans で Sass を使う - localdisk

CSS/Javascript の minify

JS CSS Minify Compress Plugin を使う。あるいは Assetic をデプロイ時に叩くような仕組みを作る。Rocketeer のタスクに組み込めばかなり自由にあれこれできるので面白いです。

まとめ

流行の開発環境に目移りするのも分かりますし悪いことでもないとは思うのですが、今自分が使っている開発環境を見なおして磨いてみるのも一つの選択肢として頭に入れておくとより捗るのではないかと思います。