« RailsでWikiクローンを作る | メイン | Wiki新設 »

2005年08月29日

RailsでWikiクローンを作る 4: ページ新規作成画面の実装

入力フォームの生成
ジェネレータで生成された雛形を元に、Minki の個々の機能の実装を始めます。 まずは「ページの新規作成」です。
「ページの新規作成」のアクションは、wikiコントローラに new という名前で作ることにします。 new のやるべきことは、新規ページ名の入力を求めるフォームを表示することです。 表示はビューの担当ですので、new アクションは今のところ何もしないままで放置します (ジェネレータが生成した空の定義のまま)。
一方、対応するビューのテンプレート app/views/wiki/new.rhtml は以下のように書き換えます。
<h1>新規作成</h1>

<%= start_form_tag :action => 'create' %>
  <%= text_field 'page', 'name' %>
  <%= submit_tag "Create" %>
<%= end_form_tag %>
そしてhttp://localhost:3000/wiki/newにアクセスします。

新規作成

Railsでは、フォームはヘルパーメソッドと呼ばれる Rails が用意しているメソッド群を使って作ります。直接、フォームの HTMLタグを書くことは避けるべきです。 ヘルパーメソッドを使うことで、パラメータの受け渡しが簡単に処理できたり、後で出てくるバリデーション機構が使いやすくなります。
フォームの開始と終了のタグは、それぞれ start_form_tag と end_form_tag で生成します。 start_form_tag の引数 :action => 'create' でフォームの submit ボタンが押されたときに実行されるアクションを指定しています(createアクションはまだ作っていません)。
テキストフィールドのタグは text_field で生成します。 Railsのフォーム関連の機能は、モデルの存在を前提としたものになっていて、 上記の例のtext_field 'page', 'name'は、page というモデルの name というカラムに格納されるべき値というような意味になります。 ちなみにこの部分は、通常時は
  <input id="page_name" name="page[name]" size="30" type="text" />
という HTML に展開されます。
submit ボタンは submit_tag で生成します。 submit が押されると、ユーザによってテキストフィールドに入力された値がパラメータとして start_form_tag の :action で指定したアクション(ここではcreate)に渡されます。

パラメータの受取り
では次に、その create アクションを作ります (create は、直接対応するビューを持たないアクションです)。 app/controllers/wiki_controller.rb の create メソッドを以下のように書き換えます。
  def create
    @page = Page.new(params[:page])
    if @page.save
      redirect_to :action => 'edit', :id => @page.name
    else
      render :action => 'new'
    end
  end
アクションが呼び出されるときのパラメータは、Rails では params という属性で参照することができるようになっています。
例えば、先ほどの new アクションのビューの中で、text_field 'page', 'name'と書いたテキストフィールドの入力値は、params[:page] の :name というキーで得ることができます。 つまり、params の :page というキーで示される値 params[:page](ハッシュ)が page モデル関連の入力パラメータになっていて、その値は {:name => 入力値} というハッシュです。 この形式は、そのまま Page.new の渡せる形になっていますので、結局 @page = Page.new(params[:page])と書くことによって、 new アクションで入力された値を元に、 モデルの新しいレコードを生成することになります。 この書き方は、入力フォームで入力された値に基づき、DBテーブルに新しい行を追加する場合の常套句です。

なお、この時点では新しいレコードが Ruby 上で生成されただけで、 まだ DB には一切書き込んでいません。 DBに書き込むには、save メソッドで行います。 save は書き込みに成功したか否かを真偽値で返すので、if @page.saveで書き込みと同時に成否を判断し、成功したら新規ページを編集するために edit アクション(後で実装)へリダイレクトし、失敗したら new の画面に戻すことになります。
Railsでは、他のアクションのURLへリダイレクトするには、redirect_to というヘルパーメソッドを使います。:action => 'edit' でアクション名を指定し、:id => @page.name でIDを指定します。
save でエラーが起きた場合は、もう一度新規入力画面に戻したいわけなので、render というヘルパーメソッドを使って、'new' アクションのビューを表示します (create には、もともと対応するビューはありませんが、あったとしても render :action => アクション名という文があると、指定したアクション名のビューが使われることになります)。
バリデーション(入力値正当性検査)
Rails では、モデルを通じて DB に値を保存する前に、その値が本来とるべき値かどうかを検査する仕組みがあります。 その検査のことをバリデーションと言います。
例えば、ページの新規作成画面でページ名に何も入力しなかったら、はじくようにしなければなりませんが、その場合はモデルクラスの定義(app/models/page.rb)に以下のように書きます。

class Page < ActiveRecord::Base
  validates_presence_of :name
end
これで、Page の name 要素が空っぽな場合にエラー(Page#saveがfalseを返す)となります。
エラーの情報はビュー側で利用することができます。 app/views/wiki/new.rhtml を以下のように変更します。
<h1>新規作成</h1>

<%= error_messages_for 'page' %> 
<%= start_form_tag :action => 'create' %>
  <%= text_field 'page', 'name' %>
  <%= submit_tag "Create" %>
<%= end_form_tag %>
<%= error_messages_for 'page' %> という行を追加しています。 error_messages_for は引数にインスタンス変数名(ここでは@page)をしています。 これで、エラーがあった場合はそのエラーが表示されるようになります。
なお、error_messages_for が @page を必要とするため、newアクションでも @pageをビューに渡すようにしなければなりません。 よって、app/controllers/wiki_controller.rb の new メソッドを以下のように書き換えます。
  def new
    @page = Page.new
  end
これで、ページの新規作成画面で、ページ名を入力せずに Create ボタンを押すと(想定通り)以下のようなエラーになります。

Name can't be blank

それから、HTMLのソースをみるとわかりますが、エラー時に text_field の部分は以下のように div タグで囲まれるようになります(実際は一行)。
<div class="fieldWithErrors">
<input id="page_name" name="page[name]" size="30" type="text" value="" />
</div>
スタイルシートで fieldWithErrors というクラスを適切に設定してやれば、エラーが起こった入力フォームを目立たせることができます。 例えば scaffold では背景を真っ赤にするスタイルシートが生成されます。
layout
今後、ビューのテンプレートをいくつか作っていくわけですが、ページのヘッダ、フッタ、サイドバーなど、多くのビューで共通する部分があります。 これをそれぞれのテンプレートに重複して記述していては DRY 精神に反するので、その共通部分をまとめて管理するための layout という仕組みがあります。
layoutの仕組みを利用するには、決まった場所にレイアウト用テンプレートファイルを置くだけです。 決まった場所とは、コントローラの名前から、規約によって自動的に決められる場所で、 wiki コントローラのレイアウト用ファイルの置き場は app/views/layouts/wiki.rhtml です。 ここでは、以下のような内容の wiki.rhtml を作ります。
<html>
<head>
  <title>Minki: <%= @title %></title>
  <%= stylesheet_link_tag '/theme/hiki/hiki.css' %>
</head>
<body>
<a name="top"></a>
<div class="main">

<h1 class="header"><%= @title %></h1>
<div class="day">
<%= @content_for_layout %>
</div>

</div>

<div class="sidebar">
</div>

</body>
</html>
stylesheet_link_tag は、スタイルシートのタグを生成するヘルパーメソッドです。 ここではとりあえず決め打ちでパスを書いています。
インスタンス変数 @content_for_layout には各ビューのテンプレートで生成された内容が入っています。 よって <%= @content_for_layout %> の部分は各ビューの内容で置き換えられることになります。

一方、ビューのテンプレート app/views/wiki/new.rhtml も、layout の利用にあたってちょっとだけ手直しをします。
<% @title = '新規作成' %>
<div class="body">
<%= error_messages_for 'page' %> 
<%= start_form_tag :action => 'create' %>
  <%= text_field 'page', 'name' %>
  <%= submit_tag "Create" %>
<%= end_form_tag %>
</div>
layoutファイル wiki.rhtml 内で使用するインスタンス変数 @title の値をセットして、h1タグの生成を wiki.rhtml 側にまかせるようにし、あと tDiary のテーマを利用するために div タグを追加しました。
あとは、public ディレクトリに Hiki から theme ディレクトリをコピーしてきて配置すれば、以下のような画面になるはずです。

新規作成・テーマ有り

まだサイドメニューを作っていないのでその部分は空っぽですが、なかなかそれらしくなってきたと思います。

投稿者 tam : 2005年08月29日 09:30

トラックバック

このエントリーのトラックバックURL:
http://tam.qmix.org/mt3/mt-tb.cgi/11

コメント

コメントしてください




保存しますか?