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