John
John

Reputation: 99

How to combine class cases?

I am using play framework and slick, play framework uses a case map in the form validation but there are values that I don't need validated as they are not inputed by the user, e.g. the ID & Date which is done is provided on the backend.

By the end I would like to have a class case like this, to provide to Slick and use with my Table.

case class Order(id: Long, order: String, date: Date)

For Play's Form validation I would provide a seperate case class:

case Class inputableOrder(order: String)

Can I then create the Order class that will grab the variables from inputableOrder and added it the Order class?

case class Order(id: Long, date: Date) // <- some way to add the variable from inputableOrder?

I'm just trying to prevent repetition but I still need two different case classes (one for form validation and another for working with the database).

Is there a way to modify an existing case Class , remove a variable or modify a variable type?

Upvotes: 0

Views: 268

Answers (1)

Petr
Petr

Reputation: 63419

I think you have several options here:

  1. Make InputableOrder a part of Order:

    case class InputableOrder(order: String) // ...
    case class Order(input: InputableOrder, id: Long, date: Date) // ..
    

    This is probably the most idiomatic solution. But it can be inflexible if you later realize that InputableOrder needs something that shouldn't be in Order.

  2. Make Order a subclass of InputableOrder. In this case there is some code repetition when passing arguments to the superclass, and the superclass can't be a case class, so you have to declare it as a regular class and create an extractor yourself:

    class InputableOrder(val order: String) // ...
    object InputableOrder {
      def unapply(o: InputableOrder): Option[String] = Some(o.order);
      // if you have more than one constructor arguments, return
      // a tuple like Option[(String,String)] etc.
    }
    
    case class Order(override val order: String, id: Long, date: Date)
      extends InputableOrder(order) // ...
    

    Again, the same problems can arise as with the previous point.

  3. Make the classes distinct and create helper methods to convert between them. The choice depends on your design, but I find this solution to be most flexible:

    case class InputableOrder(order: String);
    case class Order(order: String, id: Long, date: java.util.Date) {
      // An additional constructor to use when converting from Inputable:
      def this(other: InputableOrder, id: Long, date: java.util.Date) =
        this(other.order, id, date);
      // Update this instance with `other`:
      def this(that: Order, other: InputableOrder) =
        this(other, that.id, that.date);
    
      def toInput = InputableOrder(order);
    }
    

    This way you can create an Order from an InputableOrder just by supplying the missing fields and vice versa. You need to write these helper methods/constructors once, but using them is then easy.

    You can also use implicit methods such as

    implicit def toInput(other: InputableOrder): Order = other.toInput;
    

    to make things even easier.

Upvotes: 4

Related Questions