その他のアクション(詳細表示、編集フォーム、更新、削除、プレビュー)¶
それでは残った 5 つのアクションを一気に片付けていきましょう。
- 詳細表示( show )
- 削除( destroy )
- 編集フォーム( edit )
- 更新( update )
- プレビュー( preview )
Foo オブジェクト(@foo)の取得¶
show, destroy, edit, update のアクションでは params[:id] に入っている id から対象となる Foo のオブジェクトを取得する必要があります。
3 つのメソッドで同じことを行います。こういうときには before_filter の出番です。ただし、 index や new, create, preview では必要ありません。 before_filter では :only オプションで実行する対象を指定することも、 :except オプションで実行しない対象を指定することも出来ます。サンプルでは 3 つのアクションだけですが、チケットのようにコピーや移動などのアクションを追加したくなるかもれません。そこで :except で index と new, create, preview を指定することにします。
find_project と同じように find_foo メソッドを追加します。
class FoosController < ApplicationController
unloadable
menu_item :standard
before_filter :find_project, :authorize
before_filter :find_foo, :except => [:index, :new, :create, :preview]
# :
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_foo
@foo = Foo.find_by_id(params[:id])
render_404 unless @foo
end
end
ActiveRecord::Base クラスでは ruby のメタプログラミングを利用して find_by_XXX という名前のクラスメソッドで XXX というメンバをキーとしてテーブルを検索することが出来ます。ここでは Foo.find_by_id で id の値が一致するオブジェクトを検索しています。
ただし、 Project.find と違って見つからなかった場合は nil を返すだけで例外は発生しません。
詳細表示( show アクション )¶
詳細表示のコントローラでは、 Foo オブジェクトを取得してビューを生成するだけなので、 show メソッドはスケルトン作成時のままで何も追加することはありません。
def show
end
ビュー(show.html.erb) で @foo オブジェクトの中身を表示することになります。
表示は Redmine で定義されている CSS クラスを使って、ちょっとチケット風にしています。
<h2><%=h l(:label_foo) %>#<%= @foo.id %></h2>
<div class="issue">
<div class="subject">
<h3><%= @foo.subject %></h3>
</div>
<% unless @foo.description.blank? %>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @foo.description %>
</div>
<% end %>
</div>
説明のパラメータは CSS クラスを wiki にし、 textilizable メソッドを使うことによって、wiki を解析した結果を出力しています。
また、詳細表示ページでは右上に 編集 と 削除 リンクをつけます。
<div class="contextual">
<%= link_to_if_authorized(l(:button_edit),
{:action => 'edit', :project_id => @project, :id => @foo.id},
:class => 'icon icon-edit') %>
<%= link_to_if_authorized(l(:button_delete),
{:action => 'destroy', :project_id => @project, :id => @foo.id},
:confirm => l(:text_are_you_sure), :method => :delete,
:class => 'icon icon-del') %>
</div>
削除用リンクには :confirm=>l(:text_are_you_sure) オプションをつけています。これによりリンク実行前に確認ダイアログを表示されるようになります。なお、そのままでは GET メソッドがよばれるため DELETE メソッド に変更しています。
この詳細表示ページの実行結果は次のようになります。
削除( destroy アクション )¶
destroy はコントローラのスケルトンで作成していませんでした。そこで中身だけでなく、定義からコントローラ(foos_controller.rb)に記述することになります。
def destroy
@foo.destroy
redirect_to project_foos_path(@project)
end
Foo オブジェクトの destroy メソッドを使ってデータベースからデータを削除しています。
その後、そのままだと destroy.html.erb のビューを表示しようとしますので、 redirect_to を使って一覧表示ページを表示しています。
編集( edit アクション )¶
編集のコントローラでは、 Foo オブジェクトを取得してビューを生成するだけなので、 edit メソッドはスケルトン作成時のままで何も追加することはありません。
def edit
end
ビュー(edit.html.erb) は次のようになります。
<h2><%=h l(:label_foo) %>#<%= @foo.id %></h2>
<%= labelled_form_for :foo, @foo,
:url => project_foo_path(@project),
:html => {:multipart => true, :id => 'foo-form'} do |f| %>
<%= render :partial => 'foos/form', :locals => {:form => f} %>
<%= f.submit l(:button_edit) %>
<%= preview_link( preview_project_foo_path(@project), 'foo-form' ) %>
<% end %>
<div id="preview" class="wiki"></div>
labelled_form_for の :url の指定は new では :id のパラメータを省略していましたが、 edit では :id のパラメータを指定する必要があります。
プレビュー用のリンクを追加していますが、後はラベルなどの表示を変えているだけで new とほぼ同じです。
更新( update アクション )¶
update はコントローラのスケルトンで作成していませんでした。そこで中身だけでなく、定義からコントローラ(foos_controller.rb)に記述することになります。
update アクションはフォームの [編集] ボタンから呼ばれます。
create アクションと同じような感じで作成します。
コントローラは次のようになります。
def update
@foo.attributes = params[:foo]
if @foo.save
flash[:notice] = l(:notice_successful_update)
redirect_to project_foo_path(@project, @foo.id)
end
rescue ActiveRecord::StaleObjectError
flash.now[:error] = l(:notice_locking_conflict)
end
create の場合と同様に save メソッドを使ってデータベースに保存しています。 create との違いは次の 3 点です。
- id 番号は同じにしなければならないので、 create の引数ではなく attributes でパラメータのみ変更
- 同時に編集している人がいるとエラーで例外が発生するので、その場合にはエラーメッセージを表示
- POST リクエストではなく PUT リクエスト
その後、そのままだと update.html.erb のビューを表示しようとしますので、 redirect_to を使って詳細表示ページを表示しています。
プレビュー( preview アクション )¶
preview もコントローラのスケルトンで作成していませんでした。同様に定義からコントローラ(foos_controller.rb)に記述します。
def preview
@text = params[:foo][:description]
render :partial => 'common/preview'
end
これが edit アクションの preview_link から呼ばれることになります。
ここでは、Redmine のヘルパーメソッドの preview 機能を呼び出してプレビュー表示を実現しています。
レイアウト¶
最後に html ページのタイトルをつけましょう。
他の文書やチケットを参考にタイトルの付け方を次のようにします。
ページ | タイトル |
---|---|
一覧表示、新規作成 | (プロジェクト名) - 標準 - Redmine |
詳細表示、編集 | (プロジェクト名) - (題名) - Redmine |
html のタイトルを付けるには html_title メソッドをビュー内で呼び出します。
このとき前後のプロジェクト名と Redmine の文字は Redmine が自動的に付けます。
例えば、 index.html.erb に次のコードを追加します。
<% html_title(l(:label_standard)) %>
このときプロジェクト名が "デモ" とするとタイトルは次のようになります。
デモ - 標準 - Redmine
それでは早速、他のビューにもこの文をコピペしてと ....
もう何がいいたいかお分かりですね。 繰り返しを避ける方法があります。
コントローラの before_filter のようにビューすべてにコードを追加したい場合には レイアウト というものを使用します。
レイアウト用の html.erb ファイルは app/views/layouts ディレクトリにおきます。
スケルトンでは layouts は作成されていませんので、 layouts ディレクトリを作って app/views/layouts/standard.html.erb ファイルを作成します。このときファイル名は何でもかまいません。
<% html_title((@foo && !@foo.subject.blank?) ? @foo.subject : l(:label_standard)) %>
<%= render :file => "layouts/base" %>
html_title の引数は決めたタイトルになるように条件で文字列を変えています。
最後の行でデフォルトのレイアウトを出力しています。
この layouts/base は Redmine の app/views/layouts/base.html.erb ファイルを指しています。このファイルはレイアウトを指定しない場合に使用されているものです。レイアウトに standard.html.erb を指定すると使用するレイアウトはそちらに置き換わってしまいます。 タイトルの指定だけの追加とするためには standard.html.erb 内で再度 base のレイアウトを呼び出す必要があります。
レイアウトの指定はコントローラのクラス定義内に次のコードを追加します。
layout 'standard'
これでどのアクションに対しても layouts/standard.html.erb の内容がビューのページに付けられてタイトルが変更されるようになります。
これでサンプルプラグインは完成です!!
最終的なコントローラのファイル(foos_controller.rb)は以下のようになります。
class FoosController < ApplicationController
unloadable
menu_item :standard
before_filter :find_project, :authorize
before_filter :find_foo, :except => [:index, :new, :create, :preview]
layout 'standard'
def index
@foos = Foo.find(:all, :conditions => ["project_id = #{@project.id} "])
end
def new
@foo = Foo.new()
end
def create
@foo = Foo.new(params[:foo])
@foo.project_id = @project.id
if @foo.save
flash[:notice] = l(:notice_successful_create)
redirect_to project_foo_path(@project, @foo.id)
end
end
def show
end
def update
@foo.attributes = params[:foo]
if @foo.save
flash[:notice] = l(:notice_successful_update)
redirect_to project_foo_path(@project, @foo.id)
end
rescue ActiveRecord::StaleObjectError
flash.now[:error] = l(:notice_locking_conflict)
end
def edit
end
def destroy
@foo.destroy
redirect_to project_foos_path(@project)
end
def preview
@text = params[:foo][:description]
render :partial => 'common/preview'
end
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
def find_foo
@foo = Foo.find_by_id(params[:id])
render_404 unless @foo
end
end
^ | << | >> |
NAITOH Jun さんがほぼ11年前に更新 · 5件の履歴