職場実習17日目

引き続きGrails作業

前日の追記

日付をフォーマット指定で表示する方法について、社員の芳村さん(Grails/Groovyが得意な人)からアドバイスがあったので、ここに記しておく。
g:formatタグも必要ないらしい。次の1行で記述できる。

${userInstance?.since.format("yyyy年M月d日(E)")}
作りたいものを(MVCモデルを使って)1ページで描く

芳村さんから、まずは作りたいものを1ページで描くよう指示があったので、自分なりに考え、SS(作品)投稿サイトをMVCモデルに基づいてノートに描いてみる。
簡単に説明するとこんな感じになる。

  • M(モデル)
    • ドメイン:作品データ
      • タイトル
      • 作者名
      • 内容
      • 作成日
      • 更新日
  • V(ビュー)
    • 作品リスト画面(list.gsp)
      • 作品リスト
      • 新規登録ボタン(Create)
      • 作品選択ボタン(Show)
    • 作品詳細画面(show.gsp)
      • 作品内容表示テーブル
      • 編集ボタン(Edit)
      • 削除ボタン(Delete)
    • 編集画面(edit.gsp)
      • 作品内容変更フォーム
      • 変更ボタン(Update)
      • 削除ボタン(Delete)
    • 新規登録画面(create.gsp)
      • 作品内容入力フォーム
      • 登録ボタン(Create)
  • C(コントローラー)
    • 作品リスト取得
    • 作品データ登録(Create)
    • 作品データ取得(Read)
    • 作品データ更新(Update)
    • 作品データ削除(Delete)
Grailsを使ったアプリ設計の心得について教えてもらう

社員の山本さんから、Grailsアプリの作り方を色々教えてもらった。
山本さんのブログ:leftovers...
Grailsアプリの制作過程を記録したスライド:G* on GAE/J 挑戦編

まずはモデリングから

作品クラス(ssbbs.Story)の定義

package ssbbs

class Story {
    static mapping = {
        id generator:'uuid.hex', 
        params:[type:'string']
    }
    String id  // ID
    String title  // タイトル
    String author  // 作者名
    String text  // 本文
    Date dateCreated  // 作成日
    Date lastUpdated  // 最終更新日
    
    static constraints = {
        title (nullable:false)
        author (nullable:false)
        text (nullable:false, maxSize:4000)
        dateCreated (display:false)
        lastUpdated (display:false)
    }
}

ID生成部分と、作成日・更新日の非表示設定は、山本氏のソースをそのまま使っている。
これで、コントローラーとビューを生成したら、すぐにCRUDが揃ったデータベースの出来上がり。
ちなみに、ドメイン名を指定する時は、ssbbs.storyではなくssbbs.Storyと書かないと、grailsドメインを見付けてくれず、コントローラーとビューが生成されないので注意。

リレーションを作る

次に、作者クラス(ssbbs.Author)を作り、作品クラスとの関係を定義する。

package ssbbs

class Author {
    static mapping = {
        id generator:'uuid.hex', 
        params:[type:'string']
    }
    String id  // ID
    String name
    String spell
    String loginId
    String loginKey
    String gender
    String email
    Date birthDay
    String address
    String telNo
    String celNo
    String hpUrl
    String comment
    Date dateCreated  // 登録日
    Date lastUpdated  // 更新日
    
    static hasMany = [stories : Story]
    
    static constraints = {
        name (sizeMax:20, nullable:false)
        spell (sizeMax:40, nullable:false)
        loginId (size:8..20, nullable:false, unique:true)
        loginKey (size:8..20, nullable:false)
        gender (inList:["男性", "女性"], nullable:true)
        email (email:true, nullable:false)
        address (maxSize:40, nullable:true)
        telNo (matches:"\\d{2,4}-\\d{2,4}-\\d{4}", nullable:true)
        celNo (matches:"\\d{2,4}-\\d{2,4}-\\d{4}", nullable:true)
        hpUrl (url:true, nullable:true)
        comment (maxSize:400, nullable:true)
        dateCreated (display:false)
        lastUpdated (display:false)
    }
}
package ssbbs

class Story {
    static mapping = {
        id generator:'uuid.hex', 
        params:[type:'string']
    }
    String id  // ID
    String title  // タイトル
    static belongsTo = [author:Author]  // 作者
    String text  // 本文
    Date dateCreated  // 作成日
    Date lastUpdated  // 最終更新日
    
    static constraints = {
        title (nullable:false)
        author (nullable:false)
        text (nullable:false, maxSize:4000)
        dateCreated (display:false)
        lastUpdated (display:false)
    }
}

作者と作品の関係は、1:mの従属関係になる。ドメインを定義したら、それぞれコントローラーとビューを生成し直せば、作品データ入力時に作者名をリストから選べるようになり、作品画面から作者画面へリンクが張られ、作者画面には作品リストも表示され、それぞれの作品へリンクが張られる。とても便利。

グループドメインを考える

作品をグループに分ける際のシリーズ、ジャンルなどをインスタンスとして扱うグループドメインを考えてみた。

  • ドメイン:グループ
    • ID
    • タイトル
    • 作者
    • 親グループ(制約:自分より階層が上)
    • 子グループ
    • 階層
    • 作成日
    • 更新日
  • ドメイン:階層
    • ID
    • 名前
    • 階層(序列)
    • グループ(自分を階層としている)

ソース化するとこうなる。

package ssbbs

class Group {
    static belongsTo = Group, Author
    static mapping = {
        id generator:'uuid.hex', 
        params:[type:'string']
    }
    String id  // ID
    String title  // タイトル
    Author author  // 作者
    Group superGroup  // 親グループ
    Hierarchy hierarchy  // 階層
    Date dateCreated  // 作成日
    Date lastUpdated  // 最終更新日
    static hasMany = [subGroups:Group]
    
    static constraints = {
        title (nullable:false)
        author (nullable:false)
        dateCreated (display:false)
        lastUpdated (display:false)
    }
}
package ssbbs

class Hierarchy {
    String name
    Integer rank
    static hasMany = [groups:Group]
    static constraints = {
        name (nullable:false, blank:false)
        rank (nullable:false)
    }
}

また、作品がグループに属する事を考え、子グループをシンプルに定義するため、作品をグループの派生クラスとして定義し直してみる。

  • ドメイン:作品(グループのサブクラス)
    • 本文(制約:非NULL、4000字以内)
    • 階層(制約:非表示)

ソース化するとこんな感じ。

package ssbbs

class Story extends Group {
    String text  // 本文
    
    static constraints = {
        text (nullable:false, maxSize:4000)
        hierarchy (display:false)
    }
}

ただ、グループは再帰的に他のグループを包含し、作品がサブクラス化されてしまうため、まだ扱い慣れていないGrails上での動作は保障できない。
よって、これらを実装するのは後回しとし、当面は作品と作者のみで最小限の実装を行う事にする。