-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdemo-ittsample.html
182 lines (181 loc) · 14.5 KB
/
demo-ittsample.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Ethnamドキュメント</title>
<link href="css/style.css" media="all" rel="stylesheet" />
<link href="css/github.css" media="all" rel="stylesheet" />
</head>
<body>
<p><a href=".">目次</a></p>
<div class="blob instapaper_body">
<article class="markdown-body entry-content">
<h1>Ethnaでアプリを作ってみましょう。</h1>
<hr />
<p>書いた人:itoh@ <a href="http://www.itt-web.net/bwiki/index.html"><a href="http://www.itt-web.net/bwiki/index.html">http://www.itt-web.net/bwiki/index.html</a></a> <a href="http://www.itt-web.net/modules/bwiki/index.php?plugin=attach&pcmd=open&file=ittsample.tar.gz&refer=B-wiki%20Top">サンプルダウンロード</a></p>
<hr />
<p>私がEthnaで実際にアプリを作る時にこうやるなーって現時点での感じです。サンプルもダウンロードで来ます。表示はWindows風味になっていますがもちろんUnix系でも問題ないです。</p>
<p>今回は、ニュース更新スクリプトを書きます。要件は</p>
<ul>
<li>表画面から新着順にニュースが表示される</li>
<li>管理画面からニュースの編集が出来る</li>
<li>
<p>公開日時と掲載終了日時を設定できて、その期間のみ表示</p>
</li>
<li>Ethnaでアプリを作ってみましょう。
<ul>
<li>プロジェクトを作る </li>
<li>ライブラリを整える </li>
<li>DB設計をして、DB設定をして、AppObjectを作成してマネージャに登録 </li>
<li>アクションの追加 </li>
<li>アクションフォームの追加 </li>
<li>テンプレートの作成 </li>
<li>とりあえず、チェック </li>
<li>次のアクションに繋げる </li>
<li>Doのアクションは前のアクションを引き継ぐ </li>
<li>Doのアクションの具体的処理 </li>
<li>exportFormを使う </li>
<li>表で一覧を出す </li>
<li>Managerの仕事 </li>
<li>authenticateメソッドで権限管理 </li>
<li>エントリポイントの変更 </li>
</ul></li>
</ul>
<h3>プロジェクトを作る</h3>
<p>適当なところに作ります。</p>
<pre><code>C:\...\Ethna\bin>php generate_project_skelton.php ittsample sample1</code></pre>
<p>最初、適当なところに作ってしまっても移動させる際には、あとでエントリポイント(index.php)をの2行目のincludeのパスをちゃんと設定すれば問題ありません。</p>
<h3>ライブラリを整える</h3>
<p>Ethnaアプリがが必要とするライブラリを整えます。include_pathに置けばいいのですが、私はEthnaアプリのlibに入れています。つまり今回の場合だと、sample1/libになります。入れるのは</p>
<ul>
<li>Ethna本体</li>
<li>Smarty</li>
<li>PEAR::DB</li>
</ul>
<p>です。</p>
<h3>DB設計をして、DB設定をして、AppObjectを作成してマネージャに登録</h3>
<p>AppObject生成スクリプトがあるので、DBテーブルを先に作っておきます。使ったSQLはschema/に入れておきます。今回は、ethna_sampleというDBにsample1_newsというテーブルを作りました。</p>
<p>DB設定は、DSNをsample1/etc/sample1-ini.phpに記述します。</p>
<pre><code>'dsn' => 'mysql://mysqluserid:mysqluserpassword@mysqlhostname/mysqldbname' ,</code></pre>
<p>という感じです。</p>
<p>で、AppObjectを作ります。sample1/bin/にディレクトリ移動して</p>
<pre><code>C:\...\sample1\bin>php generate_app_object.php sample1_news</code></pre>
<p>でオッケーです。この時、AppManagerとAppObjectを記述したファイルはsample1/libにできるのですが、私はlibには「PEARなど汎用的なもの」を入れることにしているんので、sample1/appに移動させています。</p>
<p>できたSample1_Sample1Newsクラスには、auto_incrementな要素に</p>
<pre><code>seq => true ,</code></pre>
<p>を入れておきます。また、今後のアクションの追加でActionClassで簡単に使えるようにするために、Sample1_ControllerにManagerを登録しておきます。また、また、Sample_Controller.phpにこのマネージャクラスのファイルをincludeさせる必要があります。sample1/app/Sample1_Controller.phpに</p>
<pre><code>include_once 'Sample1_Sample1News.php' ;
class Sample1_Controller extends Ethna_Controller
{
........
* @var array マネージャ一覧
*/
var $manager = array(
'news' => 'Sample1News' )}</code></pre>
<p>としておきます。</p>
<h3>アクションの追加</h3>
<p>私はデータを入れたいので管理画面から作ります。で、ActionClassを作ります。sample1/bin/にディレクトリ移動して</p>
<pre><code>C:\...\sample1\bin>php generate_action_script.php admin_news</code></pre>
<p>とすると、sample1/app/action/Admin/News.phpが出来ています。</p>
<h4>アクションフォームの追加</h4>
<p>このファイルのアクションフォームクラスのSample1_Form_AdminNewsに、表画面で作りたいフォーム要素のプロパティを登録します。詳しくは、ダウンロードサンプルをご覧下さい。</p>
<h3>テンプレートの作成</h3>
<p>ここまで追加したら、デフォルトでは、sample1/tempates/ja/admin/news.tplが必要になります。ここに、先ほどActionFormに追加したフォーム要素を具体的にデザイン配置していきます。 この時、一つ一つ</p>
<pre><code><input type="text" name="n_title" value="{$form.n_title}" size="20" id="n_title"></code></pre>
<p>とか書いてもいいのですが、typeとかnameとかvalueの属性はActionFormで定義されているものです。そこで、Smartyプレースホルダを使います。Smarty関数を登録するだけです。藤本さんのサンプルを拡張したものがあるので、それをsample1/appディレクトリに置き、Sample1_Controllerクラスでincludeさせます。また、このSmarty関数をSample1_Controllerクラス内の所定の場所で登録します。</p>
<pre><code>include_once('Sample1_SmartyPlugin.php');
....
/**
* @var array smarty function定義
*/
var $smarty_function_plugin = array(
'smarty_function_form_name',
'smarty_function_form_input',
);</code></pre>
<p>これを登録することで、テンプレート中で、</p>
<pre><code>{form_input name="n_title" attr=" size='20' id='n_title' "}</code></pre>
<p>とすることで、先ほどのタグと同じ効果が得られます。</p>
<p>テキストフォームではあまり効果がありませんが、一番強力なのはセレクトタブの選択タブを自動作成する時に便利です。ActionClassのコンストラクタでセレクトタブのオプションを動的に定義したりするときには必須です。</p>
<h3>とりあえず、チェック</h3>
<p>ココまできたら、とりあえずチェックします。sample1/www/index.phpをコピーして、自分の好きなDocumentRootのところにもって行ってください。ダウンロードサンプルではpublic_html/sample1/index.phpとしました。それがSample1_Controller.phpを適切にincludeできるようにパスを調整する必要があります。ダウンロードサンプルの場合は、</p>
<pre><code>include_once('../../include/sample1/app/Sample1_Controller.php');
Sample1_Controller::main('Sample1_Controller', 'index');</code></pre>
<p>となっています。このindex.phpにアクセスして表示されるかどうかチェックします。</p>
<pre><code>http://localhost/ittsample/public_html/sample1/</code></pre>
<p>ではEthnaデフォルトのIndexページなので、先ほど作ったActionClassを呼ぶには</p>
<pre><code>http://localhost/ittsample/public_html/sample1/?action_admin_news=1</code></pre>
<p>とします。</p>
<h3>次のアクションに繋げる</h3>
<p>テンプレートに</p>
<pre><code><form action="" method="POST">
<input type="hidden" name="action_admin_news_do" value="1"></code></pre>
<p>と書くことで、次のアクションを決めます。*1</p>
<p>そこで、</p>
<pre><code>C:\...\sample1\bin>php generate_action_script.php admin_news_do</code></pre>
<p>として、ActionClass/ActionFormのスクリプトを追加します。</p>
<h3>Doのアクションは前のアクションを引き継ぐ</h3>
<p>Doのアクションを定義する際には、前のアクションがあっての事なので、前のアクションを継承するように作った方が何かと便利なことが多いです。 コード的にはActionFormを</p>
<pre><code>require_once dirname( __FILE__ ).'.php'
class Sample1_Form_AdminNewsDo extends Sample1_Form_AdminNews
{
}</code></pre>
<p>として、前のアクションの完全なコピーに。ActionClassも</p>
<pre><code>class Sample1_Action_AdminNewsDo extends Sample1_Action_AdminNews
{
function Sample1_Action_AdminNewsDo(&$ctl){
parent::Sample1_Action_AdminNews(&$ctl);
}
....
}</code></pre>
<p>としてやることで、ActionFormの定義を二度書きせずに済みます。</p>
<h3>Doのアクションの具体的処理</h3>
<p><a href="ethna-tutorial-startup-practice3.html">Ethnaのチュートリアルにもあるように</a>、prepareメソッドでフォーム値のチェックを行い、performで実働処理をします。</p>
<p>performでの処理は、AppObjectを使うことで極めて簡単に処理できます。 具体的処理は、ソースを見てもらうとして、ポイントは、</p>
<ul>
<li>データのプライマリIDがあればUPDATE処理、無ければADD処理</li>
<li>importFormを継承してそのオブジェクトに適した形にする かなと思います。後者は、Sample1_Sample1Newsクラスを見てみてください。Formから投稿された時点では「公開日」「掲載終了日」は文字列ですからunixtimestampに直してからimportFormしてやる必要があります。</li>
</ul>
<h3>exportFormを使う</h3>
<p>逆に、投稿した記事を編集したい場合は、</p>
<pre><code>http://localhost/ittsample/public_html/sample1/?action_admin_news=1&nid=3</code></pre>
<p>とかにアクセスします。それだけだと、フォームの中身は空のままなので、ActionFormにnid=3の時の値を入れてやります。そのメソッドがexportFormです。</p>
<p>Sample1_Sample1AdminNews::prepare()で、AppObjectを取得して行います。*2</p>
<pre><code>if($this->af->get('nid')){
$news =& new Sample1_Sample1News(&$this->backend, array('nid'), array($this->af->get('nid')));
$news->exportForm();
}</code></pre>
<p>だけです。スゴくお手軽ですね。 ただし、この時「公開日」「掲載終了日」はunixtimestampから文字列に直してからexportFormしてやる必要があります。</p>
<h3>表で一覧を出す</h3>
<p>これでデータが投稿できるようになったので、表面で一覧を出します。</p>
<pre><code>C:\...\sample1\bin>php generate_action_script.php news</code></pre>
<p>で、sample1/app/action/News.phpを編集します。表面では、フォームは必要ないので特に定義しません。その代わり、投稿されたニュースを複数持ってくるという作業が必要になります。これは、Sample1_Sample1News::perform()メソッドで行います</p>
<p>ただし、ActionClassではあくまでActionだけを書きたいので、実際の処理はManagerに任せます。そのため、performには</p>
<pre><code>$news = $this->news->getValidNews();
$this->af->setApp('news', $news);</code></pre>
<p>とだけ書いて、テンプレート側に値を渡してやるだけになります。*3</p>
<h3>Managerの仕事</h3>
<p>ActionClassは「状態の遷移を決めるだけ」ということにしたいので、面倒な処理はManagerクラスに任せます。実は、Manager*4に任せると、データキャッシュを行っているので、ActionClassで状態の遷移を決めるために取得した値は、ViewClassでテンプレートに渡したい値だった場合にはDBに再問い合わせをせずに済みます。ただし、もちろん「同じQUERYになる場合」です。</p>
<h3>authenticateメソッドで権限管理</h3>
<p>普通、管理者でないと投稿や投稿記事の編集はできません。EthnaではActionClassのauthenticateメソッドを使うことで楽に権限管理ができます。今回は、権限管理として簡単にBasic認証を使ったと仮定します。./adminディレクトリには、Basic認証がかかってるとします。</p>
<h3>エントリポイントの変更</h3>
<p>./admin/index.phpにエントリポイントを変更します。もともと2行だけのエントリポイントなので特に苦労することはありませんが、</p>
<pre><code>Sample1_Controller::main('Sample1_Controller', 'admin_news');</code></pre>
<p>としておくと、ここにアクセスした時に、デフォルトのアクションがadmin_newsとなり便利です。</p>
<p>そして、Sample1_Action_AdminNewsクラスのauthenticateメソッドを</p>
<pre><code>function authenticate(){
if(!preg_match('/admin/', $_SERVER['SCRIPT_NAME'])){
exit();
}
}</code></pre>
<p>としてやります。こうすることで、さきほどのエントリポイントからはアクセスが出来なくなります。なお、admin_news_doアクションにも自動的にこのauthenticateが引き継がれます。</p>
<hr />
<p>*1最初は私も違和感を感じたのですが、慣れるとこの方法がとてもコントローラブルなものであることに気が付きました。<br />
*2別にperformでも良いですけど。<br />
*3実は、この「テンプレートに値を渡す」というのはViewクラスの仕事になります。ActionClassはあくまでどのテンプレートに辿り着くか(つまりどのViewに辿り着くか)の遷移を把握しておくだけの存在だからです。複雑なActionを考える時には便利な思考パターンになると思います。しかし、こんな簡単な処理にまでViewを使うと面倒なのでここではActionClassでテンプレートに値を渡しています。<br />
*4厳密にはEthna_AppManager::getObjectPropListとEthna_AppManager::getObjectList </p> </article>
</div>
<div class="site-footer">
@2015 <a href="https://twitter.com/DQNEO">DQNEO</a>
</div>
</body>
</html>