git clone git@github.com:Taack/myLibrary.git
Configuración
La solución y los esqueletos para esta tarea están disponibles para clonar en este repositorio:
cd en el proyecto (myLibrary) y copie la siguiente línea.git checkout part-2-skeleton
O bien, en IntelliJ puede seleccionar directamente la rama deseada. Seleccione en la esquina superior izquierda main/myLibrary/remote y verá todas las ramas. Seleccione una rama y haga «Checkout».
Introducción
Ya ha creado una biblioteca personal; añadamos algunas funcionalidades. Queremos añadir un sistema de préstamos. Después de esta parte, podrá tomar prestados libros de su propia biblioteca, aprobar la solicitud y devolver los libros, y también tendrá acceso a su historial de solicitudes de préstamo.
Hemos actualizado el código en myLibrary. Haga un pull de esta versión para tener acceso al nuevo código esqueleto.
Definir y actualizar las clases de dominio
Editar: app/library/src/main/groovy/myLibrary/domain/
Modo Pro: [TODO 1.1, 1.2 & 1.3]
MyLibraryBook y añada el siguiente método (TODO 1.1):int getNumberOfBooksBorrowable() {
return MyLibraryBookInstance.countByIsAvailableBAndIsActiveAndBook(true, true, this) //(1)
}
-
countByIsAvailableBAndIsActiveAndBookes un método de búsqueda dinámica (dynamic finder) en Grails/GORM que cuenta automáticamente el número de registros en una clase de dominio que coinciden con las condiciones dadas para los camposisAvailableB,isActiveybook. Para obtener más información, consulte la documentación de Grails (countBy).
MyLibraryBookInstance y añada las siguientes variables y la relación hasMany (TODO 1.2):List<MyLibraryBorrowed> borrowHistoryOfBook
Boolean isAvailableB = true
static hasMany = [borrowHistoryOfBook: MyLibraryBorrowed]
MyLibraryBorrowed (TODO 1.3):enum ApprovalStatus {
PENDING, APPROVED, REJECTED
}
@GrailsCompileStatic
@TaackFieldEnum
class MyLibraryBorrowed {
MyLibraryBookInstance bookInstance
User user
Date requestDate
Date approvalDate
Date returnDate
ApprovalStatus statusOfApproval= ApprovalStatus.PENDING
static constraints = {
approvalDate nullable: true
returnDate nullable: true
}
}
¡Ya ha implementado todas las clases necesarias para esta sección!
Definir los menús
Para esta sección, necesitamos crear dos menús nuevos. El usuario solicitará tomar prestada una bookInstance en el menú de libros. Las bookInstances que se hayan solicitado y aún no se hayan devuelto (ya sean solicitudes pendientes o solicitudes aprobadas) estarán en el menú de préstamos. Las bookInstances que hayan sido devueltas estarán en el menú de historial.
Comencemos añadiendo estos menús a buildMenu.
Modo Pro: Añada los menús en buildMenu. [TODO 2]
buildMenu:menu MyLibraryController.&listBooksBorrowed as MC
menu MyLibraryController.&listBooksCurrentlyBorrowed as MC
Definir el menú de préstamos
Tabla de préstamos
Queremos mostrar los libros prestados (aún no devueltos) en una tabla. Para ello necesitamos crear un UiTableSpecifier. Vaya a app/library/services/myLibrary/MyLibraryUiService en Author Menu.
Nota: las únicas diferencias entre los menús de préstamos e historial son returnDate y StatusOfApproval.
Por lo tanto, utilizaremos el mismo TableSpecifier para ambos menús; esta es una de las grandes ventajas de usar un archivo separado (MyLibraryUiService) para implementar los Specifiers: pueden reutilizarse en diferentes contextos con pequeñas modificaciones.
|
Note
|
Necesitará un indicador booleano para comprobar si un libro está prestado actualmente; lo llamaremos |
Modo Pro: implemente la tabla por su cuenta [TODOS 3.1].
Solución
buildBorrowedTable:MyLibraryBook book = new MyLibraryBook()
MyLibraryBorrowed borrowed = new MyLibraryBorrowed()
UiTableSpecifier buildUserBorrowsSpecifier = new UiTableSpecifier()
MyLibraryBookInstance bookInstance = new MyLibraryBookInstance()
buildUserBorrowsSpecifier.ui {
header {
sortableFieldHeader borrowed.bookInstance_,bookInstance.book_,book.title_
sortableFieldHeader borrowed.bookInstance_,bookInstance.book_,book.author_
if (isCurrently) {label borrowed.statusOfApproval_}
label borrowed.requestDate_
label borrowed.approvalDate_
column {label borrowed.user_}
}
TaackFilter.FilterBuilder filter = taackFilterService.getBuilder(MyLibraryBorrowed)
.setMaxNumberOfLine(10)
.setSortOrder(
TaackFilter.Order.ASC,
borrowed.bookInstance_,
bookInstance.book_,book.title_)
if(isCurrently) {filter.addFilter(new FilterExpression(null, Operator.EQ, borrowed.returnDate_))} //(1)
else {filter.addFilter(new FilterExpression(null, Operator.NE, borrowed.returnDate_))} //(1)
iterate(
filter.build()) { MyLibraryBorrowed borrowedIterator ->
rowColumn {
rowField borrowedIterator.bookInstance.book.title
}
rowField borrowedIterator.bookInstance.book.author.toString()
if(isCurrently) {rowField borrowedIterator.statusOfApproval_}
rowField borrowedIterator.requestDate_
rowField borrowedIterator.approvalDate_
rowColumn {rowField borrowedIterator.user.username_}
}
}
-
Esta es otra forma de crear un filtro si no tiene previsto reutilizarlo en otro lugar.
También queremos añadir un filtro para filtrar los préstamos. Vamos a implementar este filtro.
Modo Pro: implemente el filtro por su cuenta [TODO 3.2].
buildUserBorrowsFilter:UiFilterSpecifier buildUserBorrowsFilter(User user = null) {
MyLibraryBook book = new MyLibraryBook()
UiFilterSpecifier UserBorrowsFilterSpecifier = new UiFilterSpecifier()
MyLibraryBorrowed borrowed = new MyLibraryBorrowed()
MyLibraryBookInstance bookInstance = new MyLibraryBookInstance()
UserBorrowsFilterSpecifier.ui MyLibraryBorrowed, {
if (user) hiddenId(user.id)
section "Borrows Filter", {
filterField borrowed.bookInstance_,bookInstance.book_,book.title_
}
}
}
Su tabla ya está completa; solo necesitamos renderizarla en la página. Haremos esto en un método llamado listBooksCurrentlyBorrowed. Vaya a app/library/controller/myLibrary/MyLibraryController en Borrowed Menu.
Modo Pro: implemente listBooksCurrentlyBorrowed por su cuenta [TODO 3.3].
listBooksCurrentlyBorrowed:def listBooksCurrentlyBorrowed() {
UiTableSpecifier tableUserBorrowsSpecifier = myLibraryUiService.buildUserBorrowsTable(true)
UiFilterSpecifier filterUserBorrowsSpecifier = myLibraryUiService.buildUserBorrowsFilter()
taackUiService.show(new UiBlockSpecifier().ui {
tableFilter filterUserBorrowsSpecifier, tableUserBorrowsSpecifier
}, myLibraryUiService.buildMenu())
}
Ahora puede iniciar el servidor. La tabla de préstamos debería ser funcional, pero por el momento solo verá los encabezados de la tabla, ya que no hay préstamos en su base de datos. Así que procedamos a crear un formulario y guardar objetos en la base de datos. Ahora tenemos que implementar requestBookInstance, lo que requiere crear un UiFormSpecifier.
Volvamos a MyLibraryUiService.
Formulario de préstamo
Modo Pro: implemente buildRequestBookForm [TODO 3.4].
buildRequestBookForm:UiFormSpecifier buildRequestBookForm(MyLibraryBook book) {
User user = springSecurityService.currentUser as User
MyLibraryBorrowed borrowed = new MyLibraryBorrowed()
borrowed.user = user
book ?= new MyLibraryBook(params)
UiFormSpecifier requestBookFormSpecifier = new UiFormSpecifier()
requestBookFormSpecifier.ui borrowed, {
section "Request Book Form", {
hiddenField borrowed.user_ //(1)
field borrowed.requestDate_
ajaxField borrowed.bookInstance_, MyLibraryController.&selectBookInstanceOne as MC, book.id
}
formAction MyLibraryController.&saveBookForm as MC
}
}
-
Pasa parámetros en el formulario sin que el usuario los vea.
Volvamos a MyLibraryController. Renderizaremos este formulario en una ventana modal.
Modo Pro: implemente requestBookInstance [TODO 3.5]
requestBookInstance:def requestBookInstance(MyLibraryBook book){
UiFormSpecifier requestBookInstanceForm = myLibraryUiService.buildRequestBookForm(book)
taackUiService.show new UiBlockSpecifier().ui {
modal {
form requestBookInstanceForm
}
}
}
Ahora necesitamos guardar el registro de préstamo, así que implementemos saveBookForm.
Modo Pro: implemente saveBookForm [TODO 3.6].
@Transactional
def saveBookForm() {
MyLibraryBorrowed borrowed = taackSaveService.save(MyLibraryBorrowed)
borrowed.bookInstance?.isAvailableB = false
taackSaveService.redirectOrRenderErrors(borrowed)
}
Ahora necesitamos implementar selectBookInstanceOne, que utilizará buildInstanceBookTable; primero debemos modificar este Specifier.
Modo Pro: modifique buildInstanceBookTable [TODO 3.7].
column {
if (isOne) {
label "Select Book Instance"
} else {
label "Delete"
}
}
filter.addFilter(buildIsAvailableBookInstances(book))
rowColumn {
if(isOne) {
rowAction tr('default.serialNumber.label'), ActionIcon.SELECT * IconStyle.SCALE_DOWN, bookInstanceIterator.id, bookInstanceIterator.serialNumber.toString()
} else {
rowAction ActionIcon.DELETE * IconStyle.SCALE_DOWN, MyLibraryController.&deleteBookInstances as MC, bookInstanceIterator.id, [bookId:book.id]
}
}
Hemos utilizado el filtro buildIsAvailableBookInstances; vamos a implementarlo.
Modo Pro: Implemente buildIsAvailableBookInstances [TODO 3.8].
buildIsAvailableBookInstances:UiFilterSpecifier buildIsAvailableBookInstances(MyLibraryBook book) {
MyLibraryBookInstance bookInstance = new MyLibraryBookInstance()
UiFilterSpecifier bookInstanceFilterSpecifier = new UiFilterSpecifier()
bookInstanceFilterSpecifier.sec MyLibraryBookInstance, {
filterFieldExpressionBool new FilterExpression(true, Operator.EQ, bookInstance.isAvailableB_)
}
}
Modo Pro: Implemente selectBookInstanceOne [TODO 3.9].
selectBookInstanceOne:def selectBookInstanceOne(MyLibraryBook book) {
UiTableSpecifier bookInstanceTable = myLibraryUiService.buildInstanceBookTable(book, true)
taackUiService.show new UiBlockSpecifier().ui {
modal {
table bookInstanceTable
}
}
}
Añadamos ahora el botón para acceder al formulario de solicitud. También queremos saber cuántos libros están disponibles (se pueden tomar prestados); implementaremos esto en buildBookTable.
Modo Pro: Modifique buildBookTable [TODO 3.10]
label "Number of Available Book Instances"
label "Request Form"
rowField bookIterator.numberOfBooksBorrowable_
rowColumn {
rowAction ActionIcon.CREATE * IconStyle.SCALE_DOWN, MyLibraryController.&requestBookInstance as MC, bookIterator.id
}
Ya tiene una tabla completamente funcional donde podemos mostrar y crear solicitudes de préstamo. Inicie el servidor de nuevo; ahora debería poder hacer clic en el botón y rellenar el formulario.
Ahora queremos poder aprobar solicitudes y devolver las bookInstances prestadas. Implementemos los métodos approveBook y returnBook. Vayamos a MyLibraryUiService en el método buildUserBorrowsTable.
Aprobar solicitudes y devolver BookInstances
Vaya a myLibraryUiService en buildUserBorrowsTable.
Modo Pro: añada los botones [TODO 3.11].
if (isCurrently) column {label "Return Book"}
if (isCurrently) {
rowColumn {
if (borrowedIterator.statusOfApproval == ApprovalStatus.APPROVED) {
rowAction ActionIcon.DELETE * IconStyle.SCALE_DOWN, MyLibraryController.&returnBook as MC, borrowedIterator.id
}
}
}
if (isCurrently) {label "Approve Book"}
if (isCurrently) {
rowColumn {
rowAction ActionIcon.DELETE * IconStyle.SCALE_DOWN, MyLibraryController.&approveBook as MC, borrowedIterator.id
}
}
Implementemos ahora los formularios para approveBook y returnBook.
Modo Pro: implemente buildApproveBookTable [TODO 3.12]
buildApproveBookTable:UiFormSpecifier buildApproveBookTable(MyLibraryBorrowed borrowed) {
UiFormSpecifier approveBookSpecifier = new UiFormSpecifier()
approveBookSpecifier.ui borrowed, {
section "Approve Book Form", {
field borrowed.approvalDate_
field borrowed.statusOfApproval_
}
formAction MyLibraryController.&saveApprovalBookForm as MC
}
}
Modo Pro: implemente buildRequestReturnBookForm [TODO 3.13]
buildRequestReturnBookForm:UiFormSpecifier buildRequestReturnBookForm(MyLibraryBorrowed borrowed) {
UiFormSpecifier requestBookFormSpecifier = new UiFormSpecifier()
requestBookFormSpecifier.ui borrowed, {
section "Request Return Book Form", {
field borrowed.returnDate_
}
formAction MyLibraryController.&saveReturnBookForm as MC
}
}
Vuelva a MyLibraryController para implementar los métodos approveBook & returnBook.
Modo Pro: implemente los métodos [TODO 3.14 & 3.15]
approveBook:def approveBook(MyLibraryBorrowed borrowed) {
UiFormSpecifier approveBookSpecifier = myLibraryUiService.buildApproveBookTable(borrowed)
taackUiService.show(new UiBlockSpecifier().ui {
modal {
form approveBookSpecifier
}
})
}
returnBook:def returnBook(MyLibraryBorrowed borrowed) {
UiFormSpecifier requestReturnBookInstanceForm = myLibraryUiService.buildRequestReturnBookForm(borrowed)
taackUiService.show new UiBlockSpecifier().ui {
modal {
form requestReturnBookInstanceForm
}
}
}
Implementemos los métodos de guardado, saveApprovalBookForm & saveReturnBookForm.
Modo Pro: implemente los métodos [TODO 3.16 & 3.17]
saveApprovalBookForm: @Transactional
def saveApprovalBookForm() {
Calendar cal = Calendar.getInstance()
cal.set(999999, Calendar.DECEMBER, 31)
Date date = cal.time
MyLibraryBorrowed borrowed = taackSaveService.save(MyLibraryBorrowed)
if(borrowed.statusOfApproval == ApprovalStatus.REJECTED) {
borrowed.returnDate = date
}
taackSaveService.redirectOrRenderErrors(borrowed)
}
Nota: El calendario se establece en una fecha muy lejana en el futuro (año 999999) para indicar que el rechazo es permanente.
saveReturnBookForm:@Transactional
def saveReturnBookForm() {
MyLibraryBorrowed borrowed = taackSaveService.save(MyLibraryBorrowed)
borrowed.bookInstance?.isAvailableB = true
taackSaveService.redirectOrRenderErrors(borrowed)
}
Ahora debería poder aprobar y devolver libros. ¡Ejecute el servidor y pruébelo!
Queremos poder mostrar más información sobre la solicitud en una ventana modal.
Implementemos ahora el método showBorrowed. Primero necesitamos crear un enlace a este método y un UiShowSpecifier en myLibraryUiService.
Vayamos a MyLibraryUiService en buildUserBorrowsTable.
Modo Pro: añada los botones [TODO 3.18].
Añada la acción de mostrar. Reemplace el TODO 3.18:
rowAction ActionIcon.SHOW * IconStyle.SCALE_DOWN, MyLibraryController.&showBorrowed as MC, borrowedIterator.id
Modo Pro: implemente buildBorrowedShow [TODO 3.19]
buildBorrowedShow:UiShowSpecifier buildBorrowedShow(MyLibraryBorrowed borrowed) {
UiShowSpecifier borrowedShowSpecifier = new UiShowSpecifier()
borrowedShowSpecifier.ui(borrowed, {
fieldLabeled borrowed.bookInstance.book.title_
fieldLabeled borrowed.bookInstance.book.author_
fieldLabeled borrowed.user.username_
fieldLabeled borrowed.statusOfApproval_
fieldLabeled borrowed.requestDate_
fieldLabeled borrowed.approvalDate_
fieldLabeled borrowed.returnDate_
})
}
Renderizaremos buildBorrowedShow en una ventana modal usando showBorrowed.
Modo Pro: implemente showBorrowed [TODO 3.20]
showBorrowed:def showBorrowed(MyLibraryBorrowed borrowed) {
UiShowSpecifier showSpec = myLibraryUiService.buildBorrowedShow(borrowed)
taackUiService.show(new UiBlockSpecifier().ui {
modal {
show showSpec
}
})
}
¡Enhorabuena! Ya ha implementado todo lo necesario para solicitar, aprobar y devolver libros, así como para mostrar todas las solicitudes en curso. Inicie el servidor de nuevo y pruebe todas las nuevas implementaciones.
Definir el menú de historial
Ahora queremos mostrar los resultados anteriores en una tabla. Como se explicó anteriormente, esto es sencillo y rápido ya que tenemos nuestro constructor y hemos implementado isCurrently. Vayamos a MyLibraryController y rendericemos las solicitudes pasadas.
Modo Pro: implemente listBooksBorrowed [TODOS 4.1].
listBooksBorrowed:def listBooksBorrowed() {
UiTableSpecifier tableUserBorrowsSpecifier = myLibraryUiService.buildUserBorrowsTable()
UiFilterSpecifier filterUserBorrowsSpecifier = myLibraryUiService.buildUserBorrowsFilter()
taackUiService.show(new UiBlockSpecifier().ui {
tableFilter filterUserBorrowsSpecifier, tableUserBorrowsSpecifier
}, myLibraryUiService.buildMenu())
}
|
Note
|
Prueba de prestatarios
Puede probar los prestatarios de cualquiera de estas dos formas: → Creando un → Iniciando sesión con un usuario creado. Consulte la Sección B para obtener más información. |
¡Enhorabuena! Ha completado la segunda parte de este proyecto.
En la siguiente parte, profundizaremos en la seguridad y las restricciones (Security and Constraints).