プロジェクト

全般

プロフィール

GuideActionElse » 履歴 » バージョン 3

Mitsuyoshi Yoshida, 2012/01/18 23:51

1 1 Mitsuyoshi Yoshida
h1. その他のアクション(詳細表示、編集、削除)
2
3
それでは残った 3 つのアクションを一気に片付けていきましょう。
4
5
* 詳細表示( show )
6
* 削除( destroy )
7
* 編集( edit )
8
9
10
h2. Foo オブジェクト(@foo)の取得
11
12 3 Mitsuyoshi Yoshida
show, destroy, edit のアクションでは params[:id] に入っている id から対象となる Foo のオブジェクトを取得する必要があります。 
13 1 Mitsuyoshi Yoshida
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 3 Mitsuyoshi Yoshida
    @project = Project.find(params[:project_id])
29 1 Mitsuyoshi Yoshida
  rescue ActiveRecord::RecordNotFound
30
    render_404
31
  end
32
33
  def find_foo
34 3 Mitsuyoshi Yoshida
    @foo = Foo.find_by_id(params[:id])
35 1 Mitsuyoshi Yoshida
    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 2 Mitsuyoshi Yoshida
<% unless @foo.description.blank? %>
66 1 Mitsuyoshi Yoshida
  <p><strong><%=l(:field_description)%></strong></p>
67
  <div class="wiki">
68 2 Mitsuyoshi Yoshida
    <%= textilizable @foo.description %>
69 1 Mitsuyoshi Yoshida
  </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 3 Mitsuyoshi Yoshida
                          {:action => 'edit', :project_id => @project, :id => @foo.id},
83 1 Mitsuyoshi Yoshida
			  :class => 'icon icon-edit') %>
84
<%= link_to_if_authorized(l(:button_delete),
85 3 Mitsuyoshi Yoshida
                          {:action => 'destroy', :project_id => @project, :id => @foo.id},
86 1 Mitsuyoshi Yoshida
			   :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 3 Mitsuyoshi Yoshida
    redirect_to :action => 'index', :project_id => @project
106 1 Mitsuyoshi Yoshida
  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 3 Mitsuyoshi Yoshida
        redirect_to :action => 'show', :project_id => @project, :id => @foo
125 1 Mitsuyoshi Yoshida
      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
<% labelled_tabular_form_for :foo, @foo,
143 3 Mitsuyoshi Yoshida
                             {:url => {:id => @foo.id},
144
			      :html => {:multipart => true, :id => 'foo-form'}} do |f| %>
145 1 Mitsuyoshi Yoshida
    <%= render :partial => 'foos/form', :locals => {:form => f} %>
146
    <%= f.submit l(:button_edit) %>
147 3 Mitsuyoshi Yoshida
    <%= link_to_remote l(:label_preview), 
148
                       { :url => {:action => 'preview', :project_id => @project },
149
                         :method => 'post',
150
                         :update => 'preview',
151
                         :with => "Form.serialize('foo-form')",
152
                         :complete => "Element.scrollTo('preview')"
153
                       }, :accesskey => accesskey(:preview) %>
154 1 Mitsuyoshi Yoshida
<% end %>
155
</code></pre>
156
157 3 Mitsuyoshi Yoshida
labelled_tabular_form_for の :url の指定は new ではすべて省略していましたが、 edit では :id のパラメータを指定する必要があります。
158 1 Mitsuyoshi Yoshida
159
後はラベルなどの表示を変えているだけで new とほぼ同じです。
160
161
162
h2. レイアウト
163
164
最後に html ページのタイトルをつけましょう。
165
166
他の文書やチケットを参考にタイトルの付け方を次のようにします。
167
168
|_. ページ           |_. タイトル                          |
169
| 一覧表示、新規作成 | (プロジェクト名) - 標準 - Redmine   |
170
| 詳細表示、編集     | (プロジェクト名) - (題名) - Redmine |
171
172
html のタイトルを付けるには *html_title* メソッドをビュー内で呼び出します。
173
このとき前後のプロジェクト名と Redmine の文字は Redmine が自動的に付けます。
174
175
例えば、 index.html.erb に次のコードを追加します。
176
177
<pre><code class="rhtml">
178
<% html_title(l(:label_standard)) %>
179
</code></pre>
180
181
このときプロジェクト名が "デモ" とするとタイトルは次のようになります。
182
183
<pre>
184
デモ - 標準 - Redmine
185
</pre>
186
187
それでは早速、他のビューにもこの文をコピペしてと ....
188
もう何がいいたいかお分かりですね。 繰り返しを避ける方法があります。
189
190
コントローラの before_filter のようにビューすべてにコードを追加したい場合には *レイアウト* というものを使用します。
191
192
レイアウト用の html.erb ファイルは *app/views/layouts* ディレクトリにおきます。
193
スケルトンでは layouts は作成されていませんので、 layouts ディレクトリを作って app/views/layouts/standard.html.erb ファイルを作成します。このときファイル名は何でもかまいません。
194
195
<pre><code class="rhtml">
196
<% html_title((@foo && !@foo.subject.blank?) ? @foo.subject : l(:label_standard)) %>
197
<%= render :file => "layouts/base" %>
198
</code></pre>
199
200
html_title の引数は決めたタイトルになるように条件で文字列を変えています。
201
202
最後の行でデフォルトのレイアウトを出力しています。
203
この layouts/base は Redmine の *app/views/layouts/base.rhtml* ファイルを指しています。このファイルはレイアウトを指定しない場合に使用されているものです。レイアウトに standard.html.erb を指定すると使用するレイアウトはそちらに置き換わってしまいます。 タイトルの指定だけの追加とするためには standard.html.erb 内で再度 base のレイアウトを呼び出す必要があります。
204
205
レイアウトの指定はコントローラのクラス定義内に次のコードを追加します。
206
207
<pre><code class="ruby">
208
  layout 'standard'
209
</code></pre>
210
211
これでどのアクションに対しても layouts/standard.html.erb の内容がビューのページに付けられてタイトルが変更されるようになります。
212
213
*これでサンプルプラグインは完成です!!*
214
215
最終的なコントローラのファイル(foos_controller.rb)は以下のようになります。
216
217
<pre><code class="ruby">
218
class FoosController < ApplicationController
219
  unloadable
220
  menu_item :standard
221
  before_filter :find_project, :authorize
222 3 Mitsuyoshi Yoshida
  before_filter :find_foo, :except => [:index, :new, :preview]
223 1 Mitsuyoshi Yoshida
224 3 Mitsuyoshi Yoshida
  helper :attachments
225
226 1 Mitsuyoshi Yoshida
  layout 'standard'
227
228
  def index
229
    @foos = Foo.find(:all, :conditions => ["project_id = #{@project.id} "])
230
  end
231
232
233
  def new
234
    @foo = Foo.new(params[:foo])
235
    @foo.project_id = @project.id
236
237 3 Mitsuyoshi Yoshida
    if request.post? and @foo.save
238
      Attachment.attach_files(@foo, params[:attachments])
239
      render_attachment_warning_if_needed(@foo)
240 1 Mitsuyoshi Yoshida
      flash[:notice] = l(:notice_successful_create)
241 3 Mitsuyoshi Yoshida
      redirect_to :action => 'show', :project_id => @project, :id => @foo.id
242 1 Mitsuyoshi Yoshida
    end
243
  end
244
245
246
  def show
247
  end
248
249
250
  def edit
251
    if request.post?
252
      @foo.attributes = params[:foo]
253
      if @foo.save
254 3 Mitsuyoshi Yoshida
        Attachment.attach_files(@foo, params[:attachments])
255
        render_attachment_warning_if_needed(@foo)
256 1 Mitsuyoshi Yoshida
        flash[:notice] = l(:notice_successful_update)
257 3 Mitsuyoshi Yoshida
        redirect_to :action => 'show', :project_id => @project, :id => @foo.id
258 1 Mitsuyoshi Yoshida
      end
259
    end
260
  rescue ActiveRecord::StaleObjectError
261
    flash.now[:error] = l(:notice_locking_conflict)
262
  end
263
264
265
  def destroy
266
    @foo.destroy
267 3 Mitsuyoshi Yoshida
    redirect_to :action => 'index', :project_id => @project
268 1 Mitsuyoshi Yoshida
  end
269
270
271 3 Mitsuyoshi Yoshida
  def preview
272
    @text = params[:foo][:description]
273
    render :partial => 'common/preview'
274
  end
275
276
277 1 Mitsuyoshi Yoshida
private
278
  def find_project
279 3 Mitsuyoshi Yoshida
    @project = Project.find(params[:project_id])
280 1 Mitsuyoshi Yoshida
  rescue ActiveRecord::RecordNotFound
281
    render_404
282
  end
283
284
  def find_foo
285 3 Mitsuyoshi Yoshida
    @foo = Foo.find_by_id(params[:id])
286 1 Mitsuyoshi Yoshida
    render_404	unless @foo
287
  end
288
end
289
</code></pre>
290
291
292
293
---
294
295
| [[プラグイン開発ガイド|^]] | [[GuideNewAction|<<]] | [[GuideConclusion|>>]] |