Google Cloud VisionでOCRする

CLOUD VISION API

PHP用のライブラリを使ってみる

Cloud Vision API Client Libraries
(APIリファレンス)Google\Cloud\Vision\VisionClient

$ composer require google/cloud
$ gcloud auth application-default login
$ vim cv.php
$ php cv.php

サンプルコード

cv.php

<?php
require __DIR__ . '/vendor/autoload.php';
use Google\Cloud\Vision\VisionClient;

$projectId = 'hoge-151111';
$vision = new VisionClient([
    'projectId' => $projectId
]);

$file = __DIR__ . '/res/ocr_test.png';
$img_res = fopen($file, 'r');
$image = $vision->image($img_res, ['text']);
$result = $vision->annotate($image);

foreach((array) $result->text() as $txt){
    print($txt->description().PHP_EOL);
}

gcloudの設定がうまくいかなかった(プロジェクト指定ができなかった)ので、サービスアカウントを作成して、ダウンロードしたjsonファイルを下記のように設定したらできた。

$vision = new VisionClient([
    'projectId' => $projectId,
    'keyFilePath' => __DIR__.DS.'hoge.json'
]);

windowsのphpでcURLが使えないエラー

windows10でPAY.JPのPHP SDK使ってみたら、下記のエラーが出ました。curlのSSL証明書の設定みたいなやつでした。

Fatal error: Uncaught exception ‘Payjp\Error\ApiConnection’ with message ‘ in D:\xampp\htdocs\payjp\vendor\payjp\payjp-php\lib\HttpClient\CurlClient.php on line 123
( ! ) Payjp\Error\ApiConnection: Unexpected error communicating with Payjp. If this problem persists, let us know at support@pay.jp. (Network error [errno 77]: error setting certificate verify locations: CAfile: D:\xampp\htdocs\payjp\vendor\payjp\payjp-php\lib\HttpClient/../../data/ca-certificates.crt CApath: none) in D:\xampp\htdocs\payjp\vendor\payjp\payjp-php\lib\HttpClient\CurlClient.php on line 123

ローカルテストの場合は、gitとかに入ってるやつを使えるようです。
参考:Windows版PHPのcurlの証明書

設定してapache再起動したら出来ました。

購買型クラウドファンディング構築システム『Fundee Scratch』をつくりました

Fundee Scratchとは?

購買型のクラウドファンディングサイトを簡単に作成できるパッケージシステムです。https://fundee.inでASPとして運営していた実績を基に作成したので、安定しています。特に良質なプロジェクトを集められる方でしたら、手数料をご自分で決定できるパッケージはいいと好評いただいています。PHPで作成されており、フレームワークはcakePHPを使っています。ソースコードの難読化などはもちろんしていませんので、自由にカスタマイズしていただけます。

紹介サイト・デモサイト

紹介サイトは下記になります。
http://crowdfunding-system.biz/

デモサイトは下記です。
http://demo.crowdfunding-system.biz/

決済について

  • 決済は、購買型のAll or Nothing型のみ現在は対応しております。
  • クレジットカードのvisa、masterに対応しています。

決済代行会社について

決済はGMOペイメントを利用しておりますが、これには理由があります。

他の非常に安い決済サービスだと、仮売上(オーソリ)に十分対応していない。

  • webpayは、仮売上機能は、最大45日までです。つまり、プロジェクトの募集が最大45日までしかできません。
  • SPIKEは、月10万まで無料ということで面白いのですが、仮売上機能がそもそもありません。

プロジェクトの規模が制限される

  • paypalは、そもそもクラウドファンディングは慎重姿勢ということで、1件のプロジェクト当たり最大30万までと言われました。つまり大規模なプロジェクトは一切公開できません。

以上のように、それぞれ安くてよさそうな決済サービスもクラウドファンディングの利用には適していませんでした。一方GMOペイメントは、仮売上機能が最大90日(だったはず)まで使えるので、長期間のプロジェクトも公開可能ですし、大規模プロジェクトも問題なく公開可能です。デメリットは、初期費用は月額費用が上記のような決済代行会社と比べると必要になる点です。

もし、初期・月額を抑えたいということでしたら、さすがに、1プロジェクト30万までだと小さすぎるケースが多いと思うので、webpayの45日以内を前提に運営するといいかもしれないと思いました。決済手数料は、3.25%とかで、GMOペイメントと同じかちょっと安いくらいで、初期・月額無料ですので。webpayバージョンもつくってみようかなと思ってます。

金額

金額は、40万円ですが、今だけ半額提供中です。20万円(税別)です。

cakephp2 – database.phpの情報でmysqlのpdoで接続する

cakephp2で、database.phpの情報でmysqlのpdoで接続する方法。

下記で、database.phpで設定している内容を取得できる。

include_once APP.'Config'.DS.'database.php';
if(class_exists('DATABASE_CONFIG'))
{
    $this->db_config = new DATABASE_CONFIG();
}

下記で、dbに接続できる。

private function connect_db()
{
    try
    {
        $this->pdo = new PDO('mysql:host='.$this->db_config->default['host'].';dbname='.
                             $this->db_config->default['database'].';charset=utf8',
                             $this->db_config->default['login'], $this->db_config->default['password']);
    }
    catch(PDOException $e)
    {
        exit('データベース接続失敗。'.$e->getMessage());
    }
}

下記で、データベースに存在するテーブルをすべて削除できる。

private function drop_tables()
{
    try
    {
        $stmt = $this->pdo->query('SHOW TABLES');
        $tables = $stmt->fetchAll();
        foreach($tables as $tbl)
        {
            $this->pdo->exec('drop table if exists '.$tbl[0]);
        }
    }
    catch(PDOException $e)
    {
        exit('テーブル削除失敗。'.$e->getMessage());
    }
}

下記で、データベースにsqlをインポートできる。

private function import_sql()
{
    $sql = $this->get_sql();
    try
    {
        return $this->pdo->exec($sql);
    }
    catch(PDOException $e)
    {
        exit('sqlインポート失敗。'.$e->getMessage());
    }
}
private function get_sql()
{
    try
    {
        return file_get_contents($this->sql_path);
    }
    catch(Exception $e)
    {
        exit('sqlファイル取得失敗。'.$e->getMessage());
    }
}

Xampp windows10 PHP sendmail 設定 (gmailを使う)

php.iniの設定と、sendmail.iniの設定をして、stunnelをインストールして、stunnel.confを設定する。

php.iniの設定

php.iniの場所は、xampp/php/php.ini
[mail function]という箇所を下記のようにする。

[mail function]
SMTP=localhost
smtp_port=465
sendmail_path = "D:\xampp\sendmail\sendmail.exe -t"
mail.add_x_header=On

sendamil.iniの設定

sendmail.iniの場所は、xampp/sendmail/sendmaill.ini

[sendmail]
smtp_server=localhost
smtp_port=25
smtp_ssl=none
auth_username=hogehoge@gmail.com
auth_password=hoge

stunnelのインストール

stunnel: Downloads

stunnel.confの設定

stunnel.confのバックアップをとっておき、中身を下記のようにする。

[gmail-smtp]
client = yes
accept = 127.0.0.1:25
connect = smtp.gmail.com:465
verify = 2
CAfile = ca-certs.pem
checkHost = smtp.gmail.com
OCSPaia = yes

[ssmtp]
accept  = 465
connect = 25
cert = stunnel.pem

最後にXamppコントローラでApacheを再起動する。

vimでphpを使う

参考:VimでのPHP開発環境

PHP開発に便利なプラグイン

NeoBundleでプラグインを入れられるようになったので、上記を参考に、PHP開発に有用そうなプラグインを入れてみます。

Shougo/neocomplete.vim 関数名とか補完してくれるやつ。
Shougo/neosnippet.vim スニペット登録できるやつ。Shougo/neosnippet-snippetsも入れる必要があるらしい。
thinca/vim-ref PHPのマニュアルをvimで見られるやつ。テキストブラウザが必要らしい。
tomtom/tcomment_vim ファイルの種類に合わせて、適切なコメントアウトをしてくれるやつ。
AndrewRadev/splitjoin.vim 展開したり、1行にまとめたりできるやつ。
osyo-manga/vim-watchdogs 構文チェックしてくれるやつ。

NeoBundleでインストール

NeoBundleFetch 'Shougo/neobundle.vim'
NeoBundle 'Shougo/neocomplete.vim'
NeoBundle 'nanotech/jellybeans.vim'
NeoBundle 'Shougo/neosnippet.vim'
NeoBundle 'Shougo/neosnippet-snippets'
NeoBundle 'thinca/vim-ref'
NeoBundle 'tomtom/tcomment_vim'
NeoBundle 'AndrewRadev/splitjoin.vim'
NeoBundle 'osyo-manga/vim-watchdogs'

エラーなくインストールできた。
まだ他のサイトにも色々情報があり、全部調べて設定するのは結構大変そう。また今度やる。

参考:
VimでPHP開発環境を作成
vimのPHP開発環境

mac – phpを7.0にバージョンアップ

macは、10.11.5、El Capitanです。

現在のバージョンを確認

$ php -v
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20121212/php_mysql.dll' - dlopen(/usr/lib/php/extensions/no-debug-non-zts-20121212/php_mysql.dll, 9): image not found in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20121212/php_mysqli.dll' - dlopen(/usr/lib/php/extensions/no-debug-non-zts-20121212/php_mysqli.dll, 9): image not found in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20121212/php_pdo_mysql.dll' - dlopen(/usr/lib/php/extensions/no-debug-non-zts-20121212/php_pdo_mysql.dll, 9): image not found in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20121212/php_pdo_sqlite.dll' - dlopen(/usr/lib/php/extensions/no-debug-non-zts-20121212/php_pdo_sqlite.dll, 9): image not found in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/extensions/no-debug-non-zts-20121212/php_sqlite3.dll' - dlopen(/usr/lib/php/extensions/no-debug-non-zts-20121212/php_sqlite3.dll, 9): image not found in Unknown on line 0
PHP 5.5.34 (cli) (built: Apr 22 2016 19:16:58) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2015 Zend Technologies

なんかWarningがたくさん出ている。

PHP7.0をインストール

$ curl -s http://php-osx.liip.ch/install.sh | bash -s 7.0

インストール成功した。

PATH設定

$ export PATH=/usr/local/php5/bin:$PATH

PHPバージョン確認

$ php -v
PHP 7.0.6 (cli) (built: May 24 2016 23:07:51) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies
    with Xdebug v2.4.0RC3, Copyright (c) 2002-2015, by Derick Rethans

PHP 7.0.6になった。

————————
追記:

brewでphp7をインストールする

上記のPHPを削除する

$ sudo rm -rf /usr/local/bin/php5
$ sudo rm -rf /usr/local/bin/php5-7.0.6-20160524-231014/

.bash_profileを修正する

$ vim ~/.bash_profile

下記を削除する

#php7
PATH=/usr/local/php5/bin:$PATH
PATH=/usr/local/php5/sbin:$PATH

修正を反映する

$ source ~/.bash_profile

brewのリポジトリを追加する

$ brew tap homebrew/dupes
$ brew tap homebrew/php

php7をインストールする

$ brew install php70

〜
中略
〜

The php.ini file can be found in:
    /usr/local/etc/php/7.0/php.ini

✩✩✩✩ Extensions ✩✩✩✩

If you are having issues with custom extension compiling, ensure that
you are using the brew version, by placing /usr/local/bin before /usr/sbin in your PATH:

      PATH="/usr/local/bin:$PATH"

PHP70 Extensions will always be compiled against this PHP. Please install them
using --without-homebrew-php to enable compiling against system PHP.

✩✩✩✩ PHP CLI ✩✩✩✩

If you wish to swap the PHP you use on the command line, you should add the following to ~/.bashrc,
~/.zshrc, ~/.profile or your shell's equivalent configuration file:

      export PATH="$(brew --prefix homebrew/php/php70)/bin:$PATH"

✩✩✩✩ FPM ✩✩✩✩

To launch php-fpm on startup:
    mkdir -p ~/Library/LaunchAgents
    cp /usr/local/opt/php70/homebrew.mxcl.php70.plist ~/Library/LaunchAgents/
    launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.php70.plist

The control script is located at /usr/local/opt/php70/sbin/php70-fpm

OS X 10.8 and newer come with php-fpm pre-installed, to ensure you are using the brew version you need to make sure /usr/local/sbin is before /usr/sbin in your PATH:

  PATH="/usr/local/sbin:$PATH"

You may also need to edit the plist to use the correct "UserName".

Please note that the plist was called 'homebrew-php.josegonzalez.php70.plist' in old versions
of this formula.

To have launchd start homebrew/php/php70 now and restart at login:
  brew services start homebrew/php/php70
==> Summary
🍺  /usr/local/Cellar/php70/7.0.6: 332 files, 49.2M

.bash_profileを修正する

下記を最後に追加する。

PATH="/usr/local/sbin:$PATH"

バージョン確認

$ php -v
PHP 7.0.6 (cli) (built: Apr 29 2016 04:21:39) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

$ php-fpm -v
PHP 7.0.6 (fpm-fcgi) (built: Apr 29 2016 04:21:42)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

Zend_Pdfで作成されるPDFのファイルサイズを小さくする

Zend_Pdfで日本語が含まれるPDFを作成するとファイルサイズが5MB近くになる場合があります。日本語のフォントが全部PDFに含まれるからだそうです。

参考:http://framework.zend.com/manual/1.12/ja/zend.pdf.drawing.html

日本語フォントをセットする際に、Zend_Pdf_Font::EMBED_DONT_EMBEDというパラメタを設定することで、PDFにフォントが埋め込まれなくなります。

$font = Zend_Pdf_Font::fontWithPath(APPLICATION_PATH . '/../vendor/font/ipaexg.ttf', Zend_Pdf_Font::EMBED_DONT_EMBED);

4.4MBだったものが、Zend_Pdf_Font::EMBED_DONT_EMBEDを設定することで、300KB程度になりました。

追記:2016/5/15
これだと、フォントが端末にないと表示されませんでした。代替フォントを使ってくれるのかと思いましたが、自分の環境だとうまく表示されませんでした。なので、結局mbfpdfを使いました。

Nginx・PHP – アップロードファイルの最大サイズの変更

参考:nginxで”client intended to send too large body”が発生した時の対策方法
参考:最大アップロード容量を変更する

Nginxは、/etc/nginx/nginx.confの、下記を変更する。なかったら、http, server, locationコンテキスト内に追加する。

client_max_body_size 4m;

あとは、php.iniの値も確認・修正する。
下記のようになってたので、それぞれ必要な容量に修正する。

memory_limit 128M
post_max_size 8M
upload_max_filesize 2M

FFmpeg – mac・PHPで使う

macのバージョンは、10.11.4。El Capitan。

参考: Mac OS XでFFmpegのインストールとWebM動画の作り方

brewでインストールした。ここまで長いオプションは必要ないのかもしれない。

brew install automake celt faac fdk-aac git lame \
libass libtool libvorbis libvpx \
libvo-aacenc opencore-amr openjpeg \
opus sdl schroedinger shtool speex texi2html \
theora wget x264 xvid yasm

FFmpegとは?

FFmpeg(エフエフエムペグ)は動画と音声を変換することのできるUNIX系OS生まれのフリーソフトウェアであり、libavcodec(動画/音声のコーデックライブラリ)、libavformat(動画/音声のコンテナライブラリ)、libswscale(色空間・サイズ変換ライブラリ)、libavfilter(動画のフィルタリングライブラリ)などを含む。ライセンスはコンパイル時のオプションによりLGPLかGPLに決定される。コマンドラインから使用することができる。対応コーデックが多く、多彩なオプションを使用可能なため、幅広く利用されている。

引用:FFmpeg

PHP-FFMpegを使う

最初ffmpeg-phpを使おうとしたけど、エラーでインストールできなかった。2012年で開発終了しているらしい。

PHPでffmpegを利用するには、PHP-FFMpegがおすすめです。Composer対応のモダンなPHP実装なので、PHP5.3以上のコードベースではとても扱いやすいです。

ffmpeg-phpというPHP拡張もありますが、2012年で開発が止まっているのであまりおすすめしません。

引用:FFmpegによる動画エンコードの基本

PHP-FFMpegを使ってみる。

PHP-FFMpeg

上記のREADMEに詳しく書いてあった。composerに追加してupdateしたら下記のような感じで使えるようになる。

composer.json

{
    "require": {
        "php-ffmpeg/php-ffmpeg": "~0.5"
    }
}

fuelphpのコントローラ内でテスト。保存されている動画を取得して、jpgのサムネイル画像を作成して保存している。その後、fuelphpのImageクラスでリサイズして保存した。

//ffmpegテスト
$ffmpeg = FFMpeg\FFMpeg::create(array(
	'ffprobe.binaries' => '/usr/local/bin/ffprobe',
	'ffmpeg.binaries' => '/usr/local/bin/ffmpeg'
));
$video = $ffmpeg->open(DOCROOT.'assets/movie/'.$movie['file_name']);
//		$video
//			->filters()
//			->resize(new FFMpeg\Coordinate\Dimension(320, 180))
//			->synchronize();
$frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(15));
$img_path = DOCROOT.'assets/movie/thumb/'.$movie['id'].'.jpg';
$frame->save($img_path);

//画像のリサイズ
//ffmpegのリサイズが効かない為
Image::load($img_path)->resize(320, null, true, true)->save($img_path);

fuelphp1.8 – コアクラスのカスタマイズ

参考:http://fuelphp.jp/docs/1.8/general/extending_core.html#extend_and_replace

type=”file”のformに、「required」というバリデーションルールを適用しても、うまく動作しない。ファイルをアップロードしても、ファイルがアップロードしたことにならない。だから、requiredというルールを適用するのをやめたんだけど、そうすると、form.phpで設定できる、required_markの表示がなくなるので、それも嫌だ。required_markというのは、requiredルールを設定してあるFieldをview等でbuildした時に、勝手に必須項目であることを示す文字列等を表示してくるものであります。なので、Fieldをbuild関数をいじって、required_markを表示する条件を変更してみようと思った。

fuel/core/classes/fieldset/field.phpの591行目に、下記があります。

$required_mark = $this->get_attribute('required', null) ? $form->get_config('required_mark', null) : null;

get_attributeで「required」があれば、form.phpのrequired_markに設定している文字列を返しています。なので、get_attributeに渡すものを「required」だけではなく、array(‘required’, ‘required_upload’)にしてみます。でも、fuel/core内を勝手にいじってはダメなので、下記のようにします。

コアクラスを継承したクラスを作成する

fuel/app/classes/fieldset/field.phpを下記のように作成する。

<?php

/**
 * Fieldset Class
 *
 * カスタムバリデーションルールのrequired_uploadが設定されている場合に、
 * required_markが表示されるようにFieldset_Fieldクラスを修正
 */
class Fieldset_Field extends \Fuel\Core\Fieldset_Field
{

	protected function template($build_field)
	{
		$form = $this->fieldset()->form();

		//下記を修正
		//$required_mark = $this->get_attribute('required', null) ? $form->get_config('required_mark', null) : null;
		$required_mark = $this->get_attribute(array('required', 'required_upload'), null) ? $form->get_config('required_mark', null) : null;
		$label = $this->label ? $form->label($this->label, null, array('id' => 'label_'.$this->name, 'for' => $this->get_attribute('id', null), 'class' => $form->get_config('label_class', null))) : '';
		$error_template = $form->get_config('error_template', '');
		$error_msg = ($form->get_config('inline_errors') && $this->error()) ? str_replace('{error_msg}', $this->error(), $error_template) : '';
		$error_class = $this->error() ? $form->get_config('error_class') : '';

		if (is_array($build_field))
		{
			$label = $this->label ? str_replace('{label}', $this->label, $form->get_config('group_label', '<span>{label}</span>')) : '';
			$template = $this->template ?: $form->get_config('multi_field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{group_label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{fields}\n\t\t\t\t{field} {label}<br />\n{fields}\t\t\t{error_msg}\n\t\t\t</td>\n\t\t</tr>\n");
			if ($template && preg_match('#\{fields\}(.*)\{fields\}#Dus', $template, $match) > 0)
			{
				$build_fields = '';
				foreach ($build_field as $lbl => $bf)
				{
					$bf_temp = str_replace('{label}', $lbl, $match[1]);
					$bf_temp = str_replace('{required}', $required_mark, $bf_temp);
					$bf_temp = str_replace('{field}', $bf, $bf_temp);
					$build_fields .= $bf_temp;
				}

				$template = str_replace($match[0], '{fields}', $template);
				$template = str_replace(array('{group_label}', '{required}', '{fields}', '{error_msg}', '{error_class}', '{description}'), array($label, $required_mark, $build_fields, $error_msg, $error_class, $this->description), $template);

				return $template;
			}

			// still here? wasn't a multi field template available, try the normal one with imploded $build_field
			$build_field = implode(' ', $build_field);
		}

		// determine the field_id, which allows us to identify the field for CSS purposes
		$field_id = 'col_'.$this->name;
		if ($parent = $this->fieldset()->parent())
		{
			$parent->get_tabular_form() and $field_id = $parent->get_tabular_form().'_col_'.$this->basename;
		}

		$template = $this->template ?: $form->get_config('field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{field} {description} {error_msg}</td>\n\t\t</tr>\n");
		$template = str_replace(array('{label}', '{required}', '{field}', '{error_msg}', '{error_class}', '{description}', '{field_id}'),
			array($label, $required_mark, $build_field, $error_msg, $error_class, $this->description, $field_id),
			$template);

		return $template;
	}

}

これを、fuel/app/bootstrap.phpを設定して、読み込めるようにします。

bootstrap.phpの設定

\Autoloader::add_classes(array(
	// Add classes you want to override here
	// Example: 'View' => APPPATH.'classes/view.php',
	'Fieldset_Field' => APPPATH.'classes/fieldset/field.php',
));

fuelphp 1.8 – カスタムバリデーション

どうも、Uploadをすると、Uploadクラスのバリデーションを使うことになりますが、これとfieldsetのValidationクラスとの連動がいまいちうまくできない。Fieldsetはform.phpの、inline_errorsをtrueにすると勝手にエラーを表示してくれるが、このエラーに自動的にuploadのエラーも連動するようにしたい。他にやり方があるかもしれないが、Upload関連のエラーをValidationのカスタマイズルールとして作成することで、実現してみる。

モデルでFieldsetを作成する

fileというフィールドに、uploadというルールを設定している。これはデフォルトではないので、カスタマイズルールになります。引数は、フィールド名。あとでこのuploadというルールを作成します。

public static function fieldset()
{
	//大カテゴリー取得
	$big_categories = Model_Category::get_big_category_list();

	$fset = Fieldset::forge();
	$fset->add('file', '動画ファイル', array('type' => 'file'))->add_rule('upload', 'file');
	$fset->add('title', 'タイトル')->add_rule('required')->add_rule('max_length', 150);
	$fset->add('description', '説明', array('type' => 'textarea'))->add_rule('required')->add_rule('max_length', 500);
	$fset->add('big_category_id', '大カテゴリー', array('type' => 'select', 'options' => $big_categories));
	$fset->add('category_id', '小カテゴリー', array('type' => 'select'))->add_rule('required');
	$fset->add('order_no', '表示順')->add_rule('required')->add_rule('numeric_between', 1, 100);
	return $fset;
}

カスタムバリデーションのクラスファイルを作成する

fuel/app/classes/model/validation/upload.phpというのを作成してみる。

<?php
class Model_Validation_Upload
{
	public static function _validation_upload($field_name)
	{
		if(Upload::is_valid())
		{
			Upload::save();
			return true;
		}
		else
		{
			$error = Upload::get_errors($field_name);
			if(!empty($error['errors'][0]['message']))
			{
				Validation::active()->set_message('upload', $error['errors'][0]['message']);
			}else{
				Validation::active()->set_message('upload', 'ファイルのアップロードでエラーが発生しました');
			}
			return false;
		}
	}
}

コントローラを作成する

フィールドセットを作って、Upload:processを走らせて、フィールドセットからバリデーションを取りだして、バリデーションにuploadルールを読めるように設定する。

public function action_add()
{
	//フィールドセットの取得
	$fset = Model_Movie::fieldset();

	if (Input::post()){
		Upload::process();

		// バリデーションチェック
		$val = $fset->validation();
		$val->add_callable('Model_Validation_Upload');

		if( $val->run() ){
			//動画情報をデータベースに登録
			$file = Upload::get_files( 0 );
			if( Model_Movie::add( Input::post(), $file ) ){
				Session::set_flash( 'msg', '動画を登録しました。' );
				return Response::redirect( 'admin/movies/' );
			}else{
				//保存ファイルの削除
				File::delete( DOCROOT . 'assets/movie/' . $file[ 'saved_as' ] );
			}
		}

		//小カテゴリのoptionsの取得・設定
		$big_id = Input::post( 'big_category_id' );
		$fset   = Model_Category::set_small_cat_options( $big_id, $fset );

		$fset->repopulate();

		Session::set_flash( 'msg', '動画が登録できませんでした。恐れ入りますが、再度お試しください。' );
	}

	//set view
	$this->template->content = View::forge('admin/movies/add');
	$this->template->content->set_safe('fset', $fset);
}

View

uploadは、enctypeの表示が必要。Fieldsetのフィールドをbuildしてるだけ。bootstrapを使っているので、set_attributeでform-controlクラスを設定している。

<?php echo Form::open(array('enctype' => 'multipart/form-data'))?>
<?php echo $fset->field('file')->set_attribute('class', 'form-control')->build()?>
<?php echo $fset->field('title')->set_attribute('class', 'form-control')->build()?>
<?php echo $fset->field('description')->set_attribute('class', 'form-control')->build()?>
<?php echo $fset->field('big_category_id')
	->set_attribute('class', 'form-control')
	->set_attribute('onchange', 'get_small_cat("' . Uri::create('rest/admin/categories/small_cat.json') . '");')
	->build()?>
<?php echo $fset->field('category_id')->set_attribute('class', 'form-control')->build()?>
<?php echo $fset->field('order_no')->set_attribute('class', 'form-control')->build()?>
<button type="submit" class="btn btn-primary">登録</button>
<?php echo Html::anchor('admin/movies/', 'キャンセル', array('class' => 'btn btn-default'))?>
<?php echo Form::close()?>

fuelphp1.8 – Formとバリデーション

fieldsetを使うと、入力した内容を保持できるし、バリデーションチェックも簡単に行える。エラーも自動で表示できる。core/config/form.phpにformを表示する際のHTMLの設定が書いてあるので、これをシンプルにすることで、多様なデザインにもデザイナがviewの設定のみで大体対応できるようになるかなと思った。

form.phpの設定項目

引用:FuelPHPのFieldsetクラスをまとめてみた。1

項目名 説明
form_method formのmethod属性の値
form_template formを作る際のテンプレートです。{open}がformの開始、{fields}がフィールド部分、{close}がformの終了です。こちらはFormクラスで使用されます。
fieldset_template ↑のfieldset版。
field_template fieldのテンプレート。そのままですね。
multi_field_template fieldが複数だった時のテンプレート。{fields}から{fields}までが項目のループ部分。セレクトボックスはどうやってるのかは不明
error_template エラーのテンプレート。$user_form->validation()->error()メソッドで使われている模様。
required_mark {required}に対応する値を指定。
inline_errors エラーを表示するかどうか。
error_class エラー表示に使うクラス名。デフォルトではtdに指定しているのでこのクラスに対してエラーだった時のbackground-color等を指定することになる。

バリデーションエラーメッセージの日本語化

・core/lang/en/validation.phpを、app/lang/ja/にコピーして編集する。
・app/config/config.phpの「language」を「ja」に設定する。

モデルの例

/**
 * フィールドセット
 */
public static function fieldset()
{
	$fset = Fieldset::forge();
	$fset->add('name', '名前')
		->add_rule('required')
		->add_rule('max_length', 30);
	$fset->add('order_no', '並び順')
		->add_rule('required')
		->add_rule('numeric_between', 1,100);
	return $fset;
}

app/config/form.phpのfield_templateの例

'field_template' => "<div class='form-group'>{label}{required}{field}<p class='err_msg'>{error_msg}</p></div>",

コントローラの例

/**
 * 管理 - 大カテゴリー登録
 */
public function action_add_big()
{
	//フィールドセットの取得
	$fset = Model_Category::fieldset();

	if (Input::post())
	{
		// バリデーションチェック
		$val = $fset->validation();
		if ($val->run())
		{
			//カテゴリ登録
			if(Model_Category::add_big(Input::post('name'), Input::post('order_no')))
			{
				Session::set_flash('msg', '大カテゴリーを登録しました。');
				return Response::redirect('admin/categories/');
			}
		}
		$fset->repopulate();
		Session::set_flash('msg', '大カテゴリーが登録できませんでした。恐れ入りますが、再度お試しください。');
	}

	//set view
	$data['menu_tab'] = View::forge('elements/admin/categories/menu_tab', array('active' => 'big'));
	$this->template->content = View::forge('admin/categories/add_big', $data);
	$this->template->content->set_safe('fset', $fset);
}

cakephp2 – csvダウンロード

CsvHelperがあります。
https://github.com/dai199/cakephp-csv-helper

書き方は、下記のような感じになります。(上記GitHubの使い方の説明を引用)

<?php
$this->Csv->addRow($th);
foreach($td as $t) {
    $this->Csv->addField($t['User']);
    // .... 行を追加していく
    $this->Csv->endRow();
}
$this->Csv->setFilename($filename);
// 文字化けの場合は echo $this->Csv->render(true, 'sjis', 'utf-8');
echo $this->Csv->render();

これは、ここに記載している内容と基本同じようです。下記記載のリンクからヘルパーのファイルにたどり着かなかったので、再度投稿します。
http://endoyuta.com/2014/04/28/cakephp-csv%E3%83%98%E3%83%AB%E3%83%91%E3%83%BC/

あと、このプラグインのCsvHelper.phpの28行目が、下記のようになっていて、$rowがありませんエラーが出ていたので、$rowを追加する必要があると思った。

function addRow() {
    fputcsv($this->buffer, $row, $this->delimiter, $this->enclosure);
}

あと、下記コードを書いたら文字化けした。
echo $this->Csv->render(true, ‘sjis’, ‘utf-8’);