<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GANCHIKU.com</title>
	<atom:link href="http://ganchiku.com/feed" rel="self" type="application/rss+xml" />
	<link>http://ganchiku.com</link>
	<description>renewal, baby!</description>
	<lastBuildDate>Wed, 16 May 2012 13:38:54 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Entity の カラム定義に unsigned int の指定</title>
		<link>http://ganchiku.com/2012/05/entity-%e3%81%ae-%e3%82%ab%e3%83%a9%e3%83%a0%e5%ae%9a%e7%be%a9%e3%81%ab-unsigned-int-%e3%81%ae%e6%8c%87%e5%ae%9a.html</link>
		<comments>http://ganchiku.com/2012/05/entity-%e3%81%ae-%e3%82%ab%e3%83%a9%e3%83%a0%e5%ae%9a%e7%be%a9%e3%81%ab-unsigned-int-%e3%81%ae%e6%8c%87%e5%ae%9a.html#comments</comments>
		<pubDate>Wed, 16 May 2012 13:38:54 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[Symfony2]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=1171</guid>
		<description><![CDATA[Symfony2 で Entity のカラムの定義を unsigned int にするときにちょっと迷ったのでメモ。 type ではなく、 columnDefinition を使う。 /** * @var integer &#8230; <a href="http://ganchiku.com/2012/05/entity-%e3%81%ae-%e3%82%ab%e3%83%a9%e3%83%a0%e5%ae%9a%e7%be%a9%e3%81%ab-unsigned-int-%e3%81%ae%e6%8c%87%e5%ae%9a.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Symfony2 で Entity のカラムの定義を unsigned int にするときにちょっと迷ったのでメモ。<br />
type ではなく、 columnDefinition を使う。</p>
<pre class="brush:php">
    /**
     * @var integer $point
     *
     * @ORM\Column(name="point", columnDefinition="integer unsigned")
     */
    private $point;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2012/05/entity-%e3%81%ae-%e3%82%ab%e3%83%a9%e3%83%a0%e5%ae%9a%e7%be%a9%e3%81%ab-unsigned-int-%e3%81%ae%e6%8c%87%e5%ae%9a.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>小菅村でオータサンとハワイ大学ヒロ校の本田先生のコンサートがあります</title>
		<link>http://ganchiku.com/2012/05/%e5%b0%8f%e8%8f%85%e6%9d%91%e3%81%a7%e3%82%aa%e3%83%bc%e3%82%bf%e3%82%b5%e3%83%b3%e3%81%a8%e3%83%8f%e3%83%af%e3%82%a4%e5%a4%a7%e5%ad%a6%e3%83%92%e3%83%ad%e6%a0%a1%e3%81%ae%e6%9c%ac%e7%94%b0%e5%85%88.html</link>
		<comments>http://ganchiku.com/2012/05/%e5%b0%8f%e8%8f%85%e6%9d%91%e3%81%a7%e3%82%aa%e3%83%bc%e3%82%bf%e3%82%b5%e3%83%b3%e3%81%a8%e3%83%8f%e3%83%af%e3%82%a4%e5%a4%a7%e5%ad%a6%e3%83%92%e3%83%ad%e6%a0%a1%e3%81%ae%e6%9c%ac%e7%94%b0%e5%85%88.html#comments</comments>
		<pubDate>Fri, 11 May 2012 09:28:57 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[Travel]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=1165</guid>
		<description><![CDATA[奥多摩なんですね。行くのがすごく大変そうですが、自然が好きな人はぜひ！おそらくここ以外にも広島やいろんな所を周ると伺っています。 この本田先生には、５年ほど前にハワイ島でお世話になってから、関係が続いています。日本に帰っ &#8230; <a href="http://ganchiku.com/2012/05/%e5%b0%8f%e8%8f%85%e6%9d%91%e3%81%a7%e3%82%aa%e3%83%bc%e3%82%bf%e3%82%b5%e3%83%b3%e3%81%a8%e3%83%8f%e3%83%af%e3%82%a4%e5%a4%a7%e5%ad%a6%e3%83%92%e3%83%ad%e6%a0%a1%e3%81%ae%e6%9c%ac%e7%94%b0%e5%85%88.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>奥多摩なんですね。行くのがすごく大変そうですが、自然が好きな人はぜひ！おそらくここ以外にも広島やいろんな所を周ると伺っています。</p>
<p>この本田先生には、５年ほど前にハワイ島でお世話になってから、関係が続いています。日本に帰っていらっしゃるときにいつも連絡をいただいており、これからも末永く良い関係を続けていきたいと思っています。</p>
<p>オータサンは、私は面識はないのですが、素晴らしいウクレレの演奏者です。2009年時に来たときに少しだけ抜粋をネットに上げていましたので、見てみてください。他にも YouTube に上がっているみたいですし（許しを得ているものかどうかは知らないですが。。。）、 amazon でも高い評価を得ていますね。ちょっと amazon へのリンクを貼ってみました。</p>
<p><iframe width="420" height="315" src="http://www.youtube.com/embed/40070PX2Oos" frameborder="0" allowfullscreen></iframe></p>
<p><OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" id="Player_23678c51-8660-4114-ab33-0abc08089a33"  WIDTH="600px" HEIGHT="200px"> <PARAM NAME="movie" VALUE="http://ws.amazon.co.jp/widgets/q?ServiceVersion=20070822&#038;MarketPlace=JP&#038;ID=V20070822%2FJP%2Fganchikudotco-22%2F8010%2F23678c51-8660-4114-ab33-0abc08089a33&#038;Operation=GetDisplayTemplate"><PARAM NAME="quality" VALUE="high"><PARAM NAME="bgcolor" VALUE="#FFFFFF"><PARAM NAME="allowscriptaccess" VALUE="always"><embed src="http://ws.amazon.co.jp/widgets/q?ServiceVersion=20070822&#038;MarketPlace=JP&#038;ID=V20070822%2FJP%2Fganchikudotco-22%2F8010%2F23678c51-8660-4114-ab33-0abc08089a33&#038;Operation=GetDisplayTemplate" id="Player_23678c51-8660-4114-ab33-0abc08089a33" quality="high" bgcolor="#ffffff" name="Player_23678c51-8660-4114-ab33-0abc08089a33" allowscriptaccess="always"  type="application/x-shockwave-flash" align="middle" height="200px" width="600px"></embed></OBJECT> <NOSCRIPT><A HREF="http://ws.amazon.co.jp/widgets/q?ServiceVersion=20070822&#038;MarketPlace=JP&#038;ID=V20070822%2FJP%2Fganchikudotco-22%2F8010%2F23678c51-8660-4114-ab33-0abc08089a33&#038;Operation=NoScript">Amazon.co.jp ウィジェット</A></NOSCRIPT></p>
<p>そして、本田先生とオータサンは、年に何度か一緒に小学校などの学校施設を回っているようです。本田先生のハワイに渡った日系人の話はとても興味深いです。基本的にはあまり学校で学ぶ機会がないことなので、こういう歴史もあったんだな、と勉強になります。</p>
<p><a href="https://sites.google.com/site/kosugegym/" target="_blanK">小菅村体育館こけらおとし記念</a>で申し込みができるようです。時間がある方、ウクレレが好きな方、ハワイ日系人について調べたい方、ぜひぜひどうぞー。日にちは、5月26日です。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2012/05/%e5%b0%8f%e8%8f%85%e6%9d%91%e3%81%a7%e3%82%aa%e3%83%bc%e3%82%bf%e3%82%b5%e3%83%b3%e3%81%a8%e3%83%8f%e3%83%af%e3%82%a4%e5%a4%a7%e5%ad%a6%e3%83%92%e3%83%ad%e6%a0%a1%e3%81%ae%e6%9c%ac%e7%94%b0%e5%85%88.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ベーシック認証の htpasswd の作成方法</title>
		<link>http://ganchiku.com/2012/05/%e3%83%99%e3%83%bc%e3%82%b7%e3%83%83%e3%82%af%e8%aa%8d%e8%a8%bc%e3%81%ae-htpasswd-%e3%81%ae%e4%bd%9c%e6%88%90%e6%96%b9%e6%b3%95.html</link>
		<comments>http://ganchiku.com/2012/05/%e3%83%99%e3%83%bc%e3%82%b7%e3%83%83%e3%82%af%e8%aa%8d%e8%a8%bc%e3%81%ae-htpasswd-%e3%81%ae%e4%bd%9c%e6%88%90%e6%96%b9%e6%b3%95.html#comments</comments>
		<pubDate>Fri, 11 May 2012 06:42:37 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=1161</guid>
		<description><![CDATA[単なるメモ。 いつも忘れてしまうので。。。。 apache がインストールしていなくて、 htpasswd が使えない時用。nginx のみしかインストールしていないときなど。 echo -e "username:`pe &#8230; <a href="http://ganchiku.com/2012/05/%e3%83%99%e3%83%bc%e3%82%b7%e3%83%83%e3%82%af%e8%aa%8d%e8%a8%bc%e3%81%ae-htpasswd-%e3%81%ae%e4%bd%9c%e6%88%90%e6%96%b9%e6%b3%95.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>単なるメモ。<br />
いつも忘れてしまうので。。。。 apache がインストールしていなくて、 htpasswd が使えない時用。nginx のみしかインストールしていないときなど。</p>
<pre class="brush:text">
echo -e "username:`perl -le 'print crypt("password","salt")'`" > htpasswd
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2012/05/%e3%83%99%e3%83%bc%e3%82%b7%e3%83%83%e3%82%af%e8%aa%8d%e8%a8%bc%e3%81%ae-htpasswd-%e3%81%ae%e4%bd%9c%e6%88%90%e6%96%b9%e6%b3%95.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FOSUserBundle で ユーザ名を削る</title>
		<link>http://ganchiku.com/2012/05/fosuserbundle-%e3%81%a7-%e3%83%a6%e3%83%bc%e3%82%b6%e5%90%8d%e3%82%92%e5%89%8a%e3%82%8b.html</link>
		<comments>http://ganchiku.com/2012/05/fosuserbundle-%e3%81%a7-%e3%83%a6%e3%83%bc%e3%82%b6%e5%90%8d%e3%82%92%e5%89%8a%e3%82%8b.html#comments</comments>
		<pubDate>Wed, 09 May 2012 18:09:18 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[Symfony2]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=1155</guid>
		<description><![CDATA[Symfony2 のバンドルで一番使われているものは、 FOSUserBundle のようですね。たくさんの人に使われているということは、つまり、それだけ完成度が高いということでもあると思います。私自身も最近になり FO &#8230; <a href="http://ganchiku.com/2012/05/fosuserbundle-%e3%81%a7-%e3%83%a6%e3%83%bc%e3%82%b6%e5%90%8d%e3%82%92%e5%89%8a%e3%82%8b.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Symfony2 のバンドルで一番使われているものは、 FOSUserBundle のようですね。たくさんの人に使われているということは、つまり、それだけ完成度が高いということでもあると思います。私自身も最近になり FOSUserBundle を使っており、その柔軟性と拡張性の恩恵を受けています。</p>
<p>FOSUserBundle はそのまま使っても確かに使えるのですが、よりアプリのニーズにあった柔軟な開発への適用も可能となっています。ユーザ管理でユーザ名を使用しないときなどもちょっとがんばれば可能になります。多くのウェブサービスでユーザ名でアイデンティファイするサービスの場合は確かにユニークな名前が必要になりますが、全てのウェブサービスがそうとは限りません。メールアドレスがユニークであれば、それで足りることもあると思います。となると、 FOSUserBundle のデフォルトの仕様であるユーザ名とメールアドレス、そしてパスワードを入力する、というのはやりすぎな感じがします。また、ユーザ名を毎回決めさせるのも、どうなんだろ。。。と思えます。</p>
<p>そんなときは拡張しましょう。</p>
<p>まず、フォームタイプを拡張し、username を取ってしまいましょう。</p>
<h4>src/Ganchiku/UserBundle/Form/Type/RegistrationFormType.php</h4>
<pre class="brush:php">
namespace Ganchiku\UserBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseFormType;

class RegistrationFormType extends BaseFormType
{   

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('email', 'email')
            ->add('plainPassword', 'repeated', array('type' => 'password'));
    }

    public function getName()
    {
        return 'ganchiku_user_registration';
    }
}
</pre>
<p>ここで変更した後には、app/config/config.yml と Ganchiku/UserBundle/Resources/config/services.xml を修正してみましょう。</p>
<h4>app/config/config.yml</h4>
<pre class="brush:text">
fos_user:
    ... snip ....
    registration:
        form:
            type: ganchiku_user_registration
</pre>
<h4>Ganchiku/UserBundle/Resources/config/serivces.xml</h4>
<pre class="brush:xml">
<!-- snip.. -->
      <service id="ganchiku_user.registration.form.type" class="Ganchiku\UserBundle\Form\Type\RegistrationFormType">
        <tag name="form.type" alias="ganchiku_user_registration" />
        <argument>%fos_user.model.user.class%</argument>
    </service>
<!-- snip.. -->
</pre>
<p>ちょっと省略しましたが、これで username のない FormType を表示することができます。しかし、これではバリデーションに引っかかってしまいます。username は一意でなくてはいけないですし、空ではダメなんです。そこで FormHandler も変更して、username に email の値を格納するようにしてみましょう。</p>
<h4>src/Ganchiku/UserBundle/Form/Handler/RegistrationFormHandler.php</h4>
<pre class="brush:php">
namespace Ganchiku\UserBundle\Form\Handler;

use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseFormHandler;

class RegistrationFormHandler extends BaseFormHandler
{
    public function process($confirmation = false)
    {
        $user = $this->userManager->createUser();
        $this->form->setData($user);

        if ('POST' === $this->request->getMethod()) {
            $this->form->bindRequest($this->request);

            // username に email の内容をコピーする
            $appData = $this->form->getData();
            $appData->setUsername($appData->getEmail());
            $appData->setUsernameCanonical($appData->getEmailCanonical());
            $this->form->setData($appData);
            $this->form->bindRequest($this->request);

            if ($this->form->isValid()) {
                $this->onSuccess($user, $confirmation);

                return true;
            }
        }
        return false;
    }
}
</pre>
<p>これでコピーされました。そして、config.yml で FormHandler に今の作成した Handler を指定します。</p>
<h4>app/config/config.yml</h4>
<pre class="brush:text">
fos_user:
    ...snip...
    registration:
        form:
            type: ganchiku_user_registration
            handler: ganchiku_user.form.handler.registration
</pre>
<h4>Ganchiku/UserBundle/Resources/config/serivces.xml</h4>
<pre class="brush:xml">
      <service id="ganchiku_user.form.handler.registration" class="Ganchiku\UserBundle\Form\Handler\RegistrationFormHandler" scope="request" public="false">
        <argument type="service" id="fos_user.registration.form" />
        <argument type="service" id="request" />
        <argument type="service" id="fos_user.user_manager" />
        <argument type="service" id="fos_user.mailer" />
      </service>
</pre>
<p>これで、今作成したFormHandler を使用することになり、バリデーションをする前に email の値を username にコピーすることができるようになりました。同じ内容のカラムを２つ持つのはどうなんだ！という意見もあると思います。内部のコードを書き換えずにそこまでできたら、いいのですが、今はできないんじゃないかなぁ。</p>
<p>最終的な app/config/config.yml と src/Ganchiku/UserBundle/Resources/config/services.xml は次のようになりました。</p>
<pre class="brush:text">
fos_user:
    db_driver:      orm
    firewall_name:  main
    user_class:     Application\Sonata\UserBundle\Entity\User
    group:
        group_class: Application\Sonata\UserBundle\Entity\Group
    registration:
        form:
            type: ganchiku_user_registration
            handler: ganchiku_user.form.handler.registration
        confirmation:
            enabled:    true
    from_email:
        address:        example@gmail.com
        sender_name:    Acme Demo App
</pre>
<p>user_class と group_class は SonataUserBundle を使用しているので、上のようになっています。</p>
<h4>src/Ganchiku/UserBundle/Resources/config/services.xml</h4>
<pre class="brush:xml">
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
      <service id="ganchiku_user.registration.form.type" class="Ganchiku\UserBundle\Form\Type\RegistrationFormType">
        <tag name="form.type" alias="ganchiku_user_registration" />
        <argument>%fos_user.model.user.class%</argument>
      </service>

      <service id="ganchiku_user.form.handler.registration" class="Ganchiku\UserBundle\Form\Handler\RegistrationFormHandler" scope="request" public="false">
        <argument type="service" id="fos_user.registration.form" />
        <argument type="service" id="request" />
        <argument type="service" id="fos_user.user_manager" />
        <argument type="service" id="fos_user.mailer" />
      </service>
    </services>

</container>
</pre>
<p>もっとスマートな方法がある！と知っている方は連絡をください。まだまだ私も勉強不足なので学ばせてもらいたいと思っています。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2012/05/fosuserbundle-%e3%81%a7-%e3%83%a6%e3%83%bc%e3%82%b6%e5%90%8d%e3%82%92%e5%89%8a%e3%82%8b.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Symfony2 でフォームの確認画面を作る</title>
		<link>http://ganchiku.com/2012/05/symfony2-%e3%81%a7%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e3%81%ae%e7%a2%ba%e8%aa%8d%e7%94%bb%e9%9d%a2%e3%82%92%e4%bd%9c%e3%82%8b.html</link>
		<comments>http://ganchiku.com/2012/05/symfony2-%e3%81%a7%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e3%81%ae%e7%a2%ba%e8%aa%8d%e7%94%bb%e9%9d%a2%e3%82%92%e4%bd%9c%e3%82%8b.html#comments</comments>
		<pubDate>Sun, 06 May 2012 05:11:21 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[Symfony2]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=1141</guid>
		<description><![CDATA[個人的には編集時に、プレビューを見ながらフォーム記入をする方が好きなのですが、確認画面が欲しい、というニーズはよくありますよね。 ちょっとやってみたので、メモとして書いておきます。 フォームの確認画面なのですが、特に画像 &#8230; <a href="http://ganchiku.com/2012/05/symfony2-%e3%81%a7%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e3%81%ae%e7%a2%ba%e8%aa%8d%e7%94%bb%e9%9d%a2%e3%82%92%e4%bd%9c%e3%82%8b.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>個人的には編集時に、プレビューを見ながらフォーム記入をする方が好きなのですが、確認画面が欲しい、というニーズはよくありますよね。<br />
ちょっとやってみたので、メモとして書いておきます。</p>
<p>フォームの確認画面なのですが、特に画像のアップロードが入ると結構厄介なんですよね。つまり、ファイルをどこに置くの？ってことになるので。と言うのも、確認画面の時点でアップロードが完了していないといけないんですよね。もし、そこで「やーめた」って思ったら、ファイルがゴミとして残ってしまうんですわ。また、「やっぱ他の画像にしよー」と思って戻られても、同様にファイルがゴミとして残ってしまうんですわ。ここは結構悩ましいところでいくつか方法があるのかな、と思います。私がパッと思いついた方法は、一時ディレクトリに保存しておいて、確認ができたら、ファイルを本番ディレクトリに移動って感じかな、と。</p>
<p>また、確認画面ってデータの保持ってどうするの？っていう問題があります。セッションに持っておいたり、hiddenで渡したり、一時的にデータベースに保存したり、とかいったことがよくされる方法ですかね。私はめんどくさがりやさんだったので、hiddenで渡しました。一時的にデータベースに保存した方が画像の保存のことも考えると楽かもしれません。</p>
<p>というわけで、ここでやろうとしていることを整理すると、次のようになります。</p>
<ul>
<li>登録フォームがあり、画像もアップロードができるようにフォームをレンダーする</li>
<li>バリデーションに引っかかったら、登録フォームに戻る</li>
<li>バリデーションに通ったら、確認画面に行く</li>
<li>確認画面では、各値は hidden で渡す</li>
<li>確認画面のフォームで本登録をする</li>
<li>確認画面のフォームで、修正ボタンが押されたら、値をセットして、フォームに戻る</li>
<li>本登録されたら、hidden に入っているデータをデータベースに登録し、画像も正しい場所に移動する</li>
</ul>
<p>さて、ここで厄介そうなのは、 hidden のフォームってどうするの、ってことかな、と。おそらく方法としては、新しく FormType を作る方法と、twig のフォーム周りの widget を修正して hidden で出す、ってくらいかな、と。私は後者でやってみました。</p>
<h4>confirm.html.twig</h4>
<pre class="brush:html">
{% form_theme form _self %}

{% block field_widget %}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
    {% if type is not defined or type != 'hidden' %}{{ value }}{% endif %}
{% endblock field_widget %}

{% block textarea_widget %}
  {% set type = 'hidden' %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>{{ value }}
{% endblock textarea_widget %}

{% block checkbox_widget %}
{% spaceless %}
<input type="hidden" {{ block('widget_attributes') }} value="{{ value }}" />
{% endspaceless %}
{% endblock checkbox_widget %}

{% block radio_widget %}
{% spaceless %}
<input type="hidden" {{ block('widget_attributes') }} value="{{ value }}" />
{% endspaceless %}
{% endblock radio_widget %}

{% block choice_widget %}
{% spaceless %}
    {% if expanded %}
<div {{ block('widget_container_attributes') }}>
        {% for child in form %}
           {% if child.vars.checked  %}
           {{ form_label(child) }}
            {{ form_widget(child) }}
           {% endif %}
        {% endfor %}
        </div>

    {% else %}
        {% for choice, label in choices %}
           {% if _form_is_choice_selected(form, choice) %}
          {{label|trans}}
<input type="hidden" {{ block('widget_attributes') }} value="{{value}}" />
          {% endif %}
        {% endfor %}
    {% endif %}
{% endspaceless %}
{% endblock choice_widget %}
</pre>
<p>とかやって、hidden で出力するように変更してあげます。choice の辺りが結構面倒でした。あとは、普通に扱ってあげれば大丈夫です。画像に関しては、確認画面では表示させてあげます。</p>
<pre class="brush:php">
<tr>
<th>{{ form_label(form.file) }}</th>
<td>
      {% if entity.path is not null %}
      <img src="{{ entity.getWebPath }}" />
      {% endif %}
      {{ form_widget(form.path, {'value': entity.path}) }}
      {{ form_widget(form.file) }}
      {{ form_widget(form.file_delete, {'value': null}) }}
      </td>
</tr>

...snip...


      {{ form_rest(form) }}
        <button type="submit" name="back" value="back">修正する</button>
        <button type="submit">登録</button>
    

...snip...
</pre>
<p>確認画面では、file を使用しませんし、file_delete も使用しません。ただ、path を FormType に加えて、一時保存先へのパスを入れておいてます。<br />
「修正する」ボタンを付けておきました。back が入っていれば、データを保持しつつフォームを表示するようにしています。</p>
<h4>DocumentType.php</h4>
<pre class="brush:php">
namespace Ganchiku\TestBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class DocumentType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('title', null, array(
                'label' => 'タイトル',
                'required' => true,
            ))
            ->add('file', null, array(
               'label' => '画像ファイル',
               'required' => false,
            ))
            ->add('file_delete', 'checkbox', array(
                'label' => '画像ファイル削除',
                'required' => false,
            ))
            ->add('path', 'hidden', array(
                'required' => false,
            ))
            ;
    public function getName()
    {
        return 'ganchiku_testbundle_documenttype';
    }
}
</pre>
<p>前回のポストの延長です。こんな感じで、path も追加しておいてあげます。hidden フィールドで。あとは Entity の方でゴニョゴニョします。Action でも少しだけセットしてあげたりしています。</p>
<h4>Document.php</h4>
<pre class="brush:php">
namespace Ganchiku\TestBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;

/**
 * Ganchiku\TestBundle\Entity\Document
 * @ORM\Table(name="document")
 * @ORM\HasLifecycleCallbacks
 */
class Document
{

    /**
     * @Assert\File(maxSize="6000000")
     */
    public $file;
    public $file_delete = false;
    public $file_tmp = false;
    public $file_confirmed = false;
    public $path_old = null;

    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $title
     *
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * @var string $path
     *
     * @ORM\Column(name="path", type="string", length="255", nullable=true)
     */
    private $path;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set path
     *
     * @param string $path
     */
    public function setPath($path)
    {
        $this->path = $path;
    }

    /**
     * Get path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }

    public function getAbsolutePath()
    {
        return null === $this->path ? null : $this->getUploadRootDir().'/' . $this->path;
    }

    public function getWebPath()
    {
        return null === $this->path ? null : $this->getUploadDir().'/' . $this->path;
    }

   /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->path and $this->file_delete) {
            $this->path_old = $this->getAbsolutePath();
            $this->path = null;
        }

        if (null !== $this->file) {
            $this->path = md5_file($this->file->getPathname()) . '.' . $this->file->guessExtension();
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if ($this->path_old) {
            unlink($this->path_old);
            return;
        }
        if ($this->file_confirmed) {
            $tmp_file = new File($this->getUploadRootDir(true) . '/' . $this->path);
            $tmp_file->move($this->getUploadRootDir(), $this->path);
            return;
        }

        if (null === $this->file) {
            return;
        }

        $this->file->move($this->getUploadRootDir(), $this->path);

        unset($this->file);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }
    }

    protected function getUploadRootDir($file_tmp = false)
    {
        return __DIR__.'/../../../../web' . $this->getUploadDir($file_tmp);
    }

    protected function getUploadDir($file_tmp = false)
    {
        if ($this->file_tmp or $file_tmp) {
            return '/uploads/tmp';
        }
        return '/uploads/documents';
    }

}
</pre>
<p>なんか public な変数が増えて少し嫌な感じですが、まぁ、許容範囲でしょう。ちょっとだけ説明しておきます。</p>
<p>$file は、実際のアップロードファイルが保存されます。 SplFileInfo の孫クラスの Symfony\Component\HttpFoundation\File\UploadedFile インスタンスが入ることになります。アップロードファイルがあれば。なければ、放置です。</p>
<p>$file_delete は、今回はあまり関係がありません。前回のポストを参照してください。ファイル削除のフラグを見ているだけです。</p>
<p>$file_tmp は、一時ファイルを使用するかどうかを見るフラグです。このフラグは、isser にした方がスマートかも。コントローラーの方で、confirmAction 内でセットしてあげます。getUploadDir() の保存先を変更するのに使用しています。</p>
<p>$file_confirmed は、一時ファイルから本番ディレクトリに移動する際に使用します。コントローラーの方で、createAction 内でセットしてあげます。upload()内の動作を変更しています。</p>
<p>$path_old は、今回はあまり関係がありません。前回のポストを参照してください。ファイルの削除の際のカラムの変更のために使用しています。</p>
<p>confirmAction はこんな感じになりました。</p>
<pre class="brush:php">
    /**
     * Displays a form confirmation to create a new Document entity.
     *
     * @Route("/confirm", name="document_confirm")
     * @Secure(roles="ROLE_USER")
     * @Method("post|get")
     * @Template()
     */
    public function confirmAction(Request $request)
    {
        if ($request->getMethod() == "GET") {
            return $this->redirect($this->generateUrl('document_new'));
        }
        $context = $this->get('security.context');
        $user = $context->getToken()->getUser();

        $entity  = new Document();
        $entity->file_tmp = true;
        $request = $this->getRequest();
        $form    = $this->createForm(new DocumentType(), $entity);
        $form->bindRequest($request);
        if ($form->isValid()) {
            $entity->preUpload();
            $entity->upload();
            // todo 本来は、 redirect すること
            // ここでは confirm ページを表示させる
            return array(
                'entity' => $entity,
                'form'   => $form->createView()
            );
        }

        $response = $this->render('GanchikuTestBundle:Document:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
        return $response;
    }
</pre>
<p>実際に保存をするわけではないので、preUpload()やupload()は呼び出されないので、アップロードのために直接書いておいてあげます。また、その前に file_tmp に true をセットしてファイルの保存先を変更してあげています。Method で GET を受け付けているのは、個人的な趣向です。redirect 先が confirm とかに来られると 405 エラーを出すので、嫌だなー、と。リダイレクトしちゃった方が実際はいいんじゃないかなぁ、と。 todo はしておいた方がいいですが、とりあえず。。。</p>
<pre class="brush:php">
    /**
     * Creates a new Document entity.
     *
     * @Route("/create", name="document_create")
     * @Secure(roles="ROLE_USER")
     * @Method("post")
     * @Template("GanchikuTestBundle:Document:new.html.twig")
     */
    public function createAction()
    {
        $context = $this->get('security.context');
        $user = $context->getToken()->getUser();

        $entity  = new Document();
        $entity->setUser($user);
        $entity->file_confirmed = true;
        $request = $this->getRequest();
        $form    = $this->createForm(new DocumentType(), $entity);
        $form->bindRequest($request);

        if ($request->request->get('back', null) != 'back') {
            if ($form->isValid()) {
                $em = $this->getDoctrine()->getEntityManager();
                $em->persist($entity);
                $em->flush();

                return $this->redirect($this->generateUrl('document_show', array('id' => $entity->getId())));
            }
        }
        return array(
            'entity' => $entity,
            'form'   => $form->createView()
        );
    }
</pre>
<p>ここで実際に保存（永続化）します。ただ、その前に file_confirmed に true をセットして、ファイルを移動してあげましょう。backは、修正する際に使用しており、フォームをデータを持ったまま表示させています。</p>
<p>なんか抜け落ちているような感じがしないでもないですが、こんな感じですかね。</p>
<p>あとは、一時ファイルを cron 等で定期的にお掃除してあげればいいかな、と。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2012/05/symfony2-%e3%81%a7%e3%83%95%e3%82%a9%e3%83%bc%e3%83%a0%e3%81%ae%e7%a2%ba%e8%aa%8d%e7%94%bb%e9%9d%a2%e3%82%92%e4%bd%9c%e3%82%8b.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.301 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-05-16 23:12:57 -->

