Propel ORM Behavior for defining Parent Child relationships? - symfony

I'm using Symfony2 with PropelBundle, and lets say I have the following schema:
<table name="person">
<column name="id" type="INTEGER" primaryKey="true" autoIncrement="true" required="true"/>
<column name="name" type="VARCHAR" size="100" required="true"/>
</table>
<table name="person_parent">
<column name="person_id" type="INTEGER" primaryKey="true" required="true"/>
<column name="parent_id" type="INTEGER" primaryKey="true" required="true"/>
</table>
Consider that a person can have many "Parents", just like a parent can have many child "Persons". Both of the columns in the "person_parent" table are foreign keys to the "person" table. Unlike for relationships like Book/Author where one can set isCrossRef="true" in the schema of table "book_author" to let Propel generate methods for directly getting/setting/adding book objects from an author class or viceversa, for Parent/Child relationships it is not possible to set isCrossRef="true" and thus not possible to get/set/add Parent Person objects directly from a "Person" Object. In other words it is not possible to do:
$person = new Person();
$person->setPersonParents($personCollection);
The method "setPersonParents()" to set all the parent "Person" objects of any given person is not available. However, for cross tables that do not reference to the same table like author_book, setting isCrossRef="true" allows for the following:
$author = new Author();
$author->setBooks($bookCollection);
With that in mind, it is also not possible to directly select a Person's "Parents" in a "New Person" Form...
For friend like relationships there is the EqualNestBehavior, which allows for:
$person = new Person();
$person->setFriends($personCollection);
However, that behavior does not seem applicable to Parent/Child relationships since it does not care about hierarchy (if one tries to get the "Parents" of any given "Person", one gets all the "Childs" of that "Person" in addition to its "Parents"...). This is the behavior one would expect for "friend like" relationships, where a Friend of a Person is also that Person's Friend.
Is there a way to use the EqualNestBehavior for the Parent/Child case? or is there any other Propel behavior or method to handle this kind of relationships?

I am not sure about EqualNestBehavior but you could set up your workflow as following.
In your schema define FKs to Person table:
<table name="person">
<column name="id" type="INTEGER" primaryKey="true" autoIncrement="true" required="true"/>
<column name="name" type="VARCHAR" size="100" required="true"/>
</table>
<table name="person_parent">
<column name="person_id" type="INTEGER" primaryKey="true" required="true"/>
<column name="parent_id" type="INTEGER" primaryKey="true" required="true"/>
<foreign-key foreignTable="person" name="p1" onDelete="CASCADE" onUpdate="CASCADE">
<reference local="person_id" foreign="id"/>
</foreign-key>
<foreign-key foreignTable="person" name="p2" onDelete="CASCADE" onUpdate="CASCADE">
<reference local="parent_id" foreign="id"/>
</foreign-key>
</table>
After that, when you generate models - you'll get such methods as:
$person = new Person();
$person->getPersonParentsRelatedByParentId();
$person->setPersonParentsRelatedByParentId($personCollection);
$person->getPersonParentsRelatedByPersonId();
$person->setPersonParentsRelatedByPersonId($personCollection);
Setters accept PropelCollection as argument.
You just have to think about this situation not like "get parents" or "get children", but as about related elements.

Related

Convert from Name of Bean and Name of Property to the property in JSP

I have a been called “FooOneBean” and that has a number of properties, one of which is “fooOnePropA’ which is a list of Foo objects.
In a Java Controller, this bean is set in the request as follows:
request.setAttibute(“FooOneBean”, FooOneBean.instance());
Normally, this can be used in a JSP to display the dropdown as follows:
<tiles:importAttribute name="elementName" />
<c:set var=”dropdown” value=”${FooOneBean.fooOnePropA}” /> <!—Name of the bean and property hard-coded here -- >
<form:select path="${elementName}" id="${elementName}">
<form:options items="${dropdown}" itemLabel="displayName" itemValue="code" />
</form:select>
However, what we have is a very generic JSP that should be able to display the dropdown based on the String name of the bean and property set in the JSP. The name of the bean or property can change based on how the JSP is invoked. So,
<tiles:importAttribute name="elementName" />
<tiles:importAttribute name="resourceBean" />
<tiles:importAttribute name="resourceList" />
<%--
<c:out value="${resourceBean}" /> <!—Displays “FooOneBean” -->
<c:out value="${resourceList}" /> <!—Displays “fooOnePropA” -->
--%>
<c:set var="beanName" value="${resourceBean}" />
<c:set var="propName" value="${resourceList}" />
<c:set var="dropdown" value="${beanName.propName }" />
<form:select path="${elementName}" id="${elementName}">
<form:options items="${dropdown}" itemLabel="displayName" itemValue="code" />
</form:select>
This will fail with the message: '${beanName.propName}' Property 'propName' not found on type java.lang.String
On the otherhand if dropdown is set as:
<c:set var="dropdown" value="${beanName}.${propName}" />
It fails with the error: Type [java.lang.String] is not valid for option items
How can this be accomplished in JSP? I am able to do this using Scriptlets that I want to avoid.
Thanks for the comments from JB Nizet. This can be done as:
<tiles:importAttribute name="elementName" />
<tiles:importAttribute name="resourceBean" />
<tiles:importAttribute name="resourceList" />
<c:set var="dropdown" value="${requestScope[resourceBean][resourceList]}”/>
<form:select path="${elementName}" id="${elementName}">
<form:options items="${dropdown}" itemLabel="displayName" itemValue="code" />
</form:select>

Pentaho dynamic loading name of month in Pentaho User Console

In my dimensional table i have month field with number representation of month 1...12, but in this table i don't have name of that months (Jan, Feb, etc...). In mondrian file i use it like time dimension.
<Dimension type="TimeDimension" visible="true" highCardinality="false" name="timedimension" caption="Datetime">
<Hierarchy visible="true" hasAll="true" primaryKey="id_date">
<Table name="dim_date" schema="dbo">
</Table>
<Level name="year" visible="true" column="year4" type="String" uniqueMembers="false" levelType="TimeYears" hideMemberIf="Never" caption="year">
<Annotations>
<Annotation name="AnalyzerDateFormat">
<![CDATA[[yyyy]]]>
</Annotation>
</Annotations>
</Level>
<Level name="month" visible="true" column="month" ordinalColumn="month" type="String" uniqueMembers="false" levelType="TimeMonths" hideMemberIf="Never" caption="month">
<Annotations>
<Annotation name="AnalyzerDateFormat">
<![CDATA[[yyyy].['Q'q].[M]]]>
</Annotation>
</Annotations>
</Level>
</Hierarchy>
</Dimension>
When i show this dimension in Pentaho User Console i would like to show the Name of these months instead of numbers in Anylyzer report. Is this possible without adding this names of months into my dimension table. Exists some internal function for showing of that or some property file or internal dictionary for using of that or some attribute in mondrian file? And i would like to have the names of the month depends on selected language in Pentaho User Console.
If creating a column in the dim table is not an option (although this is the recommended approach), you can do it by adding a KeyExpression element to the Level expression:
<Level name="month" visible="true" ordinalColumn="month" type="String" uniqueMembers="false" levelType="TimeMonths" hideMemberIf="Never">
<KeyExpression>
<SQL dialect="generic">
CASE
WHEN month = 1 THEN 'Jan'
WHEN month = 2 THEN 'Feb'
(...)
WHEN month = 12 THEN 'Dec'
END
</SQL>
</KeyExpression>
<Annotations>
<Annotation name="AnalyzerDateFormat">
<![CDATA[[yyyy].['Q'q].[MMM]]]>
</Annotation>
</Annotations>
</Level>
Notice that I removed the column and caption attributes. Also notice the change in the value of AnalyzerDateFormat (if you want full month names you must use MMMM instead of MMM).

Coldfusion: Need to disable or block "Error invoking CFC - server error" error pop-up message

I have a .cfm and .cfc that I am using to edit data in a cfgrid on the .cfm, and it works, however 10% of the time I will get the following error message:
"Error invoking CFC /test/editCFgrid.cfc: Internal Server Error"
I tried using the debugging advice, however no luck.
Here is the CFM code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
<cfform name="artistform">
<cfgrid format="html" name="artistgrid" pagesize=11
striperows="yes"
bind="cfc:editCFgrid.getArtists({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})"
delete="yes" insert="yes" selectmode="edit"
onchange="cfc:editCFgrid.saveArtist({cfgridaction},{cfgridrow},{cfgridchanged})">
<cfgridcolumn name="firstname" header="First Name" />
<cfgridcolumn name="lastname" header="Last Name" />
<cfgridcolumn name="address" header="Address" />
<cfgridcolumn name="city" header="City" />
<cfgridcolumn name="state" header="State" />
<cfgridcolumn name="postalcode" header="Postal Code" />
<cfgridcolumn name="email" header="Email" />
<cfgridcolumn name="phone" header="Phone" />
<cfgridcolumn name="fax" header="Fax" />
<cfgridcolumn name="thepassword" header="Password" />
</cfgrid>
</cfform>
</body>
</html>
Here is the CFC code:
<cfcomponent output="FALSE">
<cffunction name="getArtists" hint="I extract artists from the database" access="remote" output="FALSE" returntype="struct">
<cfargument name="page" required="TRUE" hint="the page the grid is on" />
<cfargument name="pagesize" required="TRUE" hint="records displayed per page" />
<cfargument name="gridsortcolumn" required="TRUE" hint="selected column to sort" />
<cfargument name="gridsortdirection" required="TRUE" hint="the sort direction" />
<cfset var qArtists = "" />
<cfif arguments.gridsortcolumn eq "">
<cfset arguments.gridsortcolumn = "lastname" />
<cfset arguments.gridsortdirection = "asc" />
</cfif>
<cfquery name="qArtists" datasource="test_database">
SELECT *
FROM Artists
ORDER BY #arguments.gridsortcolumn# #arguments.gridsortdirection#
</cfquery>
<cfreturn QueryConvertForGrid( qArtists, arguments.page, arguments.pagesize ) />
</cffunction>
<cffunction name="saveArtist" type="any" hint="I insert, update or delete an artist" access="remote" output="FALSE" returntype="void">
<cfargument name="gridaction" type="any" required="TRUE" hint="I for insert, U for update and D for delete" />
<cfargument name="gridrow" type="any" required="TRUE" hint="the rows being inserted or updated" />
<cfargument name="gridchanged" type="any" hint="the changes" />
<cfset var qInsertArtist = "" />
<cfset var qUpdateArtist = "" />
<cfset var qDeleteArtist = "" />
<cfif IsStruct( arguments.gridrow ) and IsStruct( arguments.gridchanged )>
<cfif arguments.gridaction eq "I">
<cfquery name="qInsertArtist" datasource="test_database">
INSERT INTO Artists
(firstname, lastname, address, city, state, postalcode, email, phone, fax, thepassword)
VALUES
(<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.firstname#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.lastname#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.address#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.city#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.state#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.postalcode#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.email#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.phone#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.fax#" />,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.gridrow.thepassword#" />)
</cfquery>
<cfelseif arguments.gridaction eq "U">
<cfset var colname = StructKeyList( arguments.gridchanged ) />
<cfset var value = StructFind( arguments.gridchanged, colname ) />
<cfquery name="qUpdateArtist" datasource="test_database">
UPDATE Artists
SET #colname# = <cfqueryparam value="#value#" />
WHERE artistid = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.gridrow.artistid#" />
</cfquery>
<cfelseif arguments.gridaction eq "D">
<cfquery name="qDeleteArtist" datasource="test_database">
DELETE FROM Artists
WHERE artistid = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.gridrow.artistid#" />
</cfquery>
</cfif>
</cfif>
</cffunction>
</cfcomponent>
Each function (edit, insert, delete, etc) works, but it randomly will generate the error. Ultimately, I am seeking a code that will ignore/block/"OK" the error message so the user won't see it.
Any help will be greatly appreciated! I have spent the entire day (9 hours) googling for an answer, but I haven't found any. I do not have access to the CF Admin Log, I'm just a regular developer. Thanks!
Google chrome, firebug in firefox, IE developer tools can all help you with this.
In google chrome (my preference), right click on your page > inspect element. Then go to the network tab. You'll have to refresh your web page. Then you will see your cfc in the list of files (probably RED). Right click > open in new tab, and you'll open the cfc directly with all the arguments being called. You should then be able to see your error.
There is no ignore/block/ok. You need to fix the error.
Perhaps a time out or a SQL error. To get the full error use: cferror, this tag show a full debug information (better than debug server info) I sent it to me by mail:
Put this on you Application file:
<cferror type="exception" template="error.cfm" />
<cferror type="request" template="error.cfm" />
Create a "error.cfm" file with:
"Error message"
<cfsavecontent variable="errorContent">
<cfoutput>
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<h2>Error:</h2>
<cfdump var="#error#" label="Error">
<h2>Form:</h2>
<cfdump var="#form#" label="Form">
<h2>URL:</h2>
<cfdump var="#url#" label="URL">
<h2>SESSION:</h2>
<cfdump var="#SESSION#" label="SESSION">
</cfoutput>
</cfsavecontent>
<cfmail to="mail#mymail.com" from="mail#mymail.com" subject="Error on #cgi.server_name#: #error.message#" type="html">
#errorContent#
</cfmail>
There is a way to ignore/block/ok the "error" message with the following script:
<script>ColdFusion.setGlobalErrorHandler(function (error)
{mygrid = ColdFusion.Grid.refresh ('artistgrid', false);
}
);
</script>
Insert this script in the CFM files, and you're good to go! Being that the error that I was encountering was not impacting the function (editing the cells) of the cfgrid (the pop-up message was being more of a nuisance then anything else). Hopefully this solution will help others!
You do not need to use the 'Grid.refresh' command in the script, you can use anything command you want.
Thanks everyone who attempted to answer my issue!

reRender a specific row of rich:dataTable

Good Morning!
Is it possible to reRender only 1 specific row of rich:dataTable?
I have a rich:dataTable and, when I do something that I´m sure only 1 row has changed, I need to reRednder only this row, not the entire table. Is it possible? How?
XHTML:
<rich:dataTable id="myTable" value="#{bean.table}" var="me">
<rich:column>
<h:outputText value="#{me.id}" />
</rich:column>
<rich:column>
<h:outputText value="#{me.valueOne}" />
</rich:column>
<rich:column>
<h:outputText value="#{me.valueTwo}" />
</rich:column>
</rich:dataTable>
<some:tag.... reRender="??????" action="bean.example" />
Java:
public void example{
// Do something that affects to the row selected
}
THANK YOU VERY MUCH.
Yes , it is possible . You have to specify the followings things:
Which columns to be rendered via the reRender attribute of the tags that can invoke MBean method
What rows to be rendered via the ajaxKeys attribute of the rich:dataTable .
The ajaxKeys attribute is bound to Set <Integer> Object which holds the row numbers to be updated.
For example , suppose you want to invoke a Mbean method using a4j:commandButton and want to render a particular row and column after the action finishes . You can use the following :
<a4j:commandButton action="#{bean.someAction}" reRender="columnID,columnID2">
<f:setPropertyActionListener value="#{idx}" target="#{bean.selectedRow}" />
</a4j:commandButton>
<rich:dataTable id="myTable" value="#{bean.table}" var="me" ajaxKeys="#{bean.rowsToUpdate}" rowKeyVar="idx">
<rich:column id="columnID">
<h:outputText value="#{me.id}" />
</rich:column>
<rich:column id="columnID2">
<h:outputText value="#{me.valueOne}" />
</rich:column>
<rich:column>
<h:outputText value="#{me.valueTwo}" />
</rich:column>
</rich:dataTable>
Inside the bean.someAction() , you add the row number that you want to update to the rowsToUpdate integer set:
HashSet<Integer> rows = new HashSet<Integer>();
rows.add(selectedRow);
setRowsToUpdate( rows );

Display sub-property in table tag (Spring Roo supplied tag library)

I am using Spring Roo. There is table:table and table:column tags.
How to display sub-property of colections elements in table?
In straightforward way it doesnt work:
<table:table data="${knowledgebase.concepts}" id="l_domain_Concept" path="/concepts" z="user-managed">
<table:column id="c_domain_Concept_translations" property="defaultTranslation.name" z="user-managed" />
</table:table>
Exception:
Caused by: javax.el.PropertyNotFoundException: Property 'defaultTranslation.name' not found on type domain.data.Concept
I modified table.tagx so it could be used with sub-properties and Spring converting capabilities.
<c:forTokens items="${columnProperties}" delims="," var="column" varStatus="num">
<c:set var="prop" value="${ item }" />
<c:forTokens items="${column}" delims="." var="subprop">
<c:set var="prop" value="${ prop[subprop]}" />
</c:forTokens>
<c:set var="columnMaxLength" value="${lengths[num.count - 1]}" scope="request"/>
<td>
<c:choose>
<c:when test="${not convert}">
<c:out value="${columnMaxLength lt 0 ? prop : fn:substring(prop, 0, columnMaxLength)}" />
</c:when>
<c:otherwise>
<spring:eval expression="prop" />
</c:otherwise>
</c:choose>
</td>
Edit PROJECT/src/main/webapp/WEB-INF/tags/form/fields/table.tagx. At line 78, you should see <c:set var="columnDatePattern" value="${patterns[num.count-1]}" />. Put under that line, the following piece of code:
<!-- Get the last descendant property -->
<c:set var="prop" value="${item}" />
<c:forTokens items="${column}" delims="." var="subprop">
<c:if test="${not empty prop}">
<c:set var="prop" value="${prop[subprop]}" />
</c:if>
</c:forTokens>
<!-- Now under tag c:choose below, please change from "item[column]" into "prop" -->
<!-- // End of Get the last descendant property. -->
Don't forget to change from "item[column]" into "prop". For example, the changed lines should be:
<c:choose>
<c:when test="${columnType eq 'date'}">
<spring:escapeBody>
<fmt:formatDate value="${prop}" pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt" />
</spring:escapeBody>
</c:when>
<c:when test="${columnType eq 'calendar'}">
<spring:escapeBody>
<fmt:formatDate value="${prop.time}" pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt"/>
</spring:escapeBody>
</c:when>
<c:otherwise>
<c:set var="colTxt">
<spring:eval expression="prop" htmlEscape="false" />
</c:set>
</c:otherwise>
</c:choose>
You can edit entity file Concepts.java :
#Transient
public String getDefaultTranslationName(){
return defaultTranslation.getName();
}
After , you edit to:
<table:column id="c_domain_Concept_translations" property="DefaultTranslationName" z="user-managed" />
Hope can help you !

Resources