プロジェクト

全般

プロフィール

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|>>]] |