Search code examples
grailsgspgrails-3.0

Grails many to many with join table + extra columns


I needed to create a custom join table to re-model a many-to-many mapping and following some great posts on here came up with the model below.

Now my question is, if I have either a Course or Journey object in a GSP, how do I access the extra column data belonging to the join table.

In this example, I want to access the field named extraColumn1 in the CourseJourneyDetail within my GSP if I have either a journey or course instance

I've tried the following :

${course.courseJourneyDetail.extraColumn1}

but it didn't work.

Here are (relevant parts of) my domain classes :

    class Course {

        static hasMany = [journies:         CourseJourneyDetail]

        String courseName
        String organisersDescription


        Set<Journey> getJournies() {
            return CourseJourneyDetail.findAllByCourse(this)*.journey
        }
    }

    class Journey {

        static hasMany = [courses: CourseJourneyDetail]

        java.util.Date dateCreated
        java.util.Date lastUpdated
        boolean enabled = true

        User user


        Set<Course> getCourses() {
            return CourseJourneyDetail.findAllByJourney(this)*.course

        }
}

class CourseJourneyDetail  implements Serializable {

    String extraColumn1

    static belongsTo = [course: Course, journey: Journey]

    boolean equals(other) {
        if (!(other instanceof CourseJourneyDetail)) {
            return false
        }

        other.journey?.id == journey?.id &&
                other.course?.id == course?.id
    }

    int hashCode() {
        def builder = new HashCodeBuilder()
        if (course) builder.append(course.id)
        if (journey) builder.append(journey.id)
        builder.toHashCode()
    }

    static constraints = {
    }

    static mapping = {
        version false
        id composite: ['course', 'journey']
    }
}

Solution

  • Since you've established that each Course/Journey has a collection of CourseJourneyDetail's rather than a single instance, ${course.courseJourneyDetail.extraColumn1} won't work (as you've discovered).

    If you break down your groovy expression into this: course.courseJourneyDetail, it doesn't really make sense based on the relationships you have created. The reason being, Course doesn't have a single CourseJourneyDetail but rather a collection.

    If your desire is to have Course and Journey in a one-to-one relationship, but with a join table with additional columns, then your domain structure needs to change to reflect this: rather than using static hasHany in each class, you would switch to a single instance.

    If your desire is to keep the many-to-many relationship, then you need to think about how to fetch the appropriate join object that represents the association. One example:

    CourseJourneyDetail.findAllByCourseAndJourney(<courseInstance>, <journeyInstance>)
    

    If you want the additional columns collected for all of the many-to-many associations, you can use a syntax that you are already using in your convience methods (getJournies and getCourses):

    course.journies*.extraColumn1
    

    This would output an array of Strings, so its usage makes less sense within a ${} expression; and more sense within a g:each. It entirely depends on how you plan on using this data.