Grails addTo in for loop - grails

I'm doing a website for reading stories. My goal is to do save the content of the story into several pages to get a list and then paginate it easily; I did the following:
In the domain I created two domains, Story:
class Story {
String title
List pages
static hasMany=[users:User,pages:Page]
static belongsTo = [User]
static mapping={
users lazy:false
pages lazy:false
}
}
And Page:
class Page {
String Content
Story story
static belongsTo = Story
static constraints = {
content(blank:false,size:3..300000)
}
}
The controller save action is:
def save = {
def storyInstance = new Story(params)
def pages = new Page(params)
String content = pages.content
String[] contentArr = content.split("\r\n")
int i=0
StringBuilder page = new StringBuilder()
for(StringBuilder line:contentArr){
i++
page.append(line+"\r\n")
if(i%10==0){
pages.content = page
storyInstance.addToPages(pages)
page =new StringBuilder()
}
}
if (storyInstance.save(flush:true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'story.label', default: 'Story'), storyInstance.id])}"
redirect(action: "viewstory", id: storyInstance.id)
}else {
render(view: "create", model: [storyInstance: storyInstance])
}
}
(I know it looks messy but it's a prototype)
The problem is that I'm waiting for storyInstance.addToPages(pages) to add to the set of pages an instance of the pages every time the condition is true. But what actually happens that it gives me the last instance only with the last page_idx. I thought it would save the pages one by one so I could get a list of pages to every story.
Why does this happen and is there a simpler way to do it than what i did?
Any help is appreciated.

You are working with only one page... Correct solution:
def save = {
def storyInstance = new Story(params)
def i = 0
StringBuilder page = new StringBuilder()
for(StringBuilder line in params?.content?.split("\r\n")){
i++
page.append(line+"\r\n")
if(i%10 == 0){
storyInstance.addToPages(new Page(content: page.toString()))
page = new StringBuilder()
}
}
if (storyInstance.save(flush:true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'story.label', default: 'Story'), storyInstance.id])}"
redirect(action: "viewstory", id: storyInstance.id)
}else {
render(view: "create", model: [storyInstance: storyInstance])
}
}

Related

Grails clear hasMany entries and add new ones error?

I am currently working on a grails applications and I have a list of addresses that are attached to an account. Basically what I want to do is when the Account is edited is displays a current list of all the attached Addresses and then I can delete/add as many as I want from the view. When this data is captured it is picked up by the controller and what I want to do is to be able to clear all of the current Addresses from this account and then create the list again with what exists on the view, my code is below:
Account Domain:
class Account {
String name
Date dateCreated
Date lastUpdated
static hasMany = [addresses:Addresses]
static mapping = {
addresses cascade:"all-delete-orphan"
}
def getAddressesList() {
return LazyList.decorate(
addresses,
FactoryUtils.instantiateFactory(Addresses.class))
}
static constraints = {
name(blank:false, unique: true)
}
}
Address Domain:
class Addresses {
int indexVal
String firstLine
String postcode
String area
static belongsTo = [account:Account]
static mapping = {
}
static transients = [ 'deleted' ]
static constraints = {
indexVal(blank:false, min:0)
}
}
Account Controller:
def update() {
def accountInstance = Account.get(params.id)
if (!accountInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'account.label', default: 'Account'), params.id])
redirect(action: "list")
return
}
if (params.version) {
def version = params.version.toLong()
if (accountInstance.version > version) {
accountInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'subscriptions.label', default: 'Subscriptions')] as Object[],
"Another user has updated this Account while you were editing")
render(view: "edit", model: [accountInstance: accountInstance])
return
}
}
accountInstance.properties = params
accountInstance.addresses.clear()
accountInstance.save(flush: true)
....
}
Error:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses. Stacktrace follows:
Message: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses
This error seems to be occurring in the controller on line:
accountInstance.save(flush: true)
I have tried several different way to get this to work and would really appreciate some help.
So it seems like you have done some work that Grails can do for you.
class Account {
String name
Date dateCreated
Date lastUpdated
List addresses
static hasMany = [addresses:Address]
static mapping = {
addresses cascade:"all-delete-orphan"
}
static constraints = {
name(blank:false, unique: true)
}
}
class Address {
String firstLine
String postcode
String area
static belongsTo = [account:Account]
}
This will produce the effect you want of having addresses being a list.
I've found either
instance.addresses = null
or
instance.addresses.clear()
to work for me
When you define addresses cascade:"all-delete-orphan" in Account class you don't need static belongsTo = [account:Account] in Addresses. So just try to remove that statement and test your code. See related link.

grails hasMany of jodatime Instants

How can I work around http://jira.grails.org/browse/GPJODATIME-28 which prevents a domain class from having a hasMany of any extended type?
I'm thinking of switching the whole app to persisting a custom UserType derived from BigDecimal for posix epoch dates. Seem's like a hammer for a walnut though. IS there another approach I could take?
import org.joda.time.Instant
class Foo {
Instant birthday
Set favoriteDays = []
static hasMany = [
favoriteDays : Instant
]
static constraints = {
}
}
Hopefully I'm not missing something in your question but I've done things like this:
Create a class called MyInstant and use it in the hasMany
import org.joda.time.Instant
class MyInstant {
Instant myInstant
//anything else you might need
}
class Foo {
MyInstant birthday
Set favoriteDays = []
static hasMany = [favoriteDays: MyInstant]
}
I've tested this in the FooController:
import org.joda.time.Instant
class FooController {
def save() {
def fooInstance = new Foo(params)
.addToFavoriteDays(new MyInstant(myInstant: new Instant()))
.addToFavoriteDays(new MyInstant(myInstant: new Instant()))
.addToFavoriteDays(new MyInstant(myInstant: new Instant()))
if (!fooInstance.save(flush: true)) {
render(view: "create", model: [fooInstance: fooInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'foo.label', default: Foo'), fooInstance.id])
redirect(action: "show", id: fooInstance.id)
}
}
Everything saves correctly and the show action then shows the new Foo with all the instants. I've tested this on H2 and MySql.

How can I redirect two actions instead of one action

here is my controller:
def save = {
def productNameInstance = new ProductName(params)
if (pharmacyMasterUpdateCompositeService.addProductName(productNameInstance)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'productName.label', default: 'ProductName'), productNameInstance.id])}"
redirect(action: "show", id: productNameInstance.id)
}
else {
render(view: "create", model: [productNameInstance: productNameInstance])
}
}
where addProductName(productNameInstance) is calling service that is defined in another class.
Here if you see redirecting is happening only at action :"show" if page is created.
My problem is how can i redirect two action like "show" and "print" at the same time where my "print" action downloads a pdf file ?
You can use the chain method.
def save = {
def productNameInstance = new ProductName(params)
if (pharmacyMasterUpdateCompositeService.addProductName(productNameInstance)) {
//...
chain(action: "print", params: [id: roductNameInstance.id])
}
else {
render(view: "create", model: [productNameInstance: productNameInstance])
}
def print = {
//...
chain(action: "show", params: params)
}
def show = { }
}

The object of UserProifle cannot be saved for ShiroUser

I am working on a Grails app and I am trying to tie a ShiroUser with a UserProfile.
I have two models called ShiroUser and UserProfile. In my ShiroUser:
class ShiroUser {
... ...
static hasOne = [profile: UserProfile]
static constraints = {
email(nullable: false, blank: false, unique: true)
profile(nullable: false)
}
}
And in my UserProfile.groovy, I have:
class UserProfile {
... ...
static belongsTo = [shiroUser:ShiroUser]
}
However, in my ShiroUserController.groovy, when I try to create a new ShiroUser instance, this doesn't work so well. Here's my code:
def create() {
[shiroUserInstance: new ShiroUser(params), userProfileInstance: new UserProfile()]
}
def save() {
//todo add validation for email and password here.
def shiroUserInstance = new ShiroUser(params)
// Create a user profile
def userProfileInstance = new UserProfile()
shiroUserInstance.profile.email = params.email
shiroUserInstance.profile.firstName = params.firstName
shiroUserInstance.profile.lastName = params.lastName
if (!userProfileInstance.save(flush: true)){
render(view: "create", model: [userProfileInstance: userProfileInstance])
return
}
shiroUserInstance.profile = userProfileInstance
if (!shiroUserInstance.save(flush: true)) {
render(view: "create", model: [shiroUserInstance: shiroUserInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'shiroUser.label', default: 'ShiroUser'), shiroUserInstance.id])
redirect(action: "show", id: shiroUserInstance.id)
}
When I go to my application and try to create a new ShiroUser, the object cannot be saved. I updated the schema before I run the app so it should not be a migration issue. Any thoughts?
It looks like in this block of code you are assigning email, firstName and lastName to the wrong object:
// Create a user profile
def userProfileInstance = new UserProfile()
shiroUserInstance.profile.email = params.email
shiroUserInstance.profile.firstName = params.firstName
shiroUserInstance.profile.lastName = params.lastName
Try this instead:
// Create a user profile
def userProfileInstance = new UserProfile()
userProfileInstance.email = params.email
userProfileInstance.firstName = params.firstName
userProfileInstance.lastName = params.lastName
shiroUserInstance.profile = userProfileInstance
You should then be able to get away with just saving shiroUserInstance and it should automatically save userProfileInstance as well if you have your mapping set up correctly.

Grails Controllers adding instances

Alright I asked a question before but wasn't quite sure about it. So I went ahead and waited till now to ask again.
Main Question
How do I add a new instance of the domain through the controller? I created a function named gather to read a file with data and then create a new Book with the specific information, however it is not adding it to the database at all.
I currently have a controller (bookController) and the domain for it.
My domain is quite simple:
class Book {
static belongsTo = Author
String toString() { bookNumber }
Author bookAuthor
String title
static constraints = {
bookAuthor()
title()
}
}
I just 'generated' my views so I have the basic create, edit, list, and show. I went ahead and added my own inside the controller called gather. For the gsp, I just copied over the 'list.gsp' as I just want the user to view the list of books once the gather function is completed.
Here is what my controller looks like (just the basic generated one plus gather):
package bookdemo
import bookClient
class BookController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def gather = {
def w = new bookClient() //bookClient will gather books from txt files
def hosts = ["localhost"] //host to connect to
w.queryData(hosts) //grab information and parse
def abc = w.bookList //list of books
w.printData(abc) //print out list of books to make sure its not null
int numberOfBooks = abc.size() //list size
//create book list and return it
numberOfBooks.times {
def bookInstance = new Book(Author:"$abc.author", Title:"$abc.title")
return [bookInstance: bookInstance]
}
//params to show once adding books
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[bookInstanceList: book.list(params), bookInstanceTotal: book.count()]
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[bookInstanceList: book.list(params), bookInstanceTotal: book.count()]
}
def create = {
def bookInstance = new Book()
bookInstance.properties = params
return [bookInstance: bookInstance]
}
def save = {
def bookInstance = new Book(params)
if (bookInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])}"
redirect(action: "show", id: bookInstance.id)
}
else {
render(view: "create", model: [bookInstance: bookInstance])
}
}
def show = {
def bookInstance = book.get(params.id)
if (!bookInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
else {
[bookInstance: bookInstance]
}
}
def edit = {
def bookInstance = book.get(params.id)
if (!bookInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
else {
return [bookInstance: bookInstance]
}
}
def update = {
def bookInstance = book.get(params.id)
if (bookInstance) {
if (params.version) {
def version = params.version.toLong()
if (bookInstance.version > version) {
bookInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'book.label', default: 'Book')] as Object[], "Another user has updated this Book while you were editing")
render(view: "edit", model: [bookInstance: bookInstance])
return
}
}
bookInstance.properties = params
if (!bookInstance.hasErrors() && bookInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])}"
redirect(action: "show", id: bookInstance.id)
}
else {
render(view: "edit", model: [bookInstance: bookInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
}
def delete = {
def bookInstance = book.get(params.id)
if (bookInstance) {
try {
bookInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
}
}
The gsp shows up but for some reason my new books are not added. When I add a println in to test whether the information is in the list, it shows prints with the correct info. So I'm confused as to why it is not 'creating' the new book instance and adding it to the database.
Any suggestions?
Edit
Domain class for author:
class Author {
static hasMany = [books:Book]
String authorName
String notes
String toString() { authorName }
static constraints = {
machineName()
notes(maxSize:500)
}
}
You're not calling .save() on any of the Book instances...
I would suggets you write some unit tests for you controller and/or domain objects. Your objects cannot be being created successfully with this code
new Book(Author:"$abc.author", Title:"$abc.title")
Also the return statement here makes no sense
numberOfBooks.times {
def bookInstance = new Book(Author:"$abc.author", Title:"$abc.title")
return [bookInstance: bookInstance]
}
It looks like you have cut and pasted code without understanding what the code is doing. I think you want something more like this...
// iterate through the list of books and create the object array to pass back
def bookListInstance = []
w.bookList.each {
def bookInstance = new Book(Author:it.author, Title:it.title)
bookListInstance << bookInstance
}
// now return the list of domain objects
return [bookInstance: bookListInstance]

Resources