GuideActionElse » 履歴 » バージョン 4
NAITOH Jun, 2013/03/06 23:51
Redmine 2.x対応の記載に更新
1 | 4 | NAITOH Jun | h1. その他のアクション(詳細表示、編集、削除、プレビュー) |
---|---|---|---|
2 | 1 | Mitsuyoshi Yoshida | |
3 | 4 | NAITOH Jun | それでは残った 4 つのアクションを一気に片付けていきましょう。 |
4 | 1 | Mitsuyoshi Yoshida | |
5 | * 詳細表示( show ) |
||
6 | * 削除( destroy ) |
||
7 | * 編集( edit ) |
||
8 | 4 | NAITOH Jun | * プレビュー( preview ) |
9 | 1 | Mitsuyoshi Yoshida | |
10 | |||
11 | h2. Foo オブジェクト(@foo)の取得 |
||
12 | |||
13 | 3 | Mitsuyoshi Yoshida | show, destroy, edit のアクションでは params[:id] に入っている id から対象となる Foo のオブジェクトを取得する必要があります。 |
14 | 4 | NAITOH Jun | 3 つのメソッドで同じことを行います。こういうときには *before_filter* の出番です。ただし、 index や new, preview では必要ありません。 before_filter では *:only* オプションで実行する対象を指定することも、 *:except* オプションで実行しない対象を指定することも出来ます。サンプルでは 3 つのアクションだけですが、チケットのようにコピーや移動などのアクションを追加したくなるかもれません。そこで :except で index と new, preview を指定することにします。 |
15 | 1 | Mitsuyoshi Yoshida | |
16 | find_project と同じように find_foo メソッドを追加します。 |
||
17 | |||
18 | <pre><code class="ruby"> |
||
19 | class FoosController < ApplicationController |
||
20 | unloadable |
||
21 | menu_item :standard |
||
22 | before_filter :find_project, :authorize |
||
23 | 4 | NAITOH Jun | before_filter :find_foo, :except => [:index, :new, :preview] |
24 | 1 | Mitsuyoshi Yoshida | |
25 | # : |
||
26 | |||
27 | private |
||
28 | def find_project |
||
29 | 3 | Mitsuyoshi Yoshida | @project = Project.find(params[:project_id]) |
30 | 1 | Mitsuyoshi Yoshida | rescue ActiveRecord::RecordNotFound |
31 | render_404 |
||
32 | end |
||
33 | |||
34 | def find_foo |
||
35 | 3 | Mitsuyoshi Yoshida | @foo = Foo.find_by_id(params[:id]) |
36 | 1 | Mitsuyoshi Yoshida | render_404 unless @foo |
37 | end |
||
38 | end |
||
39 | </code></pre> |
||
40 | |||
41 | ActiveRecord::Base クラスでは ruby のメタプログラミングを利用して find_by_XXX という名前のクラスメソッドで XXX というメンバをキーとしてテーブルを検索することが出来ます。ここでは *Foo.find_by_id* で id の値が一致するオブジェクトを検索しています。 |
||
42 | ただし、 Project.find と違って見つからなかった場合は nil を返すだけで例外は発生しません。 |
||
43 | |||
44 | |||
45 | h2. 詳細表示( show アクション ) |
||
46 | |||
47 | 詳細表示のコントローラでは、 Foo オブジェクトを取得してビューを生成するだけなので、 show メソッドはスケルトン作成時のままで何も追加することはありません。 |
||
48 | |||
49 | <pre><code class="ruby"> |
||
50 | def show |
||
51 | end |
||
52 | </code></pre> |
||
53 | |||
54 | ビュー(show.html.erb) で @foo オブジェクトの中身を表示することになります。 |
||
55 | 表示は Redmine で定義されている CSS クラスを使って、ちょっとチケット風にしています。 |
||
56 | |||
57 | <pre><code class="rhtml"> |
||
58 | <h2><%=h l(:label_foo) %>#<%= @foo.id %></h2> |
||
59 | |||
60 | <div class="issue"> |
||
61 | |||
62 | <div class="subject"> |
||
63 | <h3><%= @foo.subject %></h3> |
||
64 | </div> |
||
65 | |||
66 | 2 | Mitsuyoshi Yoshida | <% unless @foo.description.blank? %> |
67 | 1 | Mitsuyoshi Yoshida | <p><strong><%=l(:field_description)%></strong></p> |
68 | <div class="wiki"> |
||
69 | 2 | Mitsuyoshi Yoshida | <%= textilizable @foo.description %> |
70 | 1 | Mitsuyoshi Yoshida | </div> |
71 | <% end %> |
||
72 | |||
73 | </div></code></pre> |
||
74 | |||
75 | 説明のパラメータは CSS クラスを *wiki* にし、 *textilizable* メソッドを使うことによって、wiki を解析した結果を出力しています。 |
||
76 | |||
77 | |||
78 | また、詳細表示ページでは右上に *編集* と *削除* リンクをつけます。 |
||
79 | |||
80 | <pre><code class="rhtml"> |
||
81 | <div class="contextual"> |
||
82 | <%= link_to_if_authorized(l(:button_edit), |
||
83 | 3 | Mitsuyoshi Yoshida | {:action => 'edit', :project_id => @project, :id => @foo.id}, |
84 | 1 | Mitsuyoshi Yoshida | :class => 'icon icon-edit') %> |
85 | <%= link_to_if_authorized(l(:button_delete), |
||
86 | 3 | Mitsuyoshi Yoshida | {:action => 'destroy', :project_id => @project, :id => @foo.id}, |
87 | 1 | Mitsuyoshi Yoshida | :confirm => l(:text_are_you_sure), :method => :post, |
88 | :class => 'icon icon-del') %> |
||
89 | </div> |
||
90 | </code></pre> |
||
91 | |||
92 | 削除用リンクには *:confirm=>l(:text_are_you_sure)* オプションをつけています。これによりリンク実行前に確認ダイアログを表示されるようになります。 |
||
93 | |||
94 | この詳細表示ページの実行結果は次のようになります。 |
||
95 | |||
96 | !show.png! |
||
97 | |||
98 | |||
99 | h2. 削除( destroy アクション ) |
||
100 | |||
101 | destroy はコントローラのスケルトンで作成していませんでした。そこで中身だけでなく、定義からコントローラ(foos_controller.rb)に記述することになります。 |
||
102 | |||
103 | <pre><code class="ruby"> |
||
104 | def destroy |
||
105 | @foo.destroy |
||
106 | 3 | Mitsuyoshi Yoshida | redirect_to :action => 'index', :project_id => @project |
107 | 1 | Mitsuyoshi Yoshida | end |
108 | </code></pre> |
||
109 | |||
110 | Foo オブジェクトの *destroy* メソッドを使ってデータベースからデータを削除しています。 |
||
111 | その後、そのままだと destroy.html.erb のビューを表示しようとしますので、 redirect_to を使って一覧表示ページを表示しています。 |
||
112 | |||
113 | |||
114 | h2. 編集( edit アクション ) |
||
115 | |||
116 | edit アクションは new アクションと同じような感じで作成します。 |
||
117 | コントローラは次のようになります。 |
||
118 | |||
119 | <pre><code class="ruby"> |
||
120 | def edit |
||
121 | 4 | NAITOH Jun | if request.put? |
122 | 1 | Mitsuyoshi Yoshida | @foo.attributes = params[:foo] |
123 | if @foo.save |
||
124 | flash[:notice] = l(:notice_successful_update) |
||
125 | 4 | NAITOH Jun | redirect_to :action => 'show', :project_id => @project, :id => @foo.id |
126 | 1 | Mitsuyoshi Yoshida | end |
127 | end |
||
128 | rescue ActiveRecord::StaleObjectError |
||
129 | flash.now[:error] = l(:notice_locking_conflict) |
||
130 | end |
||
131 | </code></pre> |
||
132 | |||
133 | 詳細表示ページのリンクから呼ばれる場合は特に何もせずにビューを生成、表示します。 |
||
134 | |||
135 | 4 | NAITOH Jun | フォームの [編集] ボタンから呼ばれる場合には、 new の場合と同様に save メソッドを使ってデータベースに保存しています。 new との違いは次の 3 点です。 |
136 | 1 | Mitsuyoshi Yoshida | |
137 | * id 番号は同じにしなければならないので、 new の引数ではなく *attributes* でパラメータのみ変更 |
||
138 | * 同時に編集している人がいるとエラーで例外が発生するので、その場合にはエラーメッセージを表示 |
||
139 | 4 | NAITOH Jun | * *POST* リクエストではなく *PUT* リクエスト |
140 | 1 | Mitsuyoshi Yoshida | |
141 | ビューは次のようになります。 |
||
142 | 3 | Mitsuyoshi Yoshida | |
143 | 1 | Mitsuyoshi Yoshida | <pre><code class="rhtml"> |
144 | 4 | NAITOH Jun | <%= labelled_form_for :foo, @foo, |
145 | {:url => {:controller => 'foos', :action => 'edit', :id => @foo.id}, |
||
146 | 1 | Mitsuyoshi Yoshida | :html => {:multipart => true, :id => 'foo-form'}} do |f| %> |
147 | <%= render :partial => 'foos/form', :locals => {:form => f} %> |
||
148 | <%= f.submit l(:button_edit) %> |
||
149 | 4 | NAITOH Jun | <%= preview_link( {:action => 'preview', :project_id => @project}, 'foo-form' ) %> |
150 | 1 | Mitsuyoshi Yoshida | <% end %> |
151 | 4 | NAITOH Jun | <div id="preview" class="wiki"></div> |
152 | 1 | Mitsuyoshi Yoshida | </code></pre> |
153 | |||
154 | 4 | NAITOH Jun | labelled_form_for の :url の指定は new では :id のパラメータを省略していましたが、 edit では :id のパラメータを指定する必要があります。 |
155 | 1 | Mitsuyoshi Yoshida | |
156 | 後はラベルなどの表示を変えているだけで new とほぼ同じです。 |
||
157 | |||
158 | 4 | NAITOH Jun | h2. プレビュー( preview アクション ) |
159 | 3 | Mitsuyoshi Yoshida | |
160 | 4 | NAITOH Jun | preview もコントローラのスケルトンで作成していませんでした。同様に定義からコントローラ(foos_controller.rb)に記述します。 |
161 | |||
162 | <pre><code class="ruby"> |
||
163 | def preview |
||
164 | @text = params[:foo][:description] |
||
165 | render :partial => 'common/preview' |
||
166 | end |
||
167 | </code></pre> |
||
168 | |||
169 | これが edit アクションの preview_link から呼ばれることになります。 |
||
170 | ここでは、Redmine のヘルパーメソッドの preview 機能を呼び出してプレビュー表示を実現しています。 |
||
171 | |||
172 | |||
173 | 1 | Mitsuyoshi Yoshida | h2. レイアウト |
174 | |||
175 | 最後に html ページのタイトルをつけましょう。 |
||
176 | |||
177 | 他の文書やチケットを参考にタイトルの付け方を次のようにします。 |
||
178 | |||
179 | |_. ページ |_. タイトル | |
||
180 | | 一覧表示、新規作成 | (プロジェクト名) - 標準 - Redmine | |
||
181 | | 詳細表示、編集 | (プロジェクト名) - (題名) - Redmine | |
||
182 | |||
183 | html のタイトルを付けるには *html_title* メソッドをビュー内で呼び出します。 |
||
184 | このとき前後のプロジェクト名と Redmine の文字は Redmine が自動的に付けます。 |
||
185 | |||
186 | 例えば、 index.html.erb に次のコードを追加します。 |
||
187 | |||
188 | <pre><code class="rhtml"> |
||
189 | <% html_title(l(:label_standard)) %> |
||
190 | </code></pre> |
||
191 | |||
192 | このときプロジェクト名が "デモ" とするとタイトルは次のようになります。 |
||
193 | |||
194 | <pre> |
||
195 | デモ - 標準 - Redmine |
||
196 | </pre> |
||
197 | |||
198 | それでは早速、他のビューにもこの文をコピペしてと .... |
||
199 | もう何がいいたいかお分かりですね。 繰り返しを避ける方法があります。 |
||
200 | |||
201 | コントローラの before_filter のようにビューすべてにコードを追加したい場合には *レイアウト* というものを使用します。 |
||
202 | |||
203 | レイアウト用の html.erb ファイルは *app/views/layouts* ディレクトリにおきます。 |
||
204 | スケルトンでは layouts は作成されていませんので、 layouts ディレクトリを作って app/views/layouts/standard.html.erb ファイルを作成します。このときファイル名は何でもかまいません。 |
||
205 | |||
206 | <pre><code class="rhtml"> |
||
207 | <% html_title((@foo && !@foo.subject.blank?) ? @foo.subject : l(:label_standard)) %> |
||
208 | <%= render :file => "layouts/base" %> |
||
209 | </code></pre> |
||
210 | |||
211 | html_title の引数は決めたタイトルになるように条件で文字列を変えています。 |
||
212 | |||
213 | 最後の行でデフォルトのレイアウトを出力しています。 |
||
214 | 4 | NAITOH Jun | この layouts/base は Redmine の *app/views/layouts/base.html.erb* ファイルを指しています。このファイルはレイアウトを指定しない場合に使用されているものです。レイアウトに standard.html.erb を指定すると使用するレイアウトはそちらに置き換わってしまいます。 タイトルの指定だけの追加とするためには standard.html.erb 内で再度 base のレイアウトを呼び出す必要があります。 |
215 | 1 | Mitsuyoshi Yoshida | |
216 | レイアウトの指定はコントローラのクラス定義内に次のコードを追加します。 |
||
217 | |||
218 | <pre><code class="ruby"> |
||
219 | layout 'standard' |
||
220 | </code></pre> |
||
221 | |||
222 | これでどのアクションに対しても layouts/standard.html.erb の内容がビューのページに付けられてタイトルが変更されるようになります。 |
||
223 | |||
224 | *これでサンプルプラグインは完成です!!* |
||
225 | |||
226 | 最終的なコントローラのファイル(foos_controller.rb)は以下のようになります。 |
||
227 | |||
228 | <pre><code class="ruby"> |
||
229 | class FoosController < ApplicationController |
||
230 | unloadable |
||
231 | menu_item :standard |
||
232 | before_filter :find_project, :authorize |
||
233 | before_filter :find_foo, :except => [:index, :new, :preview] |
||
234 | 3 | Mitsuyoshi Yoshida | |
235 | helper :attachments |
||
236 | 1 | Mitsuyoshi Yoshida | |
237 | layout 'standard' |
||
238 | |||
239 | def index |
||
240 | @foos = Foo.find(:all, :conditions => ["project_id = #{@project.id} "]) |
||
241 | end |
||
242 | |||
243 | |||
244 | def new |
||
245 | @foo = Foo.new(params[:foo]) |
||
246 | @foo.project_id = @project.id |
||
247 | |||
248 | 3 | Mitsuyoshi Yoshida | if request.post? and @foo.save |
249 | Attachment.attach_files(@foo, params[:attachments]) |
||
250 | render_attachment_warning_if_needed(@foo) |
||
251 | 1 | Mitsuyoshi Yoshida | flash[:notice] = l(:notice_successful_create) |
252 | 3 | Mitsuyoshi Yoshida | redirect_to :action => 'show', :project_id => @project, :id => @foo.id |
253 | 1 | Mitsuyoshi Yoshida | end |
254 | end |
||
255 | |||
256 | |||
257 | def show |
||
258 | end |
||
259 | |||
260 | |||
261 | def edit |
||
262 | 4 | NAITOH Jun | if request.put? |
263 | 1 | Mitsuyoshi Yoshida | @foo.attributes = params[:foo] |
264 | if @foo.save |
||
265 | 3 | Mitsuyoshi Yoshida | Attachment.attach_files(@foo, params[:attachments]) |
266 | render_attachment_warning_if_needed(@foo) |
||
267 | 1 | Mitsuyoshi Yoshida | flash[:notice] = l(:notice_successful_update) |
268 | 3 | Mitsuyoshi Yoshida | redirect_to :action => 'show', :project_id => @project, :id => @foo.id |
269 | 1 | Mitsuyoshi Yoshida | end |
270 | end |
||
271 | rescue ActiveRecord::StaleObjectError |
||
272 | flash.now[:error] = l(:notice_locking_conflict) |
||
273 | end |
||
274 | |||
275 | |||
276 | def destroy |
||
277 | @foo.destroy |
||
278 | 3 | Mitsuyoshi Yoshida | redirect_to :action => 'index', :project_id => @project |
279 | 1 | Mitsuyoshi Yoshida | end |
280 | |||
281 | |||
282 | 3 | Mitsuyoshi Yoshida | def preview |
283 | @text = params[:foo][:description] |
||
284 | render :partial => 'common/preview' |
||
285 | end |
||
286 | |||
287 | |||
288 | 1 | Mitsuyoshi Yoshida | private |
289 | def find_project |
||
290 | 3 | Mitsuyoshi Yoshida | @project = Project.find(params[:project_id]) |
291 | 1 | Mitsuyoshi Yoshida | rescue ActiveRecord::RecordNotFound |
292 | render_404 |
||
293 | end |
||
294 | |||
295 | def find_foo |
||
296 | 3 | Mitsuyoshi Yoshida | @foo = Foo.find_by_id(params[:id]) |
297 | 1 | Mitsuyoshi Yoshida | render_404 unless @foo |
298 | end |
||
299 | end |
||
300 | </code></pre> |
||
301 | |||
302 | |||
303 | |||
304 | --- |
||
305 | |||
306 | | [[プラグイン開発ガイド|^]] | [[GuideNewAction|<<]] | [[GuideConclusion|>>]] | |