Let's assume the following (simplified) domain classes:
class Person {
static hasMany = [stringProperties: StringProperty]
static constraints = {
}
}
and
class StringProperty {
String name
String value
static constraints = {
name blank:false
value blank: true
}
}
When scaffolding generates the create view, there is no option in the gsp to create a StringProperty from Person.
Does a plugin exist or does somebody know a best practice, that can render a sort of create ui that allows to create members of a hasmany relation.
I'm just asking before i take the time to modify the scaffolding templates.
This is one of the areas where a plugin or enhanced scaffolding would be welcomed by the community. If I had the time I would take the information presented here and make a plugin for it. I have used this approach a few times and it works well.
Related
Would this be correct way to extend Grails parent and child classes?
Originally I thought that overriding hasMany and belongsTo properties, would be a good idea but that did not work so well as it introduced conflicting functionality so I dropped it from subclasses.
What I am trying to do here is to package shared code between multiple applications. I am starting with these two classes in my plugin.
class Purchase {
String firstName
String lastName
static mapping = {
tablePerHierarchy false
}
static hasMany = [items: PurchaseItem]
}
class PurchaseItem {
BigDecimal price
Integer qty
statiuc belongsTo = [purchase: Purchase]
static mapping = {
tablePerHierarchy false
}
}
The application specific classes have to extend both Purchase and PurchaseItem so I am implementing it like so, inheriting one-to-many relationship:
class Flight {
static hasMany = [purchases: TicketPurchase]
}
class TicketPurchase extends Purchase {
// some class specific properties
static belongsTo = [flight: Flight]
}
class TicketPurchaseItem extends PurchaseItem
Integer bagQty
static namedQueries = {
ticketPurchaseItemsWithBagsByFlight {flightInstance->
purchase {
flight {
eq 'id', flightInstance.id
}
}
gt 'bagQty', 0
}
}
}
The namedQuery in TicketPurchaseItem joins Purchase and Flight even though super class Purchase does not belongTo Flight, only subclass TicketPurchase does.
TicketPurchase ticketPurchase = new TicketPurchase()
ticketPurchase.addToItems(new TicketPurchaseItem(bagQty: 5)).save()
Flight flight = Flight.first()
flight.addToPurchases(ticketPurchase).save()
// this works
def ticketPurchaseItemList = TicketPurchaseItem.ticketPurchaseItemsWithBagsByFlight(flight)
This works with Grails but is it good design or is there a better way to deal with domain classes extending one-to-many relationships?
The short answer is you've got it right. Probably. The question to ask is whether you're ok with allowing the properties you've added to your subclasses to be set to NULL. I don't see a problem with what you have. You can learn more about Grails domain class inheritance and polymorphic queries from the Grails documentation and from my blog article on the subject.
If you're curious about the impact of your domain class model on the database, you can take a look at the queries GORM/Hibernate is running by logging then. I believe this is the article I've used to set up logging.
I am recently working on grails and would like to know how to do more complex scaffolding
For example, if I want to Scaffold a class
class Book{
Author a
Publisher p
// ....
}
Author class
class Author{
String firstName
String lastName
// ...
}
Publisher class
class Publisher{
String name
String address
// ....
}
Now if I have a BookController
class BookController{
static scaffold = true;
}
I have a layout of
Author Publisher
However if I want a layout with
AuthorID AuthorFirstName AuthorLastName PublisherName PublisherAddress
I have looked through the http://grails.org/doc/latest/guide/scaffolding.html, however, I am unable to set it to the given property. I would like to know I am able to accomplish it? A tutorial would be helpful.
The scaffolding plugin within Grails is not designed to handle these types of complex views out of the box. You have a few options:
Use the install-templates command and modify the scaffolding templates to handle your needs.
Re-design your domain class to use embedded Author and Publisher. This will change the scaffolding output, but it also will change a lot more too. I wouldn't use this option unless you understand all the changes this will make to your domain model.
Generate the code using scaffolding then customize the output to suit your needs.
Of the three options presented here I would recommend the third as it makes the most sense to address the narrow scope of your issue.
You could also use transients. But transients aren't displayed by default.
You need to modify the templates and explicitly hide otherwise hidden fields using constraints i.e. id
NOTE: code below untested, for illustration purposes only.
//optional, but allows code completion in IDE ;-P
String authorName
String getAuthorName(){
return a.firstName + ' ' + a.lastName
}
static transients = [authorName:String]
static constraints = {
id(display:false)
}
I have set up a one-to-many relationship in my scaffolded Grails application:
class Course {
County county
Date date
int maxAttendance
static hasMany = [ persons:Person ]
}
class Person {
String firstName
String lastName
String email
Course course
boolean attended
boolean paid
static belongsTo = [ class:Course ]
}
So, when a user views the CourseController, they are able to see Person's registered in the selected Course.
My question is, how can I change the application so that when a user views the people in a given course, they can also view/modify the checkboxes for 'boolean attended' and 'boolean paid', which are also in the Person domain? Here is a screenshot:
The thing is that you are using the scaffolded view, so you are pretty much stuck with the default design.
You can modify the behaviour, by installing and modifying the templates that grails uses to generate those views:
grails install-templates
This will create the templates in src/templates/scaffolding, although I do not recommend that approach. Maybe it is time that you start developing your own controllers and views, since the scaffolding is there mainly for testing and for administrative use.
when i design database.I use embedded to embed common fields.but it's can't init dateCreated and createdBy,what'd i do?extends domain or embedded is right way to handle common fields?
code to say?
class Created {
Date dateCreated
Long createdBy
def beforeInsert()
{
dateCreated= new Date()
createdBy=0
}
}
class Updated {
Date lastUpdated
Long updatedBy
//it works?
def beforeUpdate(){
lastUpdated=new Date()
updatedBy=0
}
//it works?
def beforeInsert(){
lastUpdated=new Date()
updatedBy=0
}
}
class CreatedUpdated {
Created created
Updated updated
//Must use the embedded option, or the type of exception, can not find CreatedUpdated
static embedded = ['created','updated']
}
class Term {
String name
CreatedUpdated createdUpdated
static embedded = ['createdUpdated']
Term parent
static hasMany =[terms:Term]
static mapping = {
version false
}
String toString()
{
name
}
static constraints = {
name unique:true,size: 1..20
parent nullable: true
createdUpdated display:false,nullable:true
terms display:false
url url: true
}
}
or use extends?
class Term extends CreatedUpdated{
String name
Term parent
static hasMany =[terms:Term]
static mapping = {
version false
}
String toString()
{
name
}
static constraints = {
name unique:true,size: 1..20
parent nullable: true
terms display:false
url url: true
}
}
`
what is right to me?
I'd definitely make this example embedded rather than inherited. I don't think you should make this call based solely on the fact that objects contain common fields. Instead, you should use inheritance if it makes sense for your model using standard OO design techniques. For example, if "myClass is a myBaseClass" doesn't hold true, inheritance is probably the wrong solution.
In general, I'd stay away from classes like CreatedUpdated that are just a collection of properties and not an actual object from your domain. Java/Groovy has only single inheritance, so this only works if you have one base class like this.
Also, for that particular case, created and updated timestamps can automatically be applied by GORM. If you're using spring security, check out the audit-trail plugin for automatically creating createdBy and updatedBy columns.
In this particular case audit-trail plugin should suffice the requirements. However if you have such requirement for other fields wherein no plugin is available, then one of the possible solution could be to inject such common fields at compile time via AST Transformation. Internally audit-trail plugin uses this concept to inject those fields. Depending upon your requirement you can either use Global AST Transformations or Local AST Transformations.
I have four domain classes in my Grails based web application (first I've ever made):
class Receipt
{
String title
Date dateCreated
static hasMany = [articles: Article]
static constraints =
{
title blank: false, unique: true
}
}
class Article
{
String name
Quantity quantity
TypeOfArticle typeOfArticle
static hasOne = [quantity:Quantity, typeOfArticle:TypeOfArticle]
static constraints =
{
quantity unique: true
}
}
class Quantity
{
Integer quantity
Article article
}
class TypeOfArticle
{
String type
Article article
}
How can I add one Receipt together with all details on one view?
I want to make it look like on this picture:
http://i.stack.imgur.com/uNVzW.png
I hope you'll be able to help me! Thank you in advance!
You might want to look up Command Objects. It looks like you are trying to have someone enter data into one form that spans multiple classes. You can create a Command Object that has fields from all four classes. You use that object for the gsp fields, then capture in the controller method and validate the data; then create your actual Receipt, Article, etc.
You can get parameter in your controller action,In action you can simply write object.propertyName=params.propertyName and after getting all parameters you can simply store it by calling object.save(). you can do the same process for all four tables..