Search code examples
scalaplayframeworkreactivemongoplay-reactivemongo

Instantiate a val from a given @inject class


How do I instantiate an object from this class? I have another class where I would like to use the MongoUtils class as defined below. This class is from reactivemongo

package controllers

import javax.inject.Inject

import scala.concurrent.Future

import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._

// Reactive Mongo imports
import reactivemongo.api.Cursor

import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}

// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._

/*
 * Example using ReactiveMongo + Play JSON library.
 *
 * There are two approaches demonstrated in this controller:
 * - using JsObjects directly
 * - using case classes that can be turned into JSON using Reads and Writes.
 *
 * This controller uses JsObjects directly.
 *
 * Instead of using the default Collection implementation (which interacts with
 * BSON structures + BSONReader/BSONWriter), we use a specialized
 * implementation that works with JsObject + Reads/Writes.
 *
 * Of course, you can still use the default Collection implementation
 * (BSONCollection.) See ReactiveMongo examples to learn how to use it.
 */
class MongoUtils @Inject() (val reactiveMongoApi: ReactiveMongoApi)
  extends Controller with MongoController with ReactiveMongoComponents {

  /*
   * Get a JSONCollection (a Collection implementation that is designed to work
   * with JsObject, Reads and Writes.)
   * Note that the `collection` is not a `val`, but a `def`. We do _not_ store
   * the collection reference to avoid potential problems in development with
   * Play hot-reloading.
   */
  def collection: JSONCollection = db.collection[JSONCollection]("persons")

  def index = Action { Ok("works") }

  def create(name: String, age: Int) = Action.async {
    val json = Json.obj(
      "name" -> name,
      "age" -> age,
      "created" -> new java.util.Date().getTime())

    collection.insert(json).map(lastError =>
      Ok("Mongo LastError: %s".format(lastError)))
  }

  def createFromJson = Action.async(parse.json) { request =>
    import play.api.libs.json.Reads._
    /*
     * request.body is a JsValue.
     * There is an implicit Writes that turns this JsValue as a JsObject,
     * so you can call insert() with this JsValue.
     * (insert() takes a JsObject as parameter, or anything that can be
     * turned into a JsObject using a Writes.)
     */
    val transformer: Reads[JsObject] =
      Reads.jsPickBranch[JsString](__ \ "firstName") and
        Reads.jsPickBranch[JsString](__ \ "lastName") and
        Reads.jsPickBranch[JsNumber](__ \ "age") reduce

    request.body.transform(transformer).map { result =>
      collection.insert(result).map { lastError =>
        Logger.debug(s"Successfully inserted with LastError: $lastError")
        Created
      }
    }.getOrElse(Future.successful(BadRequest("invalid json")))
  }

  def findByName(name: String) = Action.async {
    // let's do our query
    val cursor: Cursor[JsObject] = collection.
      // find all people with name `name`
      find(Json.obj("name" -> name)).
      // sort them by creation date
      sort(Json.obj("created" -> -1)).
      // perform the query and get a cursor of JsObject
      cursor[JsObject]()

    // gather all the JsObjects in a list
    val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()

    // transform the list into a JsArray
    val futurePersonsJsonArray: Future[JsArray] =
      futurePersonsList.map { persons => Json.arr(persons) }

    // everything's ok! Let's reply with the array
    futurePersonsJsonArray.map { persons =>
      Ok(persons)
    }
  }
}

So far I have tried val mongoutil = new MongoUtils(reactiveMongoApi = play.modules.reactivemongo) but apparently it does not work.


Solution

  • You can use Play’s dependency injection mechanism to resolve instance of ReactiveMongoApi which is the interface to MongoDB. The steps could be:

    Add the lines below to application.conf

    play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
    play.modules.enabled += "modules.CommonModule"
    

    modules.CommonModule is the place you define your bindings, so that Guice, a DI framework integrated with Play 2.4, knows which implementation to be used for a particular interface. For example,

    package modules
    
    import com.google.inject.AbstractModule
    import dao.impl.UserDaoMongo
    import dao.UserDao
    import play.api.{Configuration, Environment}
    import services.{UserService, UserServiceImpl}
    case class CommonModule(environment: Environment, configuration: Configuration) extends AbstractModule {
    
      def configure() {
        bindDAOs()
        bindServices()
      }
    
      private def bindDAOs(): Unit = {
        bind(classOf[UserDao]).to(classOf[UserDaoMongo])
      }
    
      def bindServices(): Unit = {
        bind(classOf[UserService]).to(classOf[UserServiceImpl])
      }
    }
    

    In addition, it is a good practice to make controllers singleton. For example:

    import javax.inject.{Singleton, Inject}
    
    @Singleton    
    class UserController @Inject()(userService: UserService) extends Controller {
    

    Please read this to know how to use Reactivemongo with Play framework. On the other hand, this is a complete example: https://github.com/luongbalinh/play-mongo