プロジェクト

全般

プロフィール

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