プロジェクト

全般

プロフィール

GuideActionElse » 履歴 » バージョン 5

NAITOH Jun, 2013/04/14 21:33
RESTfulな実装に変更。

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