CF ORM many-to-many relationship sort order - orm

I have a many to many relationship setup, and need to control the order.
I have an entity with the property below. How do I control the order of the components when retrieved via this relationship?
property name="Components" fieldtype="many-to-many" cfc="CmsComponent"
type="array"
singularname="Component"
linktable="CMSPageComponents"
fkcolumn="page_id"
inversejoincolumn="component_id";
I've setup a column in the linktable (CMSPageComponents) called dispOrder. But when I set the orderby="dispOrder" or orderby="CMSPageComponents.dispOrder" attribute on the property above, it just seems to ignore it.
Any suggestions on how I can control the order of a many-to-many relationship?

When the relationship is many-to-many CF will apply the orderBy statement to the object table not to the link table. So in theory you could move your dispOrder column from the CMSPageComponents link table to the CmsComponent table to make it work.
But in practice I expect the ordering is specific to the many-to-many relationship (i.e. the particular page), in which case you could follow Peter's advice and create a separate entity which links the other two entities and lets you define an order property.
So you would have 3 entities:
CmsPage
CmsComponent
CmsPageComponent
CmsPageComponent might look something like this:
<cfcomponent displayname="CmsPageComponent" persistent="true" table="cmsPageComponents">
<!--- Add a primary key for the link Entity --->
<cfproperty name="ID" fieldType="id" generator="native">
<cfproperty name="dispOrder">
<cfproperty name="page" fieldType="many-to-one" cfc="CmsPage" fkColumn="pageID">
<cfproperty name="component" fieldType="many-to-one" cfc="CmsComponent" fkColumn="componentID">
<!--- init() etc --->
</cfcomponent>
CmsPage could then have a one-to-many relationship with the link entity allowing ordering using the dispOrder column:
<cfcomponent displayname="CmsPage" persistent="true" table="cmsPages">
<cfproperty name="ID" fieldType="id" generator="native">
<cfproperty name="pageComponents" singularName="pageComponent" fieldType="one-to-many" cfc="PageComponent" fkColumn="pageID" orderBy="dispOrder">
<!--- init() etc --->
</cfcomponent>
Update
The following shows how you might add and display page components. Not the only or necessarily best way, but just to give you an idea:
<cfscript>
transaction{
//load the page
page = EntityLoadByPK( "CmsPage",1 );
//load the components we want to add
component1 = EntityLoadByPK( "CmsComponent",1 );
component2 = EntityLoadByPK( "CmsComponent",2 );
//create link objects
pageComponent1 = EntityNew( "CmsPageComponent" );
pageComponent2 = EntityNew( "CmsPageComponent" );
// link them to the pages and components in the order we want
pageComponent1.setComponent( component1 );
pageComponent1.setPage( page );
pageComponent1.setDispOrder( 2 );
EntitySave( pageComponent1 );
pageComponent2.setComponent( component2 );
pageComponent2.setPage( page );
pageComponent2.setDispOrder( 1 );
EntitySave( pageComponent2 );
}
//Reload from the database so the order is applied
EntityReload( page );
</cfscript>
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<cfoutput>
<h2>Page #page.getID()#</h2>
<ol>
<cfloop array="#page.getPageComponents()#" index="pageComponent">
<cfset component = pageComponent.getComponent()>
<li>Component ID #component.getID()#, Display Order = #pageComponent.getDispOrder()#)</li>
</cfloop>
</ol>
</cfoutput>
</body>
</html>
NOTE: this assumes the ORM setting flushAtRequestEnd is true in Application.cfc

Related

Adding onto an existing ColdFusion 2016 ORM application

I am working on adding to a ColdFusion 2016 application with ORM. The application pulls up and doesn't give any type of errors prior to adding the second table code (TableTwo). As soon as the second table code is added, it gives an error saying:
Can't create table XXXXX.YYYYY (errno: 150 "Foreign key constraint
is incorrectly formed")
Where XXXXX is the name of the application as a whole - not TableOne, TableTwo or BaseTable.
Are there any ideas as to what is causing the application to give such an error?
If I delete all references to TableTwo and reload ORM, the application will pull up again. TableOne and TableTwo need to join to the BaseTable, but will not join to each other.
Below is an example of how the code is currently formatted.
TableOne.cfc
component {
property name="id" fieldType="id" ormtype="int" type="numeric" generator="native";
property name="baseTableID" ormtype="int" type="numeric" insert="false" update="false";
//relations
property name"baseTable" fieldType="one-to-one" cfc="BaseTable" fkcolumn="baseTableID" joincolumn="id" notnull="true" casecade="save-update";
}
BaseTable.cfc
component {
property name="id" fieldType="id" ormtype="int" type="numeric" generator="native";
//relations
property name="TableOne" fieldtype="one-to-one" cfc="TableOne" mappedby="baseTable" cascade="all-delete-orphan";
--Attempting to add a second one
property name="TableTwo" fieldtype="one-to-one" cfc="TableTwo" mappedby="baseTable" cascade="all-delete-orphan";
}
TableTwo.cfc
component {
property name="id" fieldType="id" ormtype="int" type="numeric" generator="native";
property name="baseTableID" ormtype="int" type="numeric" insert="false" update="false";
//relations
property name"BaseTable" fieldType="one-to-one" cfc="BaseTable" fkcolumn="baseTableID" joincolumn="id" notnull="true" casecade="save-update";
}

Layout trouble with CUBA screen designer

I am testing the Cuba-Platform and I have trouble understanding the layout options within the screen designer.
I am trying to spread tables and twincolumns evenly and aligned over the screen. (And add a label on top of each)
The simplest task of stretching the containers (vbox and hbox) is difficult.
When I set height/width to 100% the items within are evenly distributed within the space. When I now try to set the height of the table within the vbox to 100% (hoping it would stretch) it gets reset to 100px.
I also tried the grid component, but when I set it to 100% the columns are have all the same hight (no good for label)
Maybe my understanding (coming from xaml/c# and the ms-components) is completely wrong. Please tell me, how can I create a view and ensure:
that the components are at the same height (stretched if need be)
that the components are aligned
that the whole screen is filled
label label
table twincolum
okcancel
As requested a simple image - of what by now drives me crazy..
Id like to add some code I eventually came up with - Its not yet the ideal thing:
<layout>
<hbox id="hboxexpand"
expand="assignTwinCol"
height="100%"
spacing="true"
width="80%">
<table id="userTable"
height="100%"
**width="auto"**>
<columns>
<column id="value1"
caption="msg://1"/>
<column id="firstName"
description="msg://firstNameHeader"/>
<column id="lastName"
caption="msg://nameHeader"/>
<column id="active"
caption="msg://activeHeader"/>
</columns>
<rows datasource="userDs"/>
</table>
<twinColumn id="assignTwinCol"
addAllBtnEnabled="true"
height="100%"
optionsDatasource="myDs"/>
</hbox>
</layout>
Please note: When I use the designer the **** part width=auto will be reset to 200px every time! I can only change that in the xml designer
Table does not have full support of AUTO width because width of columns can be adjusted by content, so I would recommend using fixed or relative width for a table.
<layout>
<hbox expand="twinColumn"
height="100%"
spacing="true"
width="100%">
<table id="userTable"
height="100%"
width="400px">
<columns>
<column id="firstName"/>
<column id="lastName"/>
<column id="active"/>
</columns>
<rows datasource="usersDs"/>
</table>
<twinColumn id="twinColumn"
addAllBtnEnabled="true"
optionsDatasource="usersSelectDs"
height="100%"/>
</hbox>
</layout>
If you want to set headers for TwinColumn columns you can use unwrap methods and Vaadin API of TwinColSelect:
public class Screen extends AbstractWindow {
#Inject
private TwinColumn twinColumn;
#Inject
private Table<User> userTable;
#Override
public void init(Map<String, Object> params) {
super.init(params);
TwinColSelect colSelect = twinColumn.unwrap(TwinColSelect.class);
colSelect.setLeftColumnCaption("Header Left");
colSelect.setRightColumnCaption("Header Left");
Layout tableComposition = userTable.unwrapComposition(Layout.class);
tableComposition.setCaption("Table Header");
}
}
We need to align our headers of the Table and TwinColumn. Table caption does not have padding-bottom 0.3em that TwinColumn headers have. We can add padding using stylename for the table:
<table id="userTable"
height="100%"
width="400px"
stylename="padding">
<columns>
<column id="firstName"/>
<column id="lastName"/>
<column id="active"/>
</columns>
<rows datasource="usersDs"/>
</table>
Then we create theme extension using Studio and add CSS definition to halo-ext.scss file:
.v-caption.v-caption-padding {
padding-bottom: 0.3em;
}
Now restart the application and our headers are aligned!
CUBA Team are planning to add caption and leftColumnCaption/rightColumnCaption attributes for components in the next minor release and you will be able to assign them from XML/Screen Designer.
See also:
https://doc.cuba-platform.com/manual-6.2/webComponentsHelper.html
https://vaadin.com/api/com/vaadin/ui/TwinColSelect.html

Dynamically add text into body as I click <Select> indexes in coldfusion

On my cold fusion page what I want to do is click one of the options from my <Select> statement. After doing so I want to see a richtextbox be dynamically filled with a matching record in my database from the value of the item I clicked. I also want it to do the same for a checkbox, and authors object.
Each object already has a column in my database, what is left is to add this dynamic feature to fill in my data for editing a post.
My code looks like this:
<!--- Query --->
<cfquery name="Posts" datasource="Postings">
Select *
from BlogPosts
</cfquery>
<!--- Fill Listbox --->
<cfselect name="LoopPosts" size="12">
<cfoutput query="Posts"><option value="#PostID#">
#PostTitle#</option></cfoutput>
</cfselect>
<!--- Secondary Query --->
<cfquery name="PostsQuery" datasource="Postings">
Select *
from BlogPosts
</cfquery>
<!--- Fill --->
<cftextarea style="width: 1000px; height: 600px;" name="PostBody" id="blog"><cfoutput query="Postquery">#PostBody#</cfoutput></cftextarea>
<input name="ActivePost" type="checkbox" value="<cfoutput query="Postquery">#Active#</cfoutput>">
Let me try to redeem myself with a modified answer:
You can dynamically reapply options to elements using ajax calls to your cfc file (since cfc bind is apparently a no-no) by adding an event listener to the first select field and calling the method in the cfc file via an AJAX get request. In the success function, you can then update the response to the textarea field.
Every time a selection is made from the select box, the text box will refresh the data with the appropriate query result.
The HTML page could look something like this:
<script type="text/javascript">
document.forms['yourformname'].elements['LoopPosts'].addEventListener('click', function(){
$.ajax({
type:"GET",
url: "yourcfcfilename.cfc?method=GetSelectedPost",
data: {selectedPost : this.value},
success: function(response) {
document.forms['yourformname'].elements['PostBody'].innerHTML = response;
},
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert("Status: " + textStatus); alert("Error: " + errorThrown);
}
});
});
</script>
And the cfc page could look something like this:
<cfcomponent displayname="Post Functions" output="true">
<!--- Secondary Query --->
<cffunction name="GetSelectedPost" access="remote" returntype="String">
<cfargument name="selectedPost" type="string" required="true">
<cfquery name="PostsQuery" datasource="Postings">
SELECT PostBody
FROM BlogPosts
WHERE PostID = <cfqueryparam value="ARGUMENTS.selectedPost">
</cfquery>
<cfreturn PostsQuery.PostBody>
</cffunction>
</cfcomponent>
Every time a selection is made from the select box, the text box will refresh the data with the appropriate query result.

while adding records separately using many-to-one relationship getting variable undefined

I am using ColdFusion ORM with FW/1.
I have two tables student and parent, I have given many-to-one relationship as we know parent can have more wards in a school. I am adding student and parent separately, while adding parent I need parentId to be stored in student table so that I can list or get student with their parents for further features and activity.
Can anyone help me out how should I save the parent in a parent table and also parentId in student table?
student.cfc
component output="false" persistent="true" accessors="true" entityname="Student" table="school_student" {
// Use a mysql autonumber for an ID
property name="StudentId" column="school_studentid" type=numeric fieldtype="id" generator="identity";
property name="Fullname" column="school_studentFullname" type="string" length="128" notnull="true";
property name="email" column="school_studentEmail" type="string" length="128" notnull="true";
property name="password" column="school_studentPassword" type="string" length="64";
property name="Parent" fieldtype="many-to-one" cfc="Parent" fkcolumn="student_school_parentId" lazy="true" singularname="Parent";
}
parent.cfc
component output="false" accessors="true" persistent="true" entityname="Parent" table="school_parent" {
//use mysql autonumber id
property name="parentId" fieldtype="id" column="school_parentid" generator="identity";
property name="name" type="string" column="school_parentname" length="128";
property name="email" type="string" column="school_parentemail" length="128" ;
property name="password" type="string" column="school_parentpassword" length="64";
//relate parent with Student.
property name="Student" fieldtype="one-to-many" cfc="Student" fkcolumn="student_school_parentId" lazy="extra" inverse="true";
This is the way i am saving parent in my controllers:
<cfargument name="rc" type="struct" required="true">
<cfset parent = getParentService().parent(arguments.rc.parentid)>
<cfset student = getStudentService().student(arguments.rc.studentId)>
<cfset parent.setName(arguments.rc.name)>
<cfset parent.setEmail(arguments.rc.email)>
<cfset parent.setPassword(arguments.rc.password)>
<cfset getParentService().save(parent)>
<cfset student.addparent(parent)>
<cfset variables.fw.redirect('parent.list')>
How can I save parentid in student table while saving parent? I mean how can i call student save method to have parentid in student table?
Any help would be appreciated.
thanks
Try this
<cfset parent.addstudent(student)>
<cfset getParentService().save(parent)>
And try setting cascade="any" on the student property of Parent. This should allow you to save both objects...even if they are both are new objects being persisted for the first time.

Coldfusion HQL query with relationships - unable to resolve path

I'm having problems using an HQL query to get data from a related entity.
I have a 'Photoshoot' entity with a one to many relationship to an 'Image' entity.
I'm trying to pull all images that belong to a particular Photoshoot, which I want to do with an HQL query so that I can get some specific filtering in.
What I'm getting back is this:
Unable to resolve path [Photoshoot.sPhotoshootGUID], unexpected token [Photoshoot] [FROM Image WHERE Photoshoot.sPhotoshootGUID = '889440aa-a12a-11e1-8edb-d02788828044']
I can't figure out why - if I pull back the Photoshoot, I can easily get to the associated images using the 'getImages()' function. If I use exactly the same code to get another related entity it seems to work fine!
Here's the code for my entities:
--- Image ---
<cfcomponent persistent="true" entityname="Image" table="tblImages_Base">
<!--- Identifier --->
<cfproperty name="sImageGUID" fieldtype="id" generator="guid" setter="false" />
<!--- Properties --->
<cfproperty name="sFileName" ormtype="string" />
<cfproperty name="sImageFolder" ormtype="string" dbdefault="" />
<cfproperty name="Active" ormtype="boolean" default=0 dbdefault=0 notnull="true" />
<!--- Many Images can belong to a single Photoshoot --->
<cfproperty name="Photoshoot"
fieldtype="many-to-one"
cfc="Photoshoot"
fkcolumn="fk_sPhotoshootGUID"
fetch="join"
inverse="true"
/>
</cfcomponent>
--- Photoshoot ---
<cfcomponent persistent="true" entityname="Photoshoot" table="tblPhotoshoots">
<!--- Identifier --->
<cfproperty name="sPhotoshootGUID" fieldtype="id" generator="guid" setter="false" />
<!--- Properties --->
<cfproperty name="Active" ormtype="boolean" default=0 dbdefault=0 notnull="true" />
<cfproperty name="l_ImageOrder" ormtype="text" />
<!--- One Photoshoot can contain many Images --->
<cfproperty name="Images"
fieldtype="one-to-many"
cfc="Image"
fkcolumn="fk_sPhotoshootGUID"
type="array"
singularname="Image"
/>
</cfcomponent>
--- HQL Query ---
<cfquery name="Local.objPhotoshootImages" dbtype="hql">
FROM Image
WHERE Photoshoot.sPhotoshootGUID = '889440aa-a12a-11e1-8edb-d02788828044'
</cfquery>
If it makes a difference, I'm running on Railo 3.3.3.000
I'm not sure why your HQL is failing - that error can be due to case-sensitivity, but "Photoshoot" seems to be in the correct case in the code you've posted.
As a workaround you could try adjusting your HQL to make the join explicit:
<cfquery name="Local.objPhotoshootImages" dbtype="hql">
FROM Image
WHERE fk_sPhotoshootGUID = '889440aa-a12a-11e1-8edb-d02788828044'
</cfquery>