人生の恥は書き捨て

プログラムとかいろいろ

fuelPHPでアプリケーションを作る前に知っておきたかったこと

はじめに

fuelPHPを使っていたら、あとからあとから、
こんな便利機能あったのかよ!自分で実装しちゃったよ...。
というのが出てきたので、今からfuelPHPを書き始める人のためにここに書いておきます。

といっても全部リファレンスに書いてあることなので、
リファレンスちゃんと読めって話だったのですが。

namespaceや独自のライブラリはbootstrap.phpに記述すれば簡単に増やせる。

bootstrap.phpに登録しさえすれば、自分の思うとおりにパッケージを切り分けられます。
fuelはMVCを基本としていますが、これを使えばドメイン駆動設計したり、
modelにならないようなクラスを置くための場所を作ったりなど、
柔軟に開発できます。
FuelPHPを使って開発する時に最初にやったこと | tech.ewdev.info

論理削除ができる。

通常、モデルはModelクラスを継承して作りますが、
Model_Softを継承してモデルを作り、削除フラグを設定すると論理削除ができます。
リレーションを使った論理削除もできますし、簡単に復元もできます。
論理削除モデル - Orm パッケージ - FuelPHP ドキュメント

一括読み込みと遅延読み込みが選べる

ORMを使ってリレーションのあるレコードを取得するとき、
リレーションの子に当たるレコード情報を一括で取得するか、必要になってから取得するか選ぶことができます。
デフォルトは遅延取得になっています。
はじめに - Relations - Orm Package - FuelPHP ドキュメント

コアの拡張が簡単にできる

fuelPHPではコアを継承して全く同じクラス名でbootstrap.phpに設定を書くと、
あたかも元からそれがcoreのクラスだったかのように動いてくれます。
自分はそれを知らなかったので、herokuで自作coreを使うために、
githubのfuelを自分のリポジトリにfolkしてcomposerに読み込ませるという
アホなことをしました...とほほ。
Core の拡張 - 概要 - FuelPHP ドキュメント

結論

自分が欲しいものは、だいたいある!
リファレンス読もうね。

FuelPHP ドキュメント

herokuにfuelphpを載せる時の注意

herokuにfuelphpを載せるとき、
fuelphpのpackageやcomposerでインストールしたライブラリ郡が邪魔になります。
ではどうするかと。
結論から言うとfuel/packages とfuel/vendorをgitの管理下から削除してください。

git remove --cached

これで大丈夫です。


以下説明。

composerでインストールしたライブラリ郡はgitのsubmodule扱い?になっており、
そのままherokuにpushすると、submoduleの参照先無いけど?っていうエラーが出ます。

remote: No submodule mapping found in .gitmodules for path 'fuel/vendor/composer/installers'

こんなかんじの。


ではどうするか。
これらのファイルはgitの管理下から外してしまって構いません。

herokuにpushした際にcomposerが実行されると、
composer install が行われ、
Core、Package、Vendorは再構成されます。

fuelPHPをnginx+php-fpmで動かすときの設定

apachefuelPHPを動かす時と違って、
nginxをwebサーバーとして使う場合はphp-fpmを利用するのが一般的です。
その際nginxにはmod_rewiteのモジュールが無いので、
自分でパスの書き換えを行わないといけません。

といっても大したことではないです。

nginxの設定ファイルは/etc/nginx/conf.d/以下で基本的な設定はdefault.confに記述します。

<?php
    location / { 
        try_files $uri $uri/ @handler;
    }   
    
    location @handler {
        rewrite ^ /index.php?/$request_uri;
    }

    location ~^/index.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 
        client_max_body_size 100M;
    }

上の2つはパスの書き換えで、下の設定はphp-fpmの設定です。

【Laravel入門】モデルとリレーション

モデルとリレーション

今回言っているモデルというのはlaravelのeloquent機能の話です。
データベースにArticlesというテーブルがあることを仮定します。

準備

tableはmigarateを使って作ります。

<?php

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

class CreateArticleTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('articles', function($table)
        {   
            $table->increments('id');
            $table->string('title', 20);
            $table->text('contents');
            $table->timestamps();
        });
	}

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

}

eloquentモデルの基本操作

次にモデルファイルapp/models/article.phpを作ります。

<?php

class Article extends Eloquent{
	
}

Laravelだと、これだけでもうデータベースへのアクセスができるようになります。
例えば

<?php
$articles = Article::all();

で全件取得ができます。

<?php
$first_article = Article::find(1);

でid=1のarticleが取得できます。

クエリービルダーのメソッドを使ってより詳細な操作ができます。

<?php
$article = Article::where('title', 'article_title')->first();

とすると、titleがarticle_titleであるarticleのうち最初の1件が取得できます。

クエリービルダーのメソッドの詳しい使い方はここに書いてあります。
DB:クエリービルダー

リレーション

articleにはカテゴリーがあって、
一つのカテゴリーに複数のarticleが所属していると考えます。

categoryもmigrateで作ります。

<?php

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

class CreateCategoryTable extends Migration {

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

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

}

次にカテゴリーのモデルを作ります。

<?php

class Category extends Eloquent{
	
}

で、カテゴリーが複数のarticleを持っているので、
カテゴリーから見てarticleはhasManyで、articleから見てカテゴリーはbelongsToです。
それぞれの設定をモデルに書いていきます。

category.php

<?php

class Category extends Eloquent{
	public function article(){
		return $this->hasMany('Article');
	}
}


article.php

<?php

class Article extends Eloquent{
	public function category(){
		return $this->belongsTo('Category');
	}
}

これだけです。

Articleから自分の所属しているカテゴリーを取得したいときは、

<?php
Article::find(1)->category

のようにして取得します。

おわりに

LaravelのEloquentは思った以上に書くことがなくて簡単でした。
あと、リレーションはCakeとかFuelだと専用の設定みたいなのに書きますが、
Laravelだと普通にメソッドとして書くのがちょっと新鮮でした。
こっちのほうが使いやすい気がします。

Apache Solrをデータインポートハンドラでmysqlと同期する

はじめに

solrのインストールについては以前書きました。
Apache Solrのインストール - 人生の恥は書き捨て

今回は、mysqlのデータをデータインポートハンドラを使ってsolrに取り込みます。

環境

solr 4.9.0
mysql 5.6.19

Apache Solrをデータインポートハンドラでmysqlと同期

ライブラリのインストール

solrはjavaで動いているので、
mysqlサーバーにアクセスするためにmysql-connector-javaを入れます。

cd /usr/local/src
wget http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.31.tar.gz/from/http://cdn.mysql.com/
tar -zxvf index.html
mv mysql-connector-java-5.1.31-bin.jar /usr/local/solr/example/lib/mysql-connector-java-5.1.31-bin.jar

パスを通します。

vim /usr/local/solr/example/solr/collection1/conf/solrconfig.xml

<lib dir="../../../contrib/extraction/lib" regex=".*\.jar" />
<lib dir="../../../dist/" regex="solr-cell-\d.*\.jar" />

<lib dir="../../../contrib/clustering/lib/" regex=".*\.jar" />
<lib dir="../../../dist/" regex="solr-clustering-\d.*\.jar" />

<lib dir="../../../contrib/langid/lib/" regex=".*\.jar" />
<lib dir="../../../dist/" regex="solr-langid-\d.*\.jar" />

<lib dir="../../../contrib/velocity/lib" regex=".*\.jar" />
<lib dir="../../../dist/" regex="solr-velocity-\d.*\.jar" />
<!-- Additional Setting -->
<lib dir="../../lib/" regex="mysql-connector-java-\d.*\.jar" />
<lib dir="../../../dist/" regex="solr-dataimporthandler-\d.*\.jar" />
<lib dir="../../../contrib/dataimporthandler/lib/" regex=".*\.jar" />

80行目くらいにライブラリの読み込みの記述があるので、
そこにAdditional Setting以下の3行を足します。

これでmysqlと連携できます。

データベースの準備


データベースを用意します。

mysql> DESC member;
+-------------+------------------+------+-----+-------------------+-----------------------------+
| Field       | Type             | Null | Key | Default           | Extra                       |
+-------------+------------------+------+-----+-------------------+-----------------------------+
| id          | int(11) unsigned | NO   | PRI | NULL              | auto_increment              |
| name        | varchar(32)      | YES  |     | NULL              |                             |
| updated     | timestamp        | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| catchPhrase | text             | YES  |     | NULL              |                             |
+-------------+------------------+------+-----+-------------------+-----------------------------+


中にダミーのデータを入れておきます。

mysql> select * from member;
+----+----------------+---------------------+--------------------------------------------+
| id | name           | updated             | catchPhrase                                |
+----+----------------+---------------------+--------------------------------------------+
|  1 | Honoka Kosaka  | 2014-08-07 13:40:51 | ファイトだよ!                             |
|  2 | Umi Sonoda     | 2014-08-07 13:41:26 | ラブアローシュート!                       |
|  3 | Kotori Minami  | 2014-08-07 13:43:11 | はいちゅんちゅん(・8・)                 |
|  4 | Hanayo Koizumi | 2014-08-07 13:41:41 | だれかたすけてー!                         |
|  5 | Rin Hoshizora  | 2014-08-07 13:43:28 | にゃんにゃんにゃ〜ん                       |
|  6 | Maki Nishikino | 2014-08-07 13:41:58 | まきちゃんかわいいかきくけこ               |
+----+----------------+---------------------+--------------------------------------------+

データインポートの設定

データインポートの設定ファイルを宣言

vim /usr/local/solr/example/solr/collection1/conf/solrconfig.xml
ファイル末尾のの前に

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
  <lst name="defaults">
    <str name="config">data-config.xml</str>
  </lst>
</requestHandler>

を追加します。
ここで、data-config.xmlにデータインポートの設定を書くよ、と宣言しています。

schema.xml

solrにどのようにデータを保存してインデックスをつけていくかを設定します。
デフォルトで設定がたくさん書いてあるので、それを使ってもいいです。
自分はこのようにしました。

<?xml version="1.0" encoding="UTF-8" ?>
<schema name="lovelive" version="1.4">
<field name="_version_" type="long" indexed="true" stored="true"/>
<types>
  <fieldType name="long" class="solr.LongField" omitNorms="true" />
  <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true" />
  <fieldType name="timestamp" class="solr.TrieDateField" />
  <fieldType name="text_cjk" class="solr.TextField">
    <analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer" />
  </fieldType>
  <fieldType name="text_ja" class="solr.TextField">
    <analyzer> 
      <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/>
      <filter class="solr.JapaneseBaseFormFilterFactory"/>
      <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" enablePositionIncrements="true"/>
      <filter class="solr.CJKWidthFilterFactory"/>
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" enablePositionIncrements="true" />
      <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>  
  </fieldType>
</types>
 
<fields>
  <field name="id" type="long" indexed="true" stored="true" required="true" />
  <field name="name" type="text_ja"  indexed="true" stored="true" />
  <field name="catchPhrase" type="text_ja" indexed="true" stored="true" />
</fields>
 
<uniqueKey>id</uniqueKey>
<defaultSearchField>name</defaultSearchField>
<solrQueryParser defaultOperator="AND" />

</schema>

...ではデータの型を定義しています。
...ではフィールドを定義しています。
フィールドにはtypesで定義した型を設定します。
フィールドには更に、indexed、stored、などの属性が設定できます。
indexed: trueにすると、インデックス、ソートなどができる。
stored: 値をsolrに保存する。
などなど詳しくはここに英語ですが載っています。
SchemaXml - Solr Wiki
日本語の形態素解析はkuromojiを使用しています。

data-config.xml

データをインポートするときのmysqlの設定を書いていきます。

<dataConfig>
  <dataSource name="ratings" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/lovelive" user="ユーザー名" password="パスワード" />
   
  <document>
    <entity name="member"
      query="select * from members"
      deltaQuery="select id from members where created >= '${dataimporter.last_index_time}'"
      deltaImportQuery="select * from members where id=${dataimporter.delta.id}">
      <field column="id" name="id" />
      <field column="name" name="name" />
      <field column="catchPhrase" name="catchPhrase" />
    </entity>
  </document>     
</dataConfig>

で接続するDBの設定をします。
でどのようにデータを取得するか設定します。
queryではfull-importする際のsqlを設定しています。
dataQueryとdataImportQueryではdelta-importする際のsqlを設定しています。

あとはsolrのメニューからcoreを選んで
dataImportをExcuteすればmysqlのデータをsolrにインポートできます。

【Laravel入門】データベース接続とマイグレーション

マイグレーションとは

マイグレーション(migration)はデータベース構成のバージョン管理ツールのようなものです。
マイグレーションを使うことで、

といったことができます。

今回はユーザーテーブルを作ってみることにします。

database接続の設定

app/config/database.phpに接続の設定が書いてあります。
connectionsの部分のdatabase名やuser名、passwordなどを適切に設定してください。

マイグレーションファイルの作成

artisanコマンドが使える場所で、

php artisan migrate:make CreateUserTable

を実行します。CreateUserTableの部分はお好きな様に。
すると2014_08_03_132022_CreateUserTable.phpというような、
作成日時+指定した名前.phpのファイルが
app/database/migration/以下に作成されます。

このファイルにデータベースの設定内容を書いていきます。

ファイルを開くと

<?php                                                                                                                                                                                

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

class CreateUserTable extends Migration {

    /** 
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {   
        // 
    }   

    /** 
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {   
        //
    }   

}

こんな感じになっていると思います。
upの部分には新しく追加したい操作を、
downのところにはrollbackする時の操作を書いていきます。

テーブル作成の操作を書くとこんな感じ。

<?php                                                                                                                                                                                

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

class CreateUserTable extends Migration {

    /** 
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {   
        Schema::create('users', function($table)
        {   
            $table->increments('id');
            $table->string('username', 20);
            $table->string('password', 64);
            $table->timestamps();
        }); 
    }   

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

}

upにはusersテーブルを作るように、downにはテーブルを消すように書いてあります。

書き終わったら。migrationを実行します。

php artisan migrate

実行するか確認されるのでEnterしましょう。
するとデータベース内にusersテーブルが作成されます。

mysql> desc users;
+------------+------------------+------+-----+---------------------+----------------+
| Field      | Type             | Null | Key | Default             | Extra          |
+------------+------------------+------+-----+---------------------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| username   | varchar(20)      | NO   |     | NULL                |                |
| password   | varchar(64)      | NO   |     | NULL                |                |
| created_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| updated_at | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
+------------+------------------+------+-----+---------------------+----------------+

ここで、認証用のremember_tokenカラムを追加したいと思います。

tableの作成と同様に、

php artisan migrate:make ModifyUserTable

として、設定追加用のファイルを作ります。

<?php

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

class ModifyUserTable extends Migration {

    /** 
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {   
        Schema::table('users', function($table)
        {   
            $table->string('remember_token', 100);
        }); 
    }   

    /** 
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {   
        Schema::table('users', function($table)                                                                                                                                      
        {   
            $table->dropColumn('remember_token');
        }); 

    }   

}

そして

php artisan migrate

すると

mysql> desc users;
+----------------+------------------+------+-----+---------------------+----------------+
| Field          | Type             | Null | Key | Default             | Extra          |
+----------------+------------------+------+-----+---------------------+----------------+
| id             | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
| username       | varchar(20)      | NO   |     | NULL                |                |
| password       | varchar(64)      | NO   |     | NULL                |                |
| created_at     | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| updated_at     | timestamp        | NO   |     | 0000-00-00 00:00:00 |                |
| remember_token | varchar(100)     | NO   |     | NULL                |                |
+----------------+------------------+------+-----+---------------------+----------------+
6 rows in set (0.00 sec)

という風にカラムが追加できます。

更に、

php artisan migrate:rollback

とするとremember_tokenカラムを追加する前に戻すことができます。

rollbackする際に class not foundのエラーが出ることがあります(僕は出ました)。
この際は composer dump-autoload として、ファイルを読み込み直すと正しく実行できます。

Laravel + Twitter-l4でTwitter連携

はじめに

LaravelでTwitterApiを利用してみようと思いまして、
Laravel便利だし、きっとLaravel用のTwitterApiのライブラリもあるだろ、
と思って探したらTwitter-l4というのがあったので使ってみました。
https://github.com/thujohn/twitter-l4

Laravelの導入は以下を参考にどうぞ
【Laravel入門】 インストールから起動まで - 人生の恥は書き捨て

導入

インストール

composerを利用して、ライブラリをインストールします。
composer.jsonのrequierに "thujohn/twitter": "dev-master" を追加します。

	"require": {
		"laravel/framework": "4.2.*",
		"thujohn/twitter": "dev-master"
	},

そしてコンソールでcomposer update します。
自分の場合はcomposer.pharをcomposerにリネームしてグローバルに置いているので,
composer update ですが、そうじゃない人は php composer.phar update ですかね。

あとかなり時間かかる(5分以上)ことがあって、
しかもその間コンソールに何も出ないので、おかしいかな?と疑いますが、
正常に動いていると思うので、気長に待ちましょう。

逆にエラーのときはすぐにエラーが表示されます。


次にLaravelからライブラリを読み込ませます。

app/config/app.phpを編集します。
providers の部分に 'Thujohn\Twitter\TwitterServiceProvider', を追加します。

'providers' => array(
		'Illuminate\Foundation\Providers\ArtisanServiceProvider',
		'Illuminate\Auth\AuthServiceProvider',
		.
                .
                <中略>
                .
                .
		'Illuminate\Workbench\WorkbenchServiceProvider',
		'Thujohn\Twitter\TwitterServiceProvider',
	),

aliasesの部分に 'Twitter' => 'Thujohn\Twitter\TwitterFacade', を追加します。

	'aliases' => array(

		'App'               => 'Illuminate\Support\Facades\App',
		'Artisan'           => 'Illuminate\Support\Facades\Artisan',
		.
                .
                <中略>
                .
                .
		'View'              => 'Illuminate\Support\Facades\View',
		'Twitter'	    => 'Thujohn\Twitter\TwitterFacade',
	),

設定

Laravel内に自分のTwitter アプリケーションの設定を書きます。
Twitterアプリはhttps://apps.twitter.com/で作ります。
作ったアプリのkeyなどをLaravelに設定します。

まず、設定ファイルの作成です。
コンソールで php artisan config:publish thujohn/twitter を実行します。
すると app/config/packages/thujohn以下に設定ファイルができます。

app/config/thujohn/twitter/config.php

<?php

// You can find the keys here : https://dev.twitter.com/

return array(
	'API_URL'             => 'api.twitter.com',
	'API_VERSION'         => '1.1',
	'USE_SSL'             => true,

	'CONSUMER_KEY'        => '',
	'CONSUMER_SECRET'     => '',
	'ACCESS_TOKEN'        => '',
	'ACCESS_TOKEN_SECRET' => '',
);

ここに作ったアプリの情報をそれぞれ入力してください。

これで導入は終わりです。

使ってみる

では設定が終わったので早速使ってみましょう。

app/routes.phpに書いていきます。

最近のツイートの取得

<?php
Route::get('/', function()
{
    return Twitter::getUserTimeline(array('screen_name' => 'アカウントのスクリーンネーム', 'count' => 20, 'format' => 'json'));
});

これでスクリーンネームを指定したアカウントの最新ツイート20件が取得できます。

ちなみに、formatはobject、json、arrayの3種類から選択出来て、defaultだとobjectになっています。

メンションの取得

<?php
Route::get('/', function()
{
    return Twitter::getMentionsTimeline(array('count' => 20, 'format' => 'json'));
});

自分でリクエストを書く

twitter-l4にはまだあまりメソッドが用意されてない?ようなのですが、
queryメソッドを使って自分でリクエスト内容を指定すれば、しっかりTwitterApiを利用できます。

ツイートの検索

<?php
Route::get('/', function(){
	return $array = Twitter::query('search/tweets', 'GET', array('q' => '検索ワード', 'format' => 'array'));
});

これで指定したワードを含むツイートが取得できます。