<?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>BPS株式会社 開発ブログ Beyond Perspective Solutions LTD. &#187; Ethna</title>
	<atom:link href="http://www.bpsinc.jp/blog/archives/category/framework/ethna/feed" rel="self" type="application/rss+xml" />
	<link>http://www.bpsinc.jp/blog</link>
	<description>BPS株式会社（Beyond Perspective Solutions）のプログラマによる技術・開発などに関してのブログです</description>
	<lastBuildDate>Wed, 20 Jul 2011 08:14:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Ethna_Util::isDuplicatePostが動かない</title>
		<link>http://www.bpsinc.jp/blog/archives/1240</link>
		<comments>http://www.bpsinc.jp/blog/archives/1240#comments</comments>
		<pubDate>Fri, 19 Mar 2010 00:43:29 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[馬場]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=1240</guid>
		<description><![CDATA[Ethnaで二重POSTを防止するには、templateに{uniqid}を埋め込み、アクションでEthna_Util::isDuplicatePost()を使います。
しかし、これを実施しても、二重POSTがチェックさ [...]]]></description>
			<content:encoded><![CDATA[<p>Ethnaで二重POSTを防止するには、templateに{uniqid}を埋め込み、アクションでEthna_Util::isDuplicatePost()を使います。</p>
<p>しかし、これを実施しても、二重POSTがチェックされず素通りしてしまうことがあります。</p>
<p>これは、isDuplicatePost()の動作に起因しています。</p>
<p>isDuplicatePost()は、uniqidで渡された値とIPアドレスを元にファイル名を決定し、tmpディレクトリ以下にそれが存在するかをチェックしています。<br />
該当ファイルが存在しない場合、そのファイルをtmpディレクトリに作成しますが、作成に失敗すると重複POSTチェックは働きません。</p>
<p>tmpディレクトリに書き込めない場合のほか、クライアント側がIPv6の時にもこの現象が生じます。<br />
たとえばWindows環境でlocalhostで動作させる場合、REMOTE_ADDRが127.0.0.1になれば問題ないですが、::1 になってしまったばあい、コロンのつくファイル名は使えないので、作成に失敗します。<br />
これによって、isDuplicatePost()を素通りしてしまう現象が発生します。</p>
<p>とりあえず解決するには、hostsファイルでlocalhostの::1を消して、127.0.0.1だけを指定すればOKですね。<br />
まさかPHPフレームワークでIPv6対応が必要とは・・・</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/1240/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ethna_DBでfetchArrayのようなことをする</title>
		<link>http://www.bpsinc.jp/blog/archives/1183</link>
		<comments>http://www.bpsinc.jp/blog/archives/1183#comments</comments>
		<pubDate>Wed, 03 Mar 2010 00:33:50 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[馬場]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=1183</guid>
		<description><![CDATA[PHPでmysql_queryの結果オブジェクトは、mysql_fetch_row()しても良いですが、mysql_fetch_array()した方が便利ですよね。
※mysql_fetch_row()では、結果を通常の [...]]]></description>
			<content:encoded><![CDATA[<p>PHPでmysql_queryの結果オブジェクトは、mysql_fetch_row()しても良いですが、mysql_fetch_array()した方が便利ですよね。<br />
※mysql_fetch_row()では、結果を通常の配列で取得しますが、mysql_fetch_array()では、カラム名をキーにした連想配列で取得できます</p>
<p>EthnaでAppObjectを使わずにEthna_DBを直接たたく場合、結果オブジェクトはPEAR::DB_Resultオブジェクトです。<br />
これはfetchRow()のメソッドを持ちますが、fetchArray()は存在しません。<br />
<a href="http://pear.plus-server.net/package.database.db.db-result.fetchrow.html">http://pear.plus-server.net/package.database.db.db-result.fetchrow.html</a></p>
<p>カラム名をキーにした連想配列が欲しい場合、queryを投げる前にsetFetchMode(DB_FETCHMODE_ASSOC)を実行しておく必要があります。<br />
<a href="http://pear.plus-server.net/package.database.db.db-common.setfetchmode.html">http://pear.plus-server.net/package.database.db.db-common.setfetchmode.html</a></p>
<p>EthnaのデフォルトDBアダプタ、DB_PEARは、内部にPEAR::DBを持っているので、以下のようなコードを書けばOKです。</p>
<pre class="brush:php">
$db = $this-&#62;backend-&#62;getDB();
$db-&#62;db-&#62;setFetchMode(DB_FETCHMODE_ASSOC);
$res = $db-&#62;query($sql);
print_r($res-&#62;fetchRow());
</pre>
<p>※<a href="http://ethna.jp/ethna-document-dev_guide-db.html">http://ethna.jp/ethna-document-dev_guide-db.html</a>に<br />
<q>Ethnaの デフォルトの DB接続クラス(Ethna_DB_PEAR) はPEAR::DBを継承しているので</q><br />
と書いてありますが、継承ではなくて委譲なので、$db->db->setFetchMode()となります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/1183/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ethnaコマンド</title>
		<link>http://www.bpsinc.jp/blog/archives/1126</link>
		<comments>http://www.bpsinc.jp/blog/archives/1126#comments</comments>
		<pubDate>Mon, 15 Feb 2010 11:02:07 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[馬場]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=1126</guid>
		<description><![CDATA[WindowsのXAMPP環境でEthnaを使う際、Ethnaコマンドの設定でよく戸惑います。
プロジェクトの環境依存を減らすため、Ethna自体をプロジェクトのlibフォルダに入れるのが好きなのですが、そのような環境だ [...]]]></description>
			<content:encoded><![CDATA[<p>WindowsのXAMPP環境でEthnaを使う際、Ethnaコマンドの設定でよく戸惑います。</p>
<p>プロジェクトの環境依存を減らすため、Ethna自体をプロジェクトのlibフォルダに入れるのが好きなのですが、そのような環境だと</p>
<ul>
<li>add-project で Could not open input file: ～\Ethna\bin\\bin\ethna_handle.php となどのエラー</li>
<li>add-action で Fatal error: Could not redeclare class Ethna などのエラー</li>
</ul>
<p>が出ることがあります。</p>
<p>私の使い方では、以下のような設定に落ち着きました。</p>
<p><strong>共通の設定</strong><br />
phpにパスを通します。xampp/php のフォルダを環境変数のPATHで設定します。<br />
ethna.bat があるフォルダにはパスを通さない方が良いです。</p>
<p><strong>add-project をするとき</strong><br />
Ethnaをダウンロードして、workspaceに設置します。<br />
workspace/Ethna/bin/ethna.bat を、workspaceにコピーします。</p>
<p>workspace/ethna.bat を開き、@PEAR-DIR@ を .\Ethna に一括置換します（2カ所）。</p>
<p>これで、コマンドプロンプトで、workspaceに入り、<br />
C:\Users\baba\workspace &gt; ethna add-project hoge<br />
と実行できます。</p>
<p><strong>add-action などをするとき</strong><br />
add-project 以外では、Ethnaのプロジェクトのディレクトリにいる必要があります。<br />
hogeプロジェクトがある場合、workspace/hoge/lib フォルダに、Ethna（とSmarty）を設置しておきます。</p>
<p>workspace/hoge/lib/Ethna/bin/ethna.bat を、workspace/hoge にコピーします。<br />
workspace/hoge/ethna.bat を開き、@PEAR-DIR@ を、 .\lib\Ethna に一括置換します（2カ所）。</p>
<p>これで、コマンドプロンプトでhogeディレクトリに入り、<br />
C:\Users\baba\workspace\hoge > ethna add-action hoge_index<br />
と実行できます。</p>
<p>以上で実行できますが、正直面倒なので、Eclipseプラグインを作ろうと思います。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/1126/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ethnaでデフォルトアクション名を変更する</title>
		<link>http://www.bpsinc.jp/blog/archives/640</link>
		<comments>http://www.bpsinc.jp/blog/archives/640#comments</comments>
		<pubDate>Thu, 19 Nov 2009 08:53:53 +0000</pubDate>
		<dc:creator>hiko</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[プログラミング言語]]></category>
		<category><![CDATA[中井]]></category>
		<category><![CDATA[投稿者]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=640</guid>
		<description><![CDATA[久々のEthna小ネタです。
Ethnaでのデフォルトアクション名はAppID_Action_Hogeのようなスタイルのアクション名ですが、これを変更したい場合のTIPSです。
例えば、AppID_HogeActionの [...]]]></description>
			<content:encoded><![CDATA[<p>久々のEthna小ネタです。</p>
<p>Ethnaでのデフォルトアクション名はAppID_Action_Hogeのようなスタイルのアクション名ですが、これを変更したい場合のTIPSです。</p>
<p>例えば、AppID_HogeActionのようにアクションのクラス名を変更したいと思います。</p>
<blockquote><p>AppID_Action_Hoge => AppID_HogeAction</p></blockquote>
<p>その場合は下記のようにAppID_Controller.php内にgetDefaultActionClassメソッドをオーバーライドすることで簡単に実現できます。<br />
ちなみに、アクションをコマンドから作る場合にコントローラに下記のコードを書いておくだけで命名規則が変更されるため、アクションを作る前にコントローラに実装しておくことがお薦めです。<br />
また、ビュー（View）やアクションフォーム（ActionForm）などの命名規則も同様にgetDefaultViewClass, getDefaultFormClassをオーバーライドするだけです。</p>
<pre class="brush:php">
function getDefaultActionClass($action_name, $gateway = null)  {
  $gateway_prefix = $this-&#62;_getGatewayPrefix($gateway);

  $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
  $r = sprintf("%s_%s%sAction", $this-&#62;getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
  $this-&#62;logger-&#62;log(LOG_DEBUG, "default action class &#91;%s&#93;", $r);

  return $r;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/640/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ethnaでユーザ権限ごとのアクセス許可を一括管理する</title>
		<link>http://www.bpsinc.jp/blog/archives/270</link>
		<comments>http://www.bpsinc.jp/blog/archives/270#comments</comments>
		<pubDate>Thu, 02 Jul 2009 10:53:56 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[馬場]]></category>
		<category><![CDATA[セキュリティ]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=270</guid>
		<description><![CDATA[Ethnaでアクセス制限をする場合、一般的にActionクラスのauthenticate()メソッドを使います。
アクション数が多くなってくると、個別に書くのは面倒なので、継承を駆使する場合が多いと思います。
しかしそれ [...]]]></description>
			<content:encoded><![CDATA[<p>Ethnaでアクセス制限をする場合、一般的にActionクラスのauthenticate()メソッドを使います。<br />
アクション数が多くなってくると、個別に書くのは面倒なので、継承を駆使する場合が多いと思います。<br />
しかしそれでも、ユーザ権限が何種類もあると、管理が煩雑になってしまいます。<br />
今回は、ユーザ権限ごとのアクセス許可を一元管理する方法を紹介します。</p>
<p>まず、ユーザ種別はdefineしてあるものとします。</p>
<blockquote><p>
define(&#8217;USER_TYPE_ANONYMOUS&#8217;, 0);<br />
define(&#8217;USER_TYPE_GUEST&#8217;,&nbsp;&nbsp;&nbsp;&nbsp; 1);<br />
define(&#8217;USER_TYPE_MEMBER&#8217;,&nbsp;&nbsp;&nbsp;&nbsp;2);<br />
define(&#8217;USER_TYPE_ADMIN&#8217;,&nbsp;&nbsp;&nbsp;&nbsp; 3);
</p></blockquote>
<p>次に、ユーザ種別ごとのアクセス許可を、適当なファイルにグローバル変数で記述します。<br />
グローバル変数にするメリットは、index.phpからControllerを生成する前に読み込めるため、BASIC認証と連動しやすい点です。<br />
このアクセス許可一覧変数では、ユーザ種別定数をキーに、アクセス許可するアクション名一覧の配列を値にします。</p>
<blockquote><p>
require_once &#8216;Appid_Const.php&#8217;; //USER_TYPE_*をdefineしてあるファイルをrequire<br />
/**<br />
 * 権限毎のアクセス可能アクション一覧<br />
 * アクション名の最後の1文字のみ、ワイルドカードとして*(アスタリスク)を使える<br />
 */<br />
$accept_action_list = array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;USER_TYPE_ADMIN =&lt; array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;*',<br />
&nbsp;&nbsp;&nbsp;&nbsp;),<br />
&nbsp;&nbsp;&nbsp;&nbsp;USER_TYPE_MEMBER =&lt; array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;entry_*&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;comment_*&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;inquiry_*&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;),<br />
&nbsp;&nbsp;&nbsp;&nbsp;USER_TYPE_GUEST =&lt; array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;comment_*&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;inquiry_*&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;),<br />
&nbsp;&nbsp;&nbsp;&nbsp;USER_TYPE_ANONYMOUS =&lt; array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;index&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8217;help&#8217;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;),<br />
);
</p></blockquote>
<p>また、Appid_Sessionには、getUserType()というメソッドを作り、ユーザ種別を取得できるようにしておきます。</p>
<p>そして、以下のメソッドを、Appid_ActionClassに入れます。</p>
<blockquote><p>
/**<br />
* このアクションにアクセスできるかどうか調べる<br />
* @return アクセスできるときtrue, 出来ないときfalse<br />
*/<br />
function _authenticate()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;global $accept_action_list;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;$controller =&#038; $this-&gt;backend-&gt;getController();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ($controller-&gt;_isAcceptableActionName($controller-&gt;getCurrentActionName(), $accept_action_list[USER_TYPE_ANONYMOUS])) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//ログインなしでアクセスできるアクションの場合<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;} else if ($this-&gt;session-&gt;isStart()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$userType = $this-&gt;session-&gt;getUserType(); //現在の権限を取得<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (is_null($userType)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   $this-&gt;ae-&gt;add(null, &#8216;ログインしていません&#8217;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$list = $accept_action_list[$userType]; //現在の権限でアクセスできるアクション<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!$controller-&gt;_isAcceptableActionName($controller-&gt;getCurrentActionName(), $list)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;ae-&gt;add(null, &#8216;権限がありません&#8217;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true; //認証された<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;ae-&gt;add(null, &#8216;ログインしていません&#8217;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}
</p></blockquote>
<p>あとは、authenticate()メソッドで</p>
<blockquote><p>
function authenticate()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ($this-&gt;_authenticate() === true)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return &#8216;index&#8217;; //ログイン画面へ飛ばす<br />
}
</p></blockquote>
<p>とすれば完了です。</p>
<p>全アクションの許可・拒否を一括管理できるので、漏れが無くて安心です。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/270/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>getObjectListの罠</title>
		<link>http://www.bpsinc.jp/blog/archives/228</link>
		<comments>http://www.bpsinc.jp/blog/archives/228#comments</comments>
		<pubDate>Fri, 05 Jun 2009 09:44:48 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[馬場]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=228</guid>
		<description><![CDATA[昨日に引き続きEthnaネタです。今回は当たり前のことですが。。
AppManagerのgetObjectListを使う場合に、
$filter = array(&#8217;number&#8217; =&#62; &# [...]]]></description>
			<content:encoded><![CDATA[<p>昨日に引き続きEthnaネタです。今回は当たり前のことですが。。</p>
<p>AppManagerのgetObjectListを使う場合に、</p>
<blockquote><p>$filter = array(&#8217;number&#8217; =&gt; &#8216;0123&#8242;);</p></blockquote>
<p>などと、省略記法を使う場合が多いと思います。<br />
デフォルトではstring型はLIKE扱いになるので、</p>
<blockquote><p>WHERE number LIKE &#8216;%0123%&#8217;</p></blockquote>
<p>というSQLが生成されてしまいます。これでは、郵便番号や会員番号などで検索する場合などに困ります。</p>
<p>もちろん、</p>
<blockquote><p>$filter = array(&#8217;number&#8217; =&gt; new Ethna_AppSearchObject(&#8217;0123&#8242;, OBJECT_CONDITION_EQ);</p></blockquote>
<p>とすれば良いのですが、たぶんEQの方がよく使うので、めんどくさいです。</p>
<p>なので、getObjectListをオーバーライドして、省略時はEQになるようにしました。</p>
<blockquote style="white-space:pre;overflow-x:scroll"><p>
function getObjectList($class, $filter=null, $order=null, $offset=null, $count=null)<br />
{<br />
  if (is_array($filter)) {<br />
    foreach ($filter as $key =&gt; $value) {<br />
      if (is_string($value)) {<br />
        $filter[$key] = new Ethna_AppSearchObject($value, OBJECT_CONDITION_EQ);<br />
      }<br />
    }<br />
  }<br />
  return parent::getObjectList($class, $filter, $order, $offset, $count);<br />
}
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/228/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AppObjectのupdateを改良</title>
		<link>http://www.bpsinc.jp/blog/archives/223</link>
		<comments>http://www.bpsinc.jp/blog/archives/223#comments</comments>
		<pubDate>Thu, 04 Jun 2009 09:00:23 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[馬場]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=223</guid>
		<description><![CDATA[EthnaのAppObjectネタです。
add()したあと、auto_incrementで決定されたIDに応じて会員番号を生成する、といったときに、AppObjectのadd()をオーバーライドして、

class A [...]]]></description>
			<content:encoded><![CDATA[<p>EthnaのAppObjectネタです。</p>
<p>add()したあと、auto_incrementで決定されたIDに応じて会員番号を生成する、といったときに、AppObjectのadd()をオーバーライドして、</p>
<blockquote><p>
class Appid_UserAppObject extends Appid_AppObject<br />
{<br />
  //オーバーライド<br />
  function add()<br />
  {<br />
    parent::add();</p>
<p>    //会員番号numberは、t+6桁の連番とする<br />
    $this-&gt;set(&#8217;number&#8217;, sprintf(&#8217;t%06d&#8217;, $this-&gt;get(&#8217;id&#8217;)));<br />
    $this-&gt;update();<br />
  }<br />
}
</p></blockquote>
<p>としたいものですが、これだとupdateの時にSQLエラーが出ることがあります。<br />
このエラーは、プロパティにNULLが含まれているときに、中途半端なSQLが生成されているのが原因です。</p>
<p>これに対処するには、Appid_AppObjectで、以下の関数をオーバーライドします。</p>
<blockquote><p>
/**<br />
 *  オブジェクトプロパティを更新するSQL文を構築する<br />
 *<br />
 *  @access private<br />
 *  @return オブジェクトプロパティを更新するためのUPDATE文<br />
 */<br />
function _getSQL_Update()<br />
{<br />
    $tables = implode(&#8217;,',<br />
        $this-&gt;my_db_rw-&gt;quoteIdentifier(array_keys($this-&gt;table_def)));</p>
<p>    // SET句構築<br />
    $set_list = &#8220;&#8221;;<br />
    $prop_arg_list = $this-&gt;prop;<br />
    Ethna_AppSQL::escapeSQL($prop_arg_list, $this-&gt;my_db_type);<br />
    foreach ($this-&gt;prop_def as $k =&gt; $v) {<br />
      //この下のif文を追加<br />
      if (isset($prop_arg_list[$k]) &amp;&amp; $prop_arg_list[$k] !== null &amp;&amp; $prop_arg_list[$k] !== &#8221;) {<br />
            if ($set_list != &#8220;&#8221;) {<br />
                $set_list .= &#8220;,&#8221;;<br />
            }<br />
            $set_list .= sprintf(&#8221;%s=%s&#8221;,<br />
                                 $this-&gt;my_db_rw-&gt;quoteIdentifier($k),<br />
                                 $prop_arg_list[$k]);<br />
      }<br />
    }</p>
<p>    // 検索条件(primary key)<br />
    $condition = null;<br />
    foreach (to_array($this-&gt;id_def) as $k) {<br />
        if (is_null($condition)) {<br />
            $condition = &#8220;WHERE &#8220;;<br />
        } else {<br />
            $condition .= &#8221; AND &#8220;;<br />
        }<br />
        $v = $this-&gt;prop_backup[$k];    // equals to $this-&gt;id<br />
        Ethna_AppSQL::escapeSQL($v, $this-&gt;my_db_type);<br />
        $condition .= Ethna_AppSQL::getCondition(<br />
            $this-&gt;my_db_rw-&gt;quoteIdentifier($k), $v);<br />
    }</p>
<p>    $sql = &#8220;UPDATE $tables SET $set_list $condition&#8221;;</p>
<p>    return $sql;<br />
}
</p></blockquote>
<p>UPDATE文を生成するときに、値がセットされていないものをSETしないようにif文を追加しただけです。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/223/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>EthnaのimportFormの強化版</title>
		<link>http://www.bpsinc.jp/blog/archives/210</link>
		<comments>http://www.bpsinc.jp/blog/archives/210#comments</comments>
		<pubDate>Wed, 15 Apr 2009 12:34:15 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[馬場]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=210</guid>
		<description><![CDATA[EthnaのAppObject::importForm()は便利ですが、DBの全カラムと同じ名前のform値を取得してしまうため、たとえばzipcodeはハイフン前後で2つのFormを用意して結合した値をセットする、等の [...]]]></description>
			<content:encoded><![CDATA[<p>EthnaのAppObject::importForm()は便利ですが、DBの全カラムと同じ名前のform値を取得してしまうため、たとえばzipcodeはハイフン前後で2つのFormを用意して結合した値をセットする、等の処理が少々やりにくいです。<br />
また、どの値がインポートされるのか見えにくいのが何となく気持ち悪い、という人もいると思います。</p>
<p>そこで、AppObjectに下記のようなメソッドを作ってみました。<br />
実際には全AppObjectの基底クラスにすると良いと思います。</p>
<blockquote><p>
class AppId_Hoge extends Ethna_AppObject<br />
{<br />
&nbsp;&nbsp;function setAll($list, $option = null)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;foreach ($list as $key =&gt; $data) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$name = &#8221;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$value = &#8221;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (is_int($key)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//連想配列のキーが数値の場合は、連想配列の値と同名のform値をセットする<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$name = $data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$value = $this-&gt;af-&gt;get($data);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//値がNULLの場合、オプション指定によって、無視するか空文字列に変換するかを決定する<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (is_null($value)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($option == OBJECT_IMPORT_IGNORE_NULL) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//NULLを無視<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$value = &#8221;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//それ以外の場合、連想配列のキーがそのまま名前に、連想配列の値がそのまま設定すべき値になる<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$name = $key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$value = $data;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (isset($this-&gt;prop_def[$name]) == false) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ethna::raiseError(&#8221;Unknown property $name&#8221;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;set($name, $value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
}
</p></blockquote>
<p>使うときは</p>
<blockquote><p>
$list = array(<br />
&nbsp;&nbsp;&#8217;user_name&#8217;,<br />
&nbsp;&nbsp;&#8217;password&#8217;,<br />
&nbsp;&nbsp;&#8217;zipcode&#8217; =&gt; $this-&gt;af-&gt;get(&#8217;zipcode1&#8242;).$this-&gt;af-&gt;get(&#8217;zipcode2&#8242;)<br />
);<br />
$obj =&#038; $this-&gt;backend-&gt;getObject(&#8217;Hoge&#8217;);<br />
$obj-&gt;setAll($list);
</p></blockquote>
<p>のようにすると、単純に配列指定したuser_nameとpasswordはForm値が直接セットされ、連想配列になっているzipcodeは、$list['zipcode']の値がセットされます。<br />
これによって、ほとんどの値はActionFormからそのままセットするが、任意のフィールドだけを特別な値にする、といった処理が簡単になります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/210/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ethnaでレイアウト</title>
		<link>http://www.bpsinc.jp/blog/archives/205</link>
		<comments>http://www.bpsinc.jp/blog/archives/205#comments</comments>
		<pubDate>Wed, 15 Apr 2009 12:08:11 +0000</pubDate>
		<dc:creator>baba</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[馬場]]></category>
		<category><![CDATA[レイアウト]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=205</guid>
		<description><![CDATA[割と既出ネタですが。。。
Ethnaでも、CakePHPなどのようなレイアウトテンプレートを使いたくなります。
ViewClassのforwardを変更すると、意外と簡単に実現できました。
Controllerの$dir [...]]]></description>
			<content:encoded><![CDATA[<p>割と既出ネタですが。。。</p>
<p>Ethnaでも、CakePHPなどのようなレイアウトテンプレートを使いたくなります。<br />
ViewClassのforwardを変更すると、意外と簡単に実現できました。<br />
Controllerの$directoryに&#8217;layout&#8217;のパスをセットして、ViewClassのforwardを下記のようにオーバーライドします。</p>
<blockquote><p>
class AppId_ViewClass extends Ethna_ViewClass<br />
{<br />
    function forward()<br />
    {<br />
        //Rendererを取得<br />
        $renderer =&#038; $this-&gt;_getRenderer();<br />
        $this-&gt;_setDefault($renderer);<br />
<br />
        //各Viewのコンテンツを取得<br />
        $contents = $renderer-&gt;perform($this-&gt;forward_path, true);<br />
<br /> <br />
        //レイアウトを取得<br />
        $layout = $renderer-&gt;getProp(&#8217;layout_file&#8217;);<br />
        if ($layout) {<br />
            $controller =&#038; $this-&gt;backend-&gt;getController();<br />
            $dir = $controller-&gt;getDirectory(&#8217;layout&#8217;);<br />
            $layout_file = $controller-&gt;getDirectory(&#8217;layout&#8217;) . &#8216;/&#8217; . $layout . &#8216;.&#8217; . $controller-&gt;getExt(&#8217;tpl&#8217;);<br />
            $renderer-&gt;setProp(&#8217;contents&#8217;, $contents);<br />
            $renderer-&gt;perform($layout_file);<br />
        } else {<br />
            echo $contents;<br />
        }<br />
    }
</p></blockquote>
<p>これで、各tplで<br />
{assign var=&#8221;layout_file&#8221; value=&#8221;default&#8221;}<br />
と指定すれば、default.tplが呼ばれて、default.tpl内の {$contents} のところに各Viewのコンテンツが挿入されます。</p>
<p>レイアウトファイルに各Viewのテンプレート内から値を渡したいときは、{assign}を使えばOKです。<br />
tplの見通しが良くなって、便利ですよ。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/205/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ethna2.5以降でTemplateのディレクトリ・パスを変えたい場合</title>
		<link>http://www.bpsinc.jp/blog/archives/201</link>
		<comments>http://www.bpsinc.jp/blog/archives/201#comments</comments>
		<pubDate>Sun, 15 Mar 2009 14:39:09 +0000</pubDate>
		<dc:creator>hiko</dc:creator>
				<category><![CDATA[Ethna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[中井]]></category>

		<guid isPermaLink="false">http://www.bpsinc.jp/blog/?p=201</guid>
		<description><![CDATA[ethna2.3.6までは、デフォルトのTemplateディレクトリ（template/ja）から変更したい時に{App_Id}_ControllerでgetTemplatedirをオーバーライドするのが定石でした。et [...]]]></description>
			<content:encoded><![CDATA[<p>ethna2.3.6までは、デフォルトのTemplateディレクトリ（template/ja）から変更したい時に{App_Id}_ControllerでgetTemplatedirをオーバーライドするのが定石でした。ethnaコマンドもgetTemplatedirを見てくれるのでethna add-templateなども問題ありませんでした。</p>
<p>ただ、Ethna-2.5以降では、国際化対応のためLocaleが追加され、以下のジェネレータプラグインのコードのコメント通り(39,40行目）、Localeが入っていないと勝手に補完してくれるようです。このため、ethna add-templateコマンドでtemplateを生成する際にLocaleをディレクトリパスに強制的に入れられてしまうため、国際化対応したくない場合（笑）はここをスキップする必要があります。</p>
<p>今回は綺麗に拡張するのが面倒だったためコメントアウトしましたが、今後は何らかの切り替えができるといいかもしれませんね。</p>
<p>Ethna/class/Plugin/Generator/Ethna_Plugin_Generator_Template.php</p>
<blockquote><p>
13 /**<br />
14  *  スケルトン生成クラス<br />
15  *<br />
16  *  @author     Masaki Fujimoto &lt;fujimoto@php.net&gt;<br />
17  *  @access     public<br />
18  *  @package    Ethna<br />
19  */<br />
20 class Ethna_Plugin_Generator_Template extends Ethna_Plugin_Generator<br />
21 {<br />
22     /**<br />
23      *  テンプレートのスケルトンを生成する<br />
24      *<br />
25      *  @access public<br />
26      *  @param  string  $forward_name   テンプレート名<br />
27      *  @param  string  $skelton        スケルトンファイル名<br />
28      *  @param  string  $locale         ロケール名<br />
29      *  @param  string  $encoding       エンコーディング<br />
30      *  @return true|Ethna_Error        true:成功 Ethna_Error:失敗<br />
31      */<br />
32     function &amp;generate($forward_name, $skelton = null, $locale, $encoding)<br />
33     {<br />
34         //  ロケールが指定された場合は、それを優先する<br />
35         if (!empty($locale)) {<br />
36             $this-&gt;ctl-&gt;setLocale($locale);<br />
37         }<br />
38<br />
39         //  ロケール名がディレクトリに含まれていない場合は、<br />
40         //  ディレクトリがないためなのでそれを補正<br />
41         $tpl_dir = $this-&gt;ctl-&gt;getTemplatedir();<br />
42         if (!empty($locale) &amp;&amp; strpos($tpl_dir, $locale) === false) {<br />
43             $tpl_dir = $this-&gt;ctl-&gt;getDirectory(&#8217;template&#8217;);<br />
44             $tpl_dir .= &#8220;/$locale&#8221;;<br />
45         }<br />
46         if ($tpl_dir{strlen($tpl_dir)-1} != &#8216;/&#8217;) {<br />
47             $tpl_dir .= &#8216;/&#8217;;<br />
48         }
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.bpsinc.jp/blog/archives/201/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

