jueves, 20 de julio de 2017

CRUD con Play Framework + Scala + PostgreSQL

¿Qué es Scala?

Scala es un lenguaje de programación multi-paradigma diseñado para expresar patrones comunes de programación en forma concisa, elegante y con tipos seguros. Integra sutilmente características de lenguajes funcionales y orientados a objetos. La implementación actual corre en la máquina virtual de Java y es compatible con las aplicaciones Java existentes.

más información en:
https://www.scala-lang.org/

¿Qué es Play Framework?

Play es un framework de aplicación web de código abierto, escrito en Scala y también utilizable desde otros lenguajes de programación compilados a Bytecode, p. Java (Play incluye una API de Java en versiones más recientes), que sigue el patrón de arquitectura model-view-controller (MVC). Su objetivo es optimizar la productividad del desarrollador mediante el uso de convenciones sobre la configuración, la recarga de código en caliente y la visualización de errores en el navegador

Scala y Play son una combinación perfecta para desarrollar aplicaciones que requieran de mucha demanda o millones de transacciones o peticiones por segundo, su diseño stateless (Sin estado) permite que Play Framework con Scala sean los elegidos para ambientes Cluster y que requieran de alto performance gracias a que también permiten operaciones asíncronas en el lado del servidor.

¿Qué necesitaremos para realizar nuestro CRUD?


- IntelliJ IDEA Community Edition
- Una computadora con Linux, Windows, Mac OSX o cualquier SO compatible.
- Instalar SBT http://www.scala-sbt.org/0.13/docs/es/Installing-sbt-on-Mac.html
- Necesitaras utilizar alguna plantilla, te recomiendo la Starter https://github.com/playframework/play-scala-starter-example/tree/2.6.x

La estructura de directorios final de nuestro proyecto será la siguiente:


Comenzamos

El primer paso será configurar nuestro proyecto es decir indicar las rutas y acceso a bases de datos. Esto lo realizaremos en los archivos conf/application.conf, conf/routes y build.sbt.


Configurando build.sbt

Para que nuestro proyecto se conecte apropiadamente utilizando JDBC necesitamos agregar las siguientes dependencias.

libraryDependencies += jdbc
libraryDependencies += "org.postgresql" % "postgresql" % "9.4-1206-jdbc42"

Ej.

Configurando Application.conf

El application.conf es un archivo muy útil ya que permite definir ciertas configuraciones como la conexión a base de datos de nuestro proyecto.
Ej.

Configurando routes.

El archivo routes será el que nos permitirá definir las rutas de acceso a nuestros controladores, los importantes en este proyecto son:

 style="background-color: white; font-family: Menlo; font-size: 9pt;"># Home page
GET     /                       controllers.TaskController.list
# Tasks
GET     /tasks                  controllers.TaskController.list
POST /newTask                   controllers.TaskController.newTask
GET /addTaskView                   controllers.TaskController.addTaskView
GET /updateTaskView/:id/:label     controllers.TaskController.updateTaskView(id: Long, label: String)
POST    /deleteTask/:id      controllers.TaskController.deleteTask(id:Long)
POST    /tasks/update/:id/:label       controllers.TaskController.updateTask(id: Long, label: String)

Las rutas anteriores contienen las operaciones básicas de alta, modificación y baja de datos en 
la tabla "task" que será objeto de uso en nuestro ejemplo.

Creando nuestro modelo.


Play Framework utiliza el patrón de diseño modelo - vista - controlador (MVC) por lo tanto
iniciaremos creando nuestro modelo.

Para ello en el directorio models crearemos una clase Scala llamada Task.scala.



package models

import play.api.db._
import anorm._
import anorm.SqlParser._
import javax.inject.{Inject, Singleton}
 case class TaskModel(id: Long, label: String)

@Singletonclass Task @Inject() (dBApi: DBApi){
  private val db = dBApi.database("default")
  val task = {
    get[Long]("id") ~
      get[String]("label") map {
      case id ~ label => TaskModel(id, label)
    }
  }

  def all(): List[TaskModel] = db.withConnection { implicit c =>
    SQL("select * from task").as(task *)
  }

  def create(label: String) {
    db.withConnection { implicit c =>
      SQL("insert into task (label) values ({label})").on(
        'label -> label
      ).executeUpdate()
    }
  }

  def update(id: Long, label: String){
    db.withConnection{ implicit c =>
      SQL("UPDATE task SET label = {label} WHERE id = {id}").on(
        'id -> id,
        'label -> label
      ).executeUpdate()
    }
  }

  def delete(id: Long) {
    db.withConnection { implicit c =>
      SQL("delete from task where id = {id}").on(
        'id -> id
      ).executeUpdate()
    }
  }
}

Creando nuestras vistas.


Luego procedemos a crear nuestras vistas en el directorio "views". Para ello utilizaremos
una plantilla basada en el archivo main.scala.html.

Archivo main.scala.html.

@*
 * This template is called from the `index` template. This template
 * handles the rendering of the page header and body tags. It takes
 * two arguments, a `String` for the title of the page and an `Html`
 * object to insert into the body of the page.
 *@
@(title: String)(content: Html)
<!DOCTYPE html>
<html lang="en">
    <head>
        @* Here's where we render the page title `String`. *@
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/bootstrap-theme.min.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/bootstrap.min.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
        <script src="@routes.Assets.versioned("javascripts/jquery-3.2.1.min.js")" type="text/javascript"></script>
        <script src="@routes.Assets.versioned("javascripts/bootstrap.min.js")" type="text/javascript"></script>
    </head>    <body>        @* And here's where we render the `Html` object containing
         * the page content. *@
        @content
    </body></html>


Archivo addTask.scala.html.


@(taskForm: Form[String])(implicit messages: Messages)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
    <h2>Agregar una nueva nota</h2>

    @form(routes.TaskController.newTask) {
    <div class="form-group">
        <label for="label">Nota</label>
        <input type="text" class="form-control" name="label" id="label" required="required" placeholder="Ingresar la nota">
    </div>
    <div class="form-group">
        <input type="submit" class="btn btn-default" value="Crear">
        <input type="reset" class="btn btn-default" value="Cancelar">
    </div>
    }
    <a href='@routes.TaskController.list'>Show list</a>
</div>
}

Archivo tasks.scala.html

@(tasks: List[TaskModel])(implicit messages: Messages, req: RequestHeader)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
<h1>@tasks.size tasks</h1>
<div class="add">
    <a href='@routes.TaskController.addTaskView'><button type="button" class="btn btn-primary">Agregar</button></a>
</div>
    <div class="table-responsive">
        <table class="table">
            <tr>
            <th>id</th><th>label</th><th>Update</th><th>Delete</th>
            </tr>
            @tasks.map { task =>
            <tr>
                <td>@task.id</td>
                <td>@task.label</td>
                <td><a href="@routes.TaskController.updateTaskView(task.id,task.label)"> <button type="button" class="btn btn-info">Actualizar</button></a></td>
                <td><button type="button" data-id="@task.id" id="btn-modal-delete" class="btn btn-danger" data-toggle="modal" data-target="#myModal">Eliminar</button></td>
            </tr>
            @*routes.TaskController.deleteTask(task.id)*@
            }

        </table>
    </div>
    <!-- Modal -->    <div id="myModal" class="modal fade" role="dialog">
        <div class="modal-dialog">

            <!-- Modal content-->            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                    <h4 class="modal-title">Modal Header</h4>
                </div>
                <div class="modal-body">
                    <p>Esta seguro en eliminar el registro?</p>
                    <input type="hidden" id="data-id"/>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-primary" id="btnEliminar" data-dismiss="modal">Aceptar</button>
                    <button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
                </div>
            </div>

        </div>
    </div>
</div>
<script type="text/javascript">

$(document).on('click','#btn-modal-delete', function(e){
    var taskId = $(this).data('id');
    console.log("taskId:" + taskId);
    $('.modal-body #data-id').val(taskId);
});

$('.modal-dialog .modal-footer').on('click','#btnEliminar', function(e) {
    var id = $('.modal-body #data-id').val();
    console.log(id);
    $.ajax({
        type: 'POST',
        url: '/deleteTask/'+ id,
        success: function(data) {
            document.open();
            document.write(data);
            document.close();
        }
    });
    return false;

});
</script>
}

Archivo updateTaskView.scala.html.

@(id: Long, label: String)(implicit messages: Messages)
@import helper._
@main("Welcome to Play") {
@*
* Get an `Html` object by calling the built-in Play welcome
* template and passing a `String` message.
*@
<div id="main">
    @form(routes.TaskController.updateTask(id, label)) {
    <input type="text" value="@id" name="id" class="form-control">
    <input type="text" value="@label" name="label" class="form-control">
    <input type="submit" class="btn btn-default" value="Update">
    }
</div>

}

Con esto ya tenemos listas nuestras vistas. Ahora procederemos a crear nuestros controladores

Creando nuestros controladores.

Para nuestro ejemplo sencillo solo crearemos un controlador llamado TaskController. En nuestro
controlador definiremos las diferentes acciones que realizaremos con nuestro modelo y vistas.
Los controladores por lo general deben ir en el directorio "controllers".
package controllers
import javax.inject.{Inject, Singleton}

import forms.TaskForm
import models.Task
import play.api.mvc.{Action, Controller}
import play.api.i18n.{I18nSupport, MessagesApi}

@Singletonclass TaskController @Inject()(val messagesApi: MessagesApi)(task: Task) extends Controller with I18nSupport {

  def list = Action {implicit request =>
    Ok(views.html.tasks(task.all()));
  }

  def addTaskView = Action {
    Ok(views.html.addTask(TaskForm.getForm));
  }

  def updateTaskView(id: Long, label: String) = Action {
    Ok(views.html.updateTaskView(id, label));
  }

  def newTask = Action { implicit request =>
    TaskForm.getForm.bindFromRequest.fold(
      errors => BadRequest(views.html.tasks(task.all())),
      label => {
        task.create(label)
        Redirect(routes.TaskController.list)
      }
    )
  }

  def updateTask(id: Long, label: String) = Action { implicit request =>
    TaskForm.getForm.bindFromRequest.fold(
      errors => BadRequest(views.html.tasks(task.all)),
      label => {
        task.update(id, label)
        Redirect(routes.TaskController.list)
      }
    )
  }

  def deleteTask(id: Long) = Action {request =>
    task.delete(id);
    Redirect(routes.TaskController.list)
  }

}

Listo. Ahora procederemos a crear nuestra base de datos en PGAdmin 4
para PostGreSQL. Crearemos una base de datos llamada "test" y a modo de ejemplo también
crearemos una tabla llamada "task" con dos campos llamados "id" y "label".



Luego nada más nos resta iniciar el servidor Netty, para lo cuál abrimos la consola o terminal
nos dirigimos a el path del proyecto y haremos uso de los comandos:

sbt : Para iniciar Scala Build Tools
clean: para limpiar el proyecto
compile: Para compilar nuestro proyecto
run: para iniciar el servidor de aplicación.














si probamos nuestro localhost puerto 9000 podremos notar nuestro CRUD funcionando a la perfección, cabe aclarar que se utilizaron otras herramientas como Bootstrap y JQuery Las cuales pienso explicar en otro post.

Espero les sea de utilidad y recuerden si desean un tema especifico solo escriban en los comentarios y yo procedo a hablar al respecto. También agradecerles a quienes colaboran vía PayPal y con los anuncios para que este blog siga vigente. Hasta el siguiente post.


3 comentarios:

  1. Como puedo pasar los datos de una fila a el modal

    ResponderEliminar
  2. Francisco buen post, puedes publicar el repositorio del proyecto o el github

    ResponderEliminar
  3. The King Casino Archives - Hertzaman
    The King Casino https://jancasino.com/review/merit-casino/ Archives, including news, articles, videos, address, wooricasinos.info gaming info, The King herzamanindir.com/ Casino & Hotel in Henderson, NV is one 토토 사이트 모음 of the newest hotels and motels 토토 사이트 on

    ResponderEliminar