Create  Edit  Diff  FrontPage  Index  Search  Changes  History  Source  RSS  Note  wikifarm  Login

Rails' Wiki - AdjusterTutorial-0003 Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

{{toc}}
!モデリング再び
!!なぜ日程調整部分で躓いてしまったのか?
さて、案の定、日程調整画面でばったりと詰まってしまいました。なぜ詰まってしまったのか考えてみます。

結論から言うと、どのように日付、スケジュールを入力するのかの考察、汲み上げが足りなかった、落とし込みきれていなかったのが原因だと思います。

モデリング時の内容でいえば、現状の流れを確認であげた"誰かがMLに次の開催予定日(土日祝日)と自分の予定を投稿"の項目。
そして、要件をあげてみようであげた、"複数の開催候補日に対して各ユーザが参加可否を登録できる"とした部分です。

この部分をもっとしっかり踏み込んで要件を固めていれば、ここで詰まってしまう事はなかったと思います。

具体的に画面イメージを見ながら考えてみましょう。

//{{attach_view("schedule_page.png",AdjusterTutorial-0002)}}
{{attach_view("schedule_page1.png",AdjusterTutorial-0002)}}

今までのモデリング、実装では、この画面に到達するまでの時点でMLユーザー、イベント名は作成しておく事ができます。しかし、候補日という日付はまだ作成されていませんね。

という事は、候補日と予定を入れなければいけないという状態になっている訳です。

しかし、この画面イメージのようなUIから候補日と予定を入れることが可能でしょうか?

筆者は無理だと思います。

この画面は実は、縦軸と横軸、この場合はユーザーと開催候補日が入っている状態、表が成立している状態になっていなければ、機能しないのです。

もし、この画面イメージでデータを入れようとするならば、入れることが出来るのは、各自の予定だけという事なのです。

これが、この画面で詰まってしまった原因です。
{{attach_view(schedule_interface.png)}}

!!実現方法を考察
では、どのようにすればよいのでしょうか?
実現させる方法を考えてみましょう。

1.別にスケジュール登録画面を用意し、各自に勝手に入れてもらう。今の画面イメージは一覧確認表示用とする


Create or edit form

{{attach_view(form-1.gif)}}

Read_only
||!>>>>勉強会第5回
||!||!10/13||!10/14||!10/20||!10/21
||!片平||○||○||○||○
||!藤岡||○||○||○||○
||!大久保||○||○||○||○
2.月一回などのペースでcronなりでバッチ処理を行い、ユーザー、開催候補日(土日祝日)を回し、○×の入ってないスケジュールデータを事前に作成してしまう

batch_example.rb

Do_nextmonth_weekend_and_holiday_extract

@dates = nextmonth_weekend_and_holiday

@users = User.find(:all)

for date in @dates
   for user in @users
     Schedule_create_do_somthing....
     .......
   end
end

3.開催候補日作成画面を用意し、事前に表を完成させるようにする。完成させた表に、各自が○×と予定を入れる

Create or edit form

{{attach_view(form-2.gif)}}

view&edit_form
||>>>>ミーティング第5回||
||!||!10/13||!10/14||!10/20||!10/21
||!片平||||||||
||!藤岡||||||||
||!大久保||||||||
||!>>>>ここに○×の予定を入れる↑

3つほど考えてみました。
それぞれのメリットデメリットをあげてみます。

1.別にスケジュール登録画面を用意、各自が勝手に入力
*メリット
**scaffoldで作成、list画面だけ各自の総和の表に変更で実装出来そう
*デメリット
**各自が勝手な日にちをいれたり、予定を入力しないと、表がぼこぼこになる
**自由に入力出来るようになっているので、validationをかける等の工夫が必要になる
2.バッチ処理で作成
*メリット
**事前に仕込みをすませておけば予定を入力する事に専念出来る
*デメリット
**急な日程の追加(平日を追加)など、イレギュラーな事態に弱い
3.開催候補日作成画面を用意し、事前に表を完成
*メリット
**開催候補日を作成、予定入力という素直な流れになる
*デメリット
**予定を入力させる為に仕掛けが必要(後述)

それぞれ一長一短がありますね。さて、どの方法をとるのが良いでしょうか?

このような場合、普通であれば顧客の要望、要件に添ったものを採用するかと思います。

[[最初のモデリング|AdjusterTutorial-0001]]を振り返ってみますと・・・、特になにも要望があがっていないですね。
そもそも、要望がのっていなかったと思いませんか?

こうしてみると、実は最初のモデリング時の情報では全然足りなかった、つまり詰まってしまうのは必然だったともいえるのです。

言い換えるならば、顧客へのヒアリングが全く足りなかった、要望の吸い上げが全然出来ていなかったという状態になっていたという訳です。

!!後付で要望をでっち上げてみる

このまま全然顧客からの要望がわからない状態で進むのは、後々機能を実装していく上でも色々と困りそうですね。

というわけで、日程調整アプリ依頼者からの要望を後付でででっち上げてみました。

なお、イメージとしては、サークルに所属する依頼者から、知り合いのよしみで作ってもらえないかといわれたといった感じででっちあげてみました。

!!!具体的に作ってみた要望
<<<
今回の日程調整アプリのイメージは、サークルの部室にある黒板に書かれた毎月のミーティングの日程調整です。

黒板にみんなのスケジュールを書き込んで調整していたもののWeb化と思って下さい。

今の実際の流れというか要件は以下のような感じです。

黒板に書き込めるのは部室の鍵を持っていて部室に入れる人、つまりサークル参加者。

黒板にはメンバーであれば誰でも書き込めます。web的にはベーシック認証ぐらいのゆるーい感じです。

メンバーの誰かが、次の月のミーティング候補日を決めて、勝手に表を書きます。

後は各メンバーが適宜、自分のスケジュールを埋めていきます。

勝手に他人のスケジュールも書き込めますが、その場合は気づいた人や当人が直せばいいよねというレベルです。

webアプリ化するにあたっての要望は、基本的に今の流れが踏襲されていればOKです。

ただし、一つ要望があります。

各メンバがスケジュールを入れる場合は表にダイレクトに入力したいのです。

一件ごとのnew or editのフォームから、create or updateしてlistに戻るのではなく、表で直接入力、データを更新して再度表を表示という流れです。

後は、まだしっかり定まっていませんが、メール配信も将来的には行いたいのです。

候補日を決めたら入力を促すためにメンバに配信するのか、開催日をメンバに配信するのかは決まったら後からお伝えします
>>>
いかがでしょうか?勉強会からいきなりサークルに変わっちゃってますが、深く気にしないで下さい。

また、妙に誘導的で細かかったり、相変わらず曖昧な部分もありますが、今後はこの要望を基に進めていきます。

これで日程調整画面がようやく進めていけそうです。
依頼者の要望では、表に直接入力したいという事でしたので、2.バッチ処理か、3.開催候補日作成画面を用意、事前に表を完成が良さそうですね。

どちらが良いでしょうか?ここでは、開催候補日作成画面を用意で行きたいと思います。

筆者が"開催候補日作成画面を用意"を選んだ理由は、こちらの方が依頼者が今やっている流れに近いと思ったからです。
また、バッチ処理のイレギュラーな事態に弱いという点が気にかかったというのもあります。
!!画面(UI)イメージ、ページフローを再び-1-
開催候補日作成画面を用意するので、画面イメージ、ページフローの見直しが必要になりますね。
検討してみましょう。

開催候補日の作成は、普通に一件ずつ作成する一般的なCRUDが出来れば問題ない気がします。scaffoldで良さそうです。

しかし、本当にscaffoldで良いのでしょうか?scaffoldするという事は、モデルを作成するという事になります。

scaffoldで用意した開催候補日作成処理で、これまでのScheduleモデルを使用すると仮定すると、開催候補日以外にユーザー(user_id)と、ユーザーの予定(attend)が付加されているため、一つの開催候補日につき、全てのユーザー分、データを一件ずつ作成するという事になります。

ところが、開催候補日作成処理では、まだ各自の予定は入れないのです。

どうやら、画面イメージだけではなく、どのような仕掛けにするのか等についても検討が必要になりそうです。

ここは大事なところですので、後で検討することにして、とりあえずscaffoldのままで、他の画面イメージ、ページフローも考えてしまいましょう。

続けます。開催候補日ですが、作成時に'''どのイベントに属する開催候補日か'''という情報が付加されていなければなりませんよね。

そうでなければ、イベント毎の日程調整表を表示するときに、日付を特定することが出来ません。

従って、開催候補日作成画面へは、イベントの一覧からリンクする形をとる事にします。

また、前回の画面イメージ、ページフロー作成時に用意した、日程調整用イベントのリスト画面(Schedule/list)から日程調整画面へのリンクという流れも、イベントのリスト(Event/list)にリンクを追加すれば、わざわざ新たに作成する必要はないので削除します。

ところで、今までは日程調整(Schedule)といっしょくたになっていたのを、各自の日程調整と開催候補日作成に分離しました。両方をScheduleとするわけには行かないので、開催候補日作成をSchedule,日程調整をUserScheduleとしておきます。

図にすると以下のようになります。なお、最初のモデリング作業でも出てきましたが、画面イメージやモデルを考えるとき、筆者はまず手書きで書いてみるようにしています。

それと、例によって、メニュー画面が抜けておりますが、気にしないで下さい。

{{attach_anchor(adjuster_vc2.png)}}

!!データベースの正規化
さて、開催候補日作成をどのように実現するかですが、データモデルについても検討が必要そうです。

そのため、ここで突然ですが、データベースの正規化に触れておきます。

なぜ、正規化が出てきたかというと、実は最初のモデリング時に無意識で、正規化を行ってたんですね。

というわけで、その部分の確認の意味も込めて最初からやってみます。

なお、参考程度に、その時点で画面を用意するならこんなフォームかなというのも載せてあります。


!!!非正規型
まずは全ての要素を並べたexcelの様な単一のフラットなテーブル。
||>>>>>Schedule?||
||!ID||!名前||!メールアドレス||!イベント名||!イベント備考||!開催候補日||!予定||
||1||片平||kat@ahi.ra||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||○||
||2||片平||kat@ahi.ra||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||○||
||3||片平||kat@ahi.ra||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○||
||4||片平||kat@ahi.ra||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||○||
||5||藤岡||fuj@io.ka||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||○||
||6||藤岡||fuj@io.ka||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||×||
||7||藤岡||fuj@io.ka||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○||
||8||藤岡||fuj@io.ka||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||○||
||9||大久保||oo@ku.bo||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||×
||10||大久保||oo@ku.bo||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||○
||11||大久保||oo@ku.bo||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○
||12||大久保||oo@ku.bo||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||△

{{attach_view(form-5.gif)}}

この状態のデータを基に作成するとしたら、上記のように一々自分の名前いれて、イベント名いれて・・・と、かなり面倒くさいですよね。

ですので、普通は正規化を行い、重複をのぞいていきます。
!!!ユーザーを分割
まず、一々自分の名前とメールアドレスを入れる必要ないですよね?一回ユーザ登録すれば、省かれるようにしたい。ユーザーの情報を別テーブルに分割します。
||>>User||
||!ID||!名前||!メールアドレス
||1||片平||kat@ahi.ra
||2||藤岡||fu@jio.ka
||3||大久保||oo@ku.bo

{{attach_view(form-3.gif)}}

||>>>>Schedule?||
||!ID||!user_id||!イベント名||!イベント備考||!開催候補日||!予定||
||1||1||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||○||
||2||1||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||○||
||3||1||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○||
||4||1||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||○||
||5||2||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||○||
||6||2||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||×||
||7||2||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○||
||8||2||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||○||
||9||3||ミーティング第5回||ランチョンミーティングするからねー||2007-10-13||×
||10||3||ミーティング第5回||ランチョンミーティングするからねー||2007-10-14||○
||11||3||ミーティング第5回||ランチョンミーティングするからねー||2007-10-20||○
||12||3||ミーティング第5回||ランチョンミーティングするからねー||2007-10-21||△

{{attach_view(form-6.gif)}}

!!!イベントを分割
ユーザー名は一々入力しなくても済むようになりましたが、イベント名、イベントの備考もユーザー名等と同様に毎回入れる必要はないですよね。これも一回登録してしまえば済むように正規化しましょう。

||>>User||
||!ID||!名前||!メールアドレス
||1||片平||kat@ahi.ra
||2||藤岡||fu@jio.ka
||3||大久保||oo@ku.bo

{{attach_view(form-3.gif)}}

||>>Event||
||!ID||!イベント名||!備考
||1||ミーティング第5回||ランチョンミーティングするからねー
||2||ミーティング第6回||今回は別会場になります
||3||ミーティング第7回||特になし

{{attach_view(form-4.gif)}}

||>>>>Schedule?||
||!ID||!user_id||!event_id||!開催候補日||!予定||
||1||1||1||2007-10-13||○||
||2||1||1||2007-10-14||○||
||3||1||1||2007-10-20||○||
||4||1||1||2007-10-21||○||
||5||2||1||2007-10-13||○||
||6||2||1||2007-10-14||×||
||7||2||1||2007-10-20||○||
||8||2||1||2007-10-21||○||
||9||3||1||2007-10-13||×
||10||3||1||2007-10-14||○
||11||3||1||2007-10-20||○
||12||3||1||2007-10-21||△

{{attach_view(form-1.gif)}}

ここまでが今時点のデータベースの状態ですね。日程調整を表からいれるのでなく、好き勝手に入れるのであれば、これでオッケーなんですが・・・。

!!開催候補日作成時の仕掛けを考える
さて、今時点のデータベースの状態まで正規化を行いました。では、仕掛けを考えていきます。

ここで、考えを進める為に一つ明示しておかなければ行けないことがあります。

それは、表からの入力を実現させる為には、どんなモデルの形になるにせよ、'''予定のデータが作成されていなければならない'''と、非常に難しいという事です。

出欠可否の予定は最後に入れるから、それまでは作成されなくて良いのでは?と思われた方もおられるかと思います。

以下の表を見て下さい。

||>>>>ミーティング第5回||
||!||!10/13||!10/14||!10/20||!10/21
||!片平||||||||
||!藤岡||||||||
||!大久保||||||||

予定のデータが作成されていなくても表に出来ると思われるかもしれません。開催候補日を作成すれば、確かに'''表示だけなら'''出来ます。

しかし、その場合、各テキストフィールドから入力された予定を、一意に特定させる、つまり候補日やユーザーに結びつけるのがかなり難しいと思いませんか?

でも、それじゃあ"開催候補日作成を用意"では無理という事になってしまいます。

確かに、'''ただ'''開催候補日を作成しただけでは、無理です。

そこで、前述の'''予定のデータが作成されていなければならない'''という事が必要になります。

開催候補日作成時になんらかの仕掛けが必要といっていたやつです。

ここまで長々引っ張りましたが、どうでしょう?どのような仕掛けが必要か見えたのではないでしょうか?

どのようにすればいいかは、画面(UI)イメージ、ページフローを再び-1-で実は出ていました。

それは、「一つの開催候補日につき、全てのユーザー分、データを一件ずつ作成するという事になります。」という部分です。
候補日やユーザーという情報に結びつけられた'''未定という予定が入ったデータ'''を必要分作成します。

え、そんな面倒くさい事するの?もしユーザーが10人いたら、一つの開催候補日につき10件も作業しなければいけなくなるじゃないかと思われたかもしれません。

その通りです。但し、その作成作業はアプリに任せてしまえばいいのです。

つまり、こうです。
開催候補日を作成します。その裏でアプリが、ユーザー分、ユーザーIDと、イベントID,作成されたばかりの開催候補日、その開催候補日の予定を'''予定はまだ未定、これから予定が入りますよというデータが入っている状態'''としてデータを作成します。

例えば開催候補日が2007-10-13で、ユーザーが3人であればこんな感じに作成します。
||!user_id||!event_id||開催候補日||予定
||1||1||2007-10-13||
||2||1||2007-10-13||
||3||1||2007-10-13||

ちなみに、ユーザーを新規に追加したときにも、同様に既にある開催候補日分、ユーザーID,イベントID,開催候補日、'''未定という状態になっている予定データ'''を作成しておかないと片手落ちになります。

||>>>>ミーティング第5回||
||!||!10/13||!10/14||!10/20||!10/21
||!片平||||||||
||!藤岡||||||||
||!大久保||||||||
||!宗形
||>>>>ユーザ追加時、作成しないとこのデータがない↑
画面イメージ、ページフローで、開催候補日作成の画面は、scaffoldでとしていましたが、この画面ではモデルを用意せず(scaffoldを使用せず)、単に候補日を入力して、候補日とイベントIDのパラメータをコントローラに渡し、Scheduleを作成する処理を記述する形でも良さそうです。

これで実現出来るでしょうか?
開催候補日側からの処理は問題なくいけそうです。

ユーザー新規追加時はどうでしょう?
イベントIDを付加するって難しくないでしょうか?

同じ開催候補日の他のメンバーの予定データから引っ張ってこればいけそうではあります。でもなんだか直感的じゃないというか、気持ち悪くないですか?

!!データモデルを見直してみる
ここまでで一応実装出来ると思います。ですが、なんだかすっきりしません。特にユーザー側からの処理は実装時にいけてない感じがします。

DRYじゃない感じ、モデルをしっかり表現しきれていない感じ、なにかが足りない感じがするのです。

開催候補日作成の画面で、イベントの一覧画面より、Evetのidを受け渡されている事になるのですが、今のままであれば、ユーザーだけでなく、イベントも一々埋め込む形になります。

そして、ユーザー追加側の処理でのイベントIDが取りづらい点。

!!!そして再び正規化
正規化の一番最後のScheduleテーブルをよーく見て下さい。開催候補日が重複していますよね。開催候補日はEventに従属する・・・。ここ切り出せないでしょうか?

以下のようになるべきなのではないでしょうか?

||>>User||
||!ID||!名前||!メールアドレス
||1||片平||kat@ahi.ra
||2||藤岡||fu@jio.ka
||3||大久保||oo@ku.bo

||>>Event||
||!ID||!イベント名||!備考
||1||ミーティング第5回||ランチョンミーティングするからねー
||2||ミーティング第6回||今回は別会場になります
||3||ミーティング第7回||特になし

||>>>Schedule||
||!ID||!event_id||!開催候補日
||1||1||2007-10-13||
||2||1||2007-10-14||
||3||1||2007-10-20||
||4||1||2007-10-21||

||>>>>UserSchedule||
||!id||!user_id||!schedule_id||!予定||
||1||1||1||○||
||2||1||2||○||
||3||1||3||○||
||4||1||4||○||
||5||2||1||○||
||6||2||2||×||
||7||2||3||○||
||8||2||4||○
||9||3||1||×
||10||3||2||○
||11||3||3||○
||12||3||4||△

今までScheduleとしていたモデルですが、実は、開催候補日というモデル(Schedule)と、各自の予定というモデル(UserSchedule)に分けられるのではないでしょうか?

このようにすると、ユーザー追加時に予定を作成する処理で、開催候補日(Schedule)を付加すれば、その親であるEventのIDを辿る事が出来るので、一々Eventを与える必要はなくなります。

また、開催候補日のモデルを明示的に作成していますので、先の画面イメージ、ページフローの通り、開催候補日作成は、Scheduleのscaffoldを使用でいけるようになります。

日程調整の画面もUserScheduleモデルの振る舞いで考えればよくなり、各モデル毎にコントローラ、モデル、viewが全て切り分けられてすっきりしたと思います。

!!!データモデルの関係を見直す
さて、ではモデルの関係を再度見直してみましょう。

と、言っても再正規化の時点で、もう形は見えているのですが。

はじめにモデルの関係を考えたときは、こうでした。
Userに対して、10月13日、14日といったように複数のScheduleが存在している為、UserとScheduleの関係は1対多の関係と考えました。
また、Rails東北勉強会@第5回というEventからスケジュールを見たときもやはり、1対多の関係であるように見えます。
したがって下記のようにリレーションシップを設定してみました。

User 1 ----- n Schedule n ----- 1 Event

今回、新たにUserScheduleというモデルが出来ましたが、このモデルはUserとScheduleで一対多となっていた間に入ります。UserとScheduleとの関係ですが、例で考えていきます。

10月21日というscheduleモデルから見たとき、一つの日付に片平の○、藤岡さんの○、大久保さんの○という状態(UserSchedule)がぶらさがっています。

一方、片平というUserモデルからみると、10月13日、10月14日、10月20日、10月21日といったそれぞれの候補日に13日○、14日○、20日○、21日○という状態(UserSchedule)がぶらさがっています。

{{attach_view(schedule_page2.png)}}//{{attach_view(schedule_page2.png)}}
{{attach_view(schedule_page2_1.png)}}


このように考えると、候補日(Schedule)とユーザー(User)の間に各ユーザの予定(UserSchedule)という中間テーブルが入った、多対多の
つながり(has_many :through)の関係と考えるのが自然かと思います。

つまり以下のような関係となります
User 1 ----- n UserSchedule n----- 1 Schedule n ----- 1 Event

!!改訂版ER図
データモデルのまとめです。再度ER図にしてみます。

{{attach_view("adjuster_model_rev2.png",RailsMeetingTohoku-0004}}

!!ActiveRecordのコールバック
さて、モデルも固まり、開催候補日(ユーザー)作成時に日程調整データを作成するという仕掛けも決まりました。

この、日程調整データを作成する仕掛けですが、Railsには、ActiveRecordに用意されているcallbackというちょうどおあつらえ向きの機能がありますので、これを利用しましょう。

簡単に説明すると、ActiveRecordのcreate,update,save,validation等の各状態前後にフックをかけて何某かの処理をさせる機能です。

今回は、UserとScheduleのデータ作成の後(after_create)にフックをかけることにします。
!!画面(UI)イメージ、ページフローを再び-2-
開催候補日作成の仕掛けを反映した画面イメージ、ページフローです。

{{attach_anchor("adjuster_vc.png",RailsMeetingTohoku-0004)}}

おっと、肝心の日程調整画面の入力をどうするかを忘れていました。表から直接入力ですので、方法としては2通りあるかと思います。
#表全体をフォームとして、全てのデータを更新、リダイレクト。
#一件ごとにデータを更新、表へ
そんなにデータ量は多くないので、1でも構わないかと思いますが、ここでは、2つめを採用することにしましょう。

さらに、せっかくなので、InPlaceEditorを使ってみようと思います。
!!ER図、画面、ページフローをまとめたもの
上記までを全てまとめたのが以下の図です。

{{attach_anchor(adjuster_vc3.png)}}

これでようやく実装にかかれそうです。さっそく取りかかりましょう。

||[[目次|AdjusterTutorial]]||[[前ページへ|AdjusterTutorial-0002]]||[[次ページへ|AdjusterTutorial-0004]]||