Liftことはじめ その5 mapper OneToMany

前述の記事の覚書
LiftのORMでOneToManyを実装。
各trackの添付を複数指定可能にする。(必然性はないが、音楽ファイルとTab符とか)
One側のTrackクラスは、OneToManyをMixIn。属性にattachesを定義。
model/Track

package code.model

import net.liftweb.mapper._
import net.liftweb.util._
import net.liftweb.common._

object Track extends Track with LongKeyedMetaMapper[Track] {
  override def dbTableName = "tracks"
}

class Track extends LongKeyedMapper[Track] with IdPK with OneToMany[Long, Track]{
  def this(albumid: Long, seq: Long, tracktitle: String) = {
    this()
    this.albumid(albumid)
    this.seq(seq)
    this.tracktitle(tracktitle)
  }

  def getSingleton = Track

  object albumid extends MappedLong(this)
   
  object seq extends MappedLong(this)

  object tracktitle extends MappedString(this, 100) {
    override def validations =
      valMaxLen(100, "name length must be under 100 characters long ") _  ::
      valMinLen(1, "you have to input!!") _ ::
      super.validations
  }

  object attaches extends MappedOneToMany(Attach, Attach.track, OrderBy(Attach.id, Ascending))

}

Many側のAttachクラス。属性にtrackを定義。

package code.model

import net.liftweb.mapper._

object Attach extends Attach with LongKeyedMetaMapper[Attach] {
  override def dbTableName = "attaches"
}

class Attach extends LongKeyedMapper[Attach] with IdPK {
  def getSingleton = Attach

  def this(filename: String, mimetype: String, trackattach: Array[Byte]) = {
    this()
    this.filename(filename)
    this.mimetype(mimetype)
    this.trackattach(trackattach)
  }

  object filename extends MappedString(this, 100)
  object mimetype extends MappedString(this, 40)
  object trackattach extends MappedBinary(this)
  object track extends LongMappedMapper(this, Track)
}

利用は、以下のようなコード。

package code.snippet

import java.io._
import scala.xml.{NodeSeq, Text}
import net.liftweb.util._
import net.liftweb.common._
import Helpers._

import code.model._
import net.liftweb.mapper._
import net.liftweb.http._
import S._
import SHtml._
import net.liftweb.http.js.{JsCmd, JsCmds}

class TrackView {
…
// Save
  def addProcess() {
    try {
      val track: Track = isAtachFileExist(upload) match {
        case true => {
          val attach: Attach = new Attach(getFileParamHolder(upload).fileName, getFileParamHolder(upload).mimeType, getFileParamHolder(upload).file)
          val track: Track = Track.create.albumid(albumid.toLong).seq(seq.toLong).tracktitle(tracktitle)
          track.attaches += attach
          track.save
          track
        }
        case false => new Track(albumid.toLong, seq.toLong, tracktitle)
      }
      track.validate match{
        case Nil => {
          track.save()
          S.notice("Added " + track.tracktitle)
          S.redirectTo("/track?albumid=" + albumid)
        }
        case x => {
          S.error("Validation Error!")
          S.redirectTo("/track?albumid=" + albumid)
        }
      }
    } catch {
      case e: java.lang.NumberFormatException => {
        S.error("SEQ must be the number!")
        S.redirectTo("/track?albumid=" + albumid)
      }
    }
  }
// Select
  private def doList(reDraw: () => JsCmd)(html: NodeSeq): NodeSeq = {
    val tracks:List[Track] = Track.findAll(By(Track.albumid, getAlbumId().toLong), OrderBy(Track.seq, Ascending))
    bind("track", html, "albumid" -> <input type="text" name="albumid" class="column span-10"/>)
    tracks.flatMap(trk => {
      trk.attaches.flatMap(atc => {
…
}

ソースコードこちら