localdisk

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

CakePHPer のための Laravel 入門 - ブログチュートリアル1

CakePHPer のための Laravel 入門 - インストール - localdisk の続き。

さて、前回はインストール、環境の設定(localに設定しました)、拡張のインストールを行いました。今回はメインである CakePHP のブログチュートリアルを Laravel で実装してみましょう。

データベースの設定

まずは、cake-to-laravel/app/config/local/database.php を設定しましょう。前回触れましたが、Laravel は環境ごとの設定を容易に作成することができます。今回は local 環境のデータベースを SQLite を使用する設定を行います。たった一行追加するだけです。

<?php

return array(
    'default'     => 'sqlite', // <- ここを追加!
    'connections' => array(
        'mysql' => array(
            'driver'    => 'mysql',
            'host'      => 'localhost',
            'database'  => 'homestead',
            'username'  => 'homestead',
            'password'  => 'secret',
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ),
        'pgsql' => array(
            'driver'   => 'pgsql',
            'host'     => 'localhost',
            'database' => 'homestead',
            'username' => 'homestead',
            'password' => 'secret',
            'charset'  => 'utf8',
            'prefix'   => '',
            'schema'   => 'public',
        ),
    ),
);

これで local 環境は、SQLite を使用することになります。

マイグレーション

Laravel では、データベースのスキーマコントロールにマイグレーションを使用します。CakePHP でも同様のことができますがプラグインが必要です。まずはマイグレーションを使用できるようにするために以下のコマンドをターミナルから実行してください。

$ php artisan migrate:install

コマンドを実行したあと、以下のように表示されれば成功です。

Migration table created successfully.

テーブルの作成

マイグレーションの設定ができたら posts テーブルを作成しましょう。ターミナルで以下のコマンドを実行してください。

$ php artisan generate:migration create_posts_table

コマンド実行すると以下のように表示されます。

Created: /Users/localdisk/NetBeansProjects/cake-to-laravel/app/database/migrations/2014_08_03_061640_create_posts_table.php
Generating optimized class loader 

はい、作成できましたね。ではこのファイルを覗いてみましょう。

<?php

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

class CreatePostsTable extends Migration
{

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function(Blueprint $table)
        {
            $table->increments('id');
            $table->timestamps();
        });
    }

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

}

ちゃんと出来ていますね。では、 up メソッドを修正しましょう。2行追加するだけです。

<?php
class CreatePostsTable extends Migration
{

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('title', 50); // 追加
            $table->text('body'); //  追加
            $table->timestamps();
        });
    }
}

修正したら、以下のコマンドを実行して posts テーブルを作成します。

$ php artisan migrate

コマンド実行すると以下のように表示されます。

Migrated: 2014_08_03_061640_create_posts_table

はい、これでテーブルができました。次はモデルを作成しましょう。

Post テーブルの作成

posts テーブルと対になる Post モデルを作成します。ターミナルで以下のコマンドを実行してください。

$ php artisan generate:model Post 

コマンド実行すると以下のように表示されます。

Migrated: 2014_08_03_061640_create_posts_table

これで Post モデルが作成されました。

<?php

class Post extends \Eloquent {
	protected $fillable = [];
}

シード

作成した posts テーブルに表示用の初期値を設定します。

$  php artisan generate:seed posts 

コマンド実行すると以下のように表示されます。

Created: /Users/localdisk/NetBeansProjects/cake-to-laravel/app/database/seeds/PostsTableSeeder.php  

これで PostsTableSeeder.php が作成されました。では、内容を確認してみましょう。

<?php

// Composer: "fzaninotto/faker": "v1.4.0"
use Faker\Factory as Faker;

class PostsTableSeeder extends Seeder
{

    public function run()
    {
        $faker = Faker::create();

        foreach (range(1, 10) as $index) {
            Post::create([
            ]);
        }
    }

}

生成されたファイルには Faker の記述がありますが、今回は使用しません。結構便利なライブラリなので、興味があるならぜひ使用してみてください。
では、ブログチュートリアルにそってデータを作成していきましょう。

<?php

class PostsTableSeeder extends Seeder
{

    public function run()
    {
        Post::create([
            'title' => 'タイトル',
            'body'  => 'これは、記事の本文です。'
        ]);
        Post::create([
            'title' => 'またタイトル',
            'body'  => 'そこに本文が続きます。'
        ]);
        Post::create([
            'title' => 'タイトルの逆襲',
            'body'  => 'こりゃ本当にわくわくする!うそ。'
        ]);
    }

}

そして DatabaseSeeder.php を以下のように修正します。

<?php

class DatabaseSeeder extends Seeder
{

    /**
     * データベースシード(初期値設定)を実行
     *
     * @return void
     */
    public function run()
    {
        Eloquent::unguard();
        // PostTableSeeder を実行する

        $this->call('PostsTableSeeder');
    }

}

では、db:seed コマンドを実行して初期値をテーブルに設定しましょう。

$ php artisan db:seed

コマンド実行すると以下のように表示されます。

Seeded: PostsTableSeeder

これでデータベースに初期値が設定されました。

続く!

CakePHPer のための Laravel 入門 - インストール

CakePHP のブログチュートリアルを Laravel で実装してみました。手順をなるだけ詳細に説明します。CakePHPer で Laravel に興味を持っている人に参考になれば幸い。

Laravel のインストール

必要なもの

laravelja.phar

Laravel のインストーラー phar ファイルをダウンロードします。

  1. @HiroKwsさんの作成したインストーラー日本語版をダウンロードします
    1. laravel.phar
  2. ターミナルで以下のコマンドを実行します。
    1. $ php laravelja.phar new -l ja -s cake-to-laravel
    2. すると cake-to-laravel というディレクトリの下に Laravel がインストールされます。
    3. phar ファイルでのインストールは Composer のそれと比べて早いのと日本語化されたメッセージファイルがインストールされるのでおすすめ。
  3. インストールされた Laravel は最新版ではない(4.2.1)ので composer update します。
    1. $ composer update
    2. これで 4.2.7 にバージョンアップされます (2014/08/02 現在)
    3. 川瀬さんが対応してくれました!ブログ書いててよかった!

環境を設定する

Laravel も他のフレームワークと同じく手軽に複数の環境を扱うことができます(local / testing / staging / production) 。環境の設定は cake-to-laravel/bootstrap/start.php 内の $app->detectEnvironment で行います。デフォルトの環境は production です。環境はホスト名で決定します。 例外として* は全てに当てはまります。今回は local に設定します。cake-to-laravel/bootstrap/start.php を以下のように設定してください。

<?php
$env = $app->detectEnvironment(array(
    'production' => array('production.host'), // production のホスト名
    'staging'    => array('staging.host'),  // ステージングのホスト名
    'local'      => array('*'),  // それ以外はローカル
));

開発中は local に設定しておきましょう。

設定ファイルについて

Laravel の設定ファイルは app/config にあります。この下に環境名(今回の場合は local)の名前のついたディレクトリを作成し、オーバーライドしたい同名の設定ファイルを設置することによって、環境ごとの設定を行うことができます。

必須パッケージをインストールする

以下2つのパッケージをインストールします。

composer.json を編集

cake-to-laravel の直下の composer.json を以下のように書き換えてください。コピペでOK。

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "laravel/framework": "4.2.*"
    },
    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php"
        ]
    },
    "require-dev": {
        "barryvdh/laravel-ide-helper": "1.*",
        "way/generators": "dev-master"
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan ide-helper:generate",
            "php artisan optimize"
        ],
        "post-create-project-cmd": [
            "php artisan key:generate"
        ]
    },
    "config": {
        "preferred-install": "dist"
    },
    "minimum-stability": "stable"
}

app.php を編集

cake-to-laravel/app/config/app.phpproviders の配列に以下を追加します。

<?php
return array(
    'providers'       => array(
        ・・・
        'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
        'Way\Generators\GeneratorsServiceProvider',
    ),
);

ファイルを編集したら $ composer update してください。

動作確認

では、Laravel の動作確認をしてみましょう。ターミナルで以下のコマンドを入力して下さい。

$ php artisan serve

すると

Laravel development server started on http://localhost:8000

とターミナルに表示されると思います。ブラウザから http://localhost:8000 にアクセスしてみてください。

f:id:localdisk:20140803034528p:plain

上記画面が、ブラウザに表示されれば正常に処理されています。

続く!

Laravel の unique ルールとソフトデリート

先月中旬から東京に出張行ってるんですが、通勤のストレス*1から部屋(くっそ狭いマンスリーマンション)で勉強する気力が沸かなかったのですが、1ヶ月弱経ちまして少しは回復したのでまずはブログでリハビリ。

フォーラムに投稿された質問

メールアドレスを他の人と被らないよう、バリデーションを「users,mail_address」としました。
usersテーブル自体はソフトデリートを有効にしています。 この場合、削除したユーザのメールアドレスと、新しく登録するユーザのメールアドレスが同じ場合、 バリデーションに引っ掛かって登録することができません。
どうにかソフトデリートとユニークなメールアドレスを両立させる方法はないでしょうか。

[解決済み] バリデーションで「unique」を指定した際のソフトデリートとの連携について - laravel.jp

Laravel の Validator からはモデルがソフトデリート(論理削除を簡単に実装できる機能)を使ってるかどうかわからないので unique ルールを適用したら削除したモデルがひっかかって辛いという話です。

再現するとこんな感じです。

<?php

// マイグレーションファイル(抜粋)
<?php

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

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('email');
            $table->softDeletes();
            $table->timestamps();
        });
    }
}
// シードファイル(抜粋)
class UserTableSeeder extends Seeder
{

    public function run()
    {
        // Model の $unguarded を true にすると楽
        // これ豆知識な
        User::unguard();
        User::create([
            'email' => 'test@example.com'
        ]);
    }

}
Route::get('unique', function()
{
    User::destory(1); // 削除した!
    $email = 'test@example.com';
    $v     = Validator::make(['email' => $email], ['email' => 'unique:users']);
    // 削除したけどバリデーションはエラー!
    var_dump($v->fails());
});

ソフトデリートの場合、削除されるとカラムの deleted_at に日付を入れるだけなので unique で発行される SQL ではダメなわけです。ちなみにこんなSQLが発行されます。

select count(*) as aggregate from "users" where "email" = ?

上記 SQL の結果が 0 の場合 unique バリデーションは通過します。

解決方法

クエリーへWHERE節として追加される条件を追加することも可能です。

<?php
'email' => 'unique:users,email_address,NULL,id,account_id,1'
v4.2:バリデーション

なのでこんな感じで書いて上げればOK.

<?php
Validator::make(['email' => $email], ['email' => 'unique:users,email,NULL,id,deleted_at,NULL']);

こうするとこんなSQLが発行されます。

select count(*) as aggregate from "users" where "email" = ? and "deleted_at" is null

where 節が付加されているのがわかりますね。

*1:なんか抜け毛が増えた気がする

Laravel で簡易APIサーバーを作ってみた

風邪をひいて一日中臥せっていた。が、さすがにずっと眠れるわけもないので意味もなく blog を更新してみる試み。

Sinatraで簡易APIサーバーを作ってみた | Developers.IO の Laravel 版。

Laravel について

Laravelは表現力に富むエレガントな記述が使用できるWebアプリケーションのフレームワークです。

Laravel-イントロダクション

多分トレーズ閣下くらいエレガント。

ソース

Gist を作ってある。

説明は割愛。見ればなんとなくわかると思う。元エントリである Sinatra と比べてみてほしい。

動作確認

では、実際に動かしてみよう。ターミナル(あるいはコマンド・プロンプト)で Laravel のプロジェクトにカレントディレクトリを移動させて以下のコマンドを実行する。

$ php artisan serve

こうするだけで PHP の Built-in web server が起動する。あとはブラウザから http://localhost:8000/show にアクセスしてみよう。

{"id":1,"title":"today's dialy","content":"It's a sunny day."}

こんな感じで出力されていると思う。余談だが、return Response::json($article); の部分を return json_encode($article); しても同じ結果に見えるが Response::json を使用すると Content-Typeapplication/json にしてくれる。json_encode 使うと text/html になるので実際おすすめできない。

次は POST リクエスト。元エントリと同じく curl を使用してリクエストを投げる。まずは body を未指定で POST する。

curl -X POST -I http://localhost:8000/edit

結果はこんな感じ

HTTP/1.0 400 Bad Request
(以下略)

return App::abort(400); がちゃんと機能しているのがわかる。

次は body を指定して POST する。

curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"title":"MyFavorite","content":"Color is Black."}' http://localhost:8000/edit -w "\n%{http_code}\n"

結果は

略
{"title":"MyFavorite","content":"Color is Black."}
200

リクエストされたものがそのまま返却された。

言いたいこと

Laravel は簡単なことはちゃんと簡単に書ける。僕らは難しいことに頭をひねろう。

Typetalk Hack Fukuoka に行ってきたよ

ブログを書くまでが勉強会です。
Typetalk Hack Fukuoka on Zusaar

API を見ると、とりあえず叩いてみる習性があるので行ってきました。
感想としては、90 分でなにか作るのは結構厳しいなぁ…とか思ってたんですが発表を聞いている限りみんなすごかったので、こりゃあ頑張らなきゃなあとおもいました(小並感)。

発表で印象に残っているのは @kiwanami さんのS式をポストすると結果を返す変態botcakephper さんのtwilio APIマッシュアップさせてtypetalk にボイスメッセージを届くようにする仕組み。

僕はなぜか、backlog-api-php のテスト書いてました。typetalk-api-phpリポジトリだけは作ったので近いうちにリリースしたい。

その後の懇親会は(いや、全体的に)@hayashi_77無双でした。とても愉快な方です。

楽しかったのでまたやって欲しいなぁ。

Laravel 4.2 Beta をインストールする

これが一番簡単だと思います。

$ git clone -b develop https://github.com/laravel/laravel.git laravel-beta 
$ cd laravel-beta
$ composer install

変更点等は @HiroKws のツイートをチェックするべし。
注意点ですが、trait を使用しているため PHP5.4 以上になっているのでそこだけ気をつけてください。

追記

今現在開発しているアプリケーションを 4.2 にあげたい人は composer.json を修正しましょう。

{

    "require": {
        "laravel/framework": "4.2.*"
    },
    "minimum-stability": "dev"
}

この2箇所を修正すればOKです。

Laravel で Controller から Controller を呼ぶ


たまーに、こんなことしたいことがありますね。で、まぁリダイレクトすればいいよ、みたいな回答したんですが、よく考えたら普通にできるよなーと。

まず、呼び出される CalledController.

<?php

class CalledController extends \BaseController
{

    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function getUser()
    {
        return $this->user;
    }
}

この getUser を呼び出したいとします。

で、呼び出す側。CallController はこんな実装になります。

<?php

class CallController extends \BaseController
{

    public function getIndex()
    {
        $called = app()->make('CalledController');
        $user   = $called->getUser();
        var_dump($user);
    }
}

app()->make('CalledController') でコンテナ経由で CalledControllerインスタンスを取得しています。なので CalledController に定義されているコンストラクタインジェクションが機能しててちゃんとインジェクションされた User クラスが取得できるわけです。

もちろんコンストラクタCalledController をインジェクションすることもできます。

<?php

class CallController extends \BaseController
{

    protected $called;

    public function __construct(CalledController $called)
    {
        $this->called = $called;
    }

    public function getIndex()
    {
        $user = $this->called->getUser();
        var_dump($user);
    }
}

まぁ、Controller から別の Controller のメソッドを呼びたいというシチュエーションが発生した場合、リファクタリングのチャンスと思ってリファクタリングしたほうがいいんじゃないかなーと思います。色々あってそうも行かない時等にこのエントリを思い出していただければ幸い。

Guzzle を使って XML-RPC を利用する

作る前はめんどくさいかなーと思ってたら簡単だった。

22 行目の xmlrpc_encode_request は、XML-RPC のめんどくさい XML を作ってくれる関数。もう一つは 25 行目の auth 部分。こう書けば HTTP HEADER をちゃんと付加してくれる。ほんとは user/password のほかに認証形式が必要なんだけどデフォルトが Basic 認証なのでそのまま。

さよなら PEAR XML-RPC. 君にはお世話になった。

Typetalk Hack Fukuoka で Services_Backlog を作り直します

むかーし、Services_BacklogというBacklogAPIPHP Wrapper を作った*1んだけど、PEAR も時代遅れだし、対応しているAPIが少ないとか、なんか動かなくて困ってる人とかいて、心の中で謝罪を繰り返していたんですが、ちょうど上記のイベントが開催されるようなのでちゃんと作りなおそうと思います。余裕があれば typetalk の PHP API Wrapper も作りたいところ。

もう大体できてる。

手抜き部分*2やテスト書いたらリリースします。もちろん Composer 対応です。今回は PSR-4 使ってみた。

(2014/05/14 追記)
こっそりリリース localdisk/backlog-api-php - Packagist

あと、明日は Fukuoka.jvm #1 に行ってきます。ここのところ、Java で頭使う仕事全然してなくて*3 Java 力が落ちる一方なのでここらへんで喝を入れたい。

*1:devworks っていうのは僕の旧アカウントです。

*2:xmlrpc_decode が入ってない人対応とか。

*3:同じような Web アプリケーションばっかり作ってた。