Pass arguments to implicit getter?

12 views
Skip to first unread message

slashwalker

unread,
Jan 17, 2010, 12:01:29 PM1/17/10
to cf-orm-dev
Hallo List,
I have the follwing two entities.

page.cfc:
<cfcomponent hint="I am a page" output="false" persistent="true">

<cfproperty name="pageID" hint="The id for the page" type="numeric"
fieldtype="id" datatype="integer" generator="identity">
<cfproperty name="title" hint="name of the page" type="string"
lenght="255">
<cfproperty name="navititle" hint="Alternative name for the
navigation" type="string" length="255">
<cfproperty name="alias" hint="Alias for use with mod_rewrite ;)"
type="string" length="255">
<cfproperty name="additional_header" hint="Insert additional css or
js" type="string">
<cfproperty name="tree_level" hint="Tree-Level" type="numeric"
length="4">
<cfproperty name="weight" hint="Ordering in the Page-Tree"
type="numeric" length="4">
<cfproperty name="sub" hint="Children of this Page" type="string"
length="255">
<cfproperty name="parent" hint="Parent of this Page" type="numeric"
length="4">
<cfproperty name="lang_code" hint="Language" type="string" length="4"
default="de">
<cfproperty name="related" hint="This is the Translation of page x"
type="numeric" length="4">
<cfproperty name="hidden" hint="hide in menu" type="numeric"
length="1" default="0">
<cfproperty name="contents" hint="The contents of this page"
singularname="content" fieldtype="many-to-many" cfc="content"
linktable="page_content" FKColumn="pageID"
inversejoincolumn="contentID" orderby="sorting"
missingrowIgnored="true">


<cffunction name="init" hint="Constructor" access="public"
returntype="page" output="false">
<cfargument name="name" hint="the name of the page" type="string"
required="no" default="">
<cfscript>
setTitle(arguments.name);

return this;

</cfscript>
</cffunction>

</cfcomponent>


content.cfc:

<cfcomponent hint="I am the Content" output="false" persistent="true">

<cfproperty name="contentID" hint="The id of the content"
type="numeric" fieldtype="id" ormtype="integer" generator="identity">
<cfproperty name="title" hint="only for internal use" type="string"
length="255">
<cfproperty name="header" hint="the headline" type="string"
lenght="255">
<cfproperty name="bodytext" hint="the main content" type="string">
<cfproperty name="includefile" hint="optional includefile"
type="string" length="255">
<cfproperty name="sorting" hint="inpage position" type="integer"
length="255">
<cfproperty name="col" hint="column in the template" type="integer"
length="1">
<cfproperty name="cfml" hint="Content contains CFML" type="integer"
length="1">
<cfproperty name="lang_code" hint="Language of this element"
type="string" length="4">

<cffunction name="init" hint="Constructor" access="public"
returntype="content" output="false">
<cfargument name="name" hint="the name of the page" type="string"
required="no" default="">
<cfscript>
setTitle(arguments.name);

return this;

</cfscript>
</cffunction>

</cfcomponent>


Every page can have any content and any content can be used in any
page. Page 1 can have content 1,3 and 6 and Content 1,3 and 6 can be
used on Page 1,4 and 6 for example.

My index.cfm lokks like this:

<cfoutput>
<cfparam name="request.id" default="startseite"><!---Default page to
load --->
<cfparam name="request.lang_code" default="de"><!---Default language
--->
<cfscript>

nav=EntityLoad("page",{parent =
0,lang_code=request.lang_code,hidden=0}); // Get first-level of navi
page=EntityLoad("page",
{alias=request.id,lang_code=request.lang_code},true);// Get current
page
</cfscript>
<html>
<head>
<title>#page.gettitle()#</title>
</head>
<body>
<!--- Loop through the navi-levels --->
<ul>
<!---First level --->
<cfloop array="#nav#" index="link">

<li><a href="#application.baseurl##request.lang_code#/#link.getalias()
#">#link.getnavititle()#</a>
<cfset sub=EntityLoad("page",{parent = link.getpageid()})>
<cfif arraylen(sub)>
<ul>
<!--- Second level --->
<cfloop array="#sub#" index="link">

<li><a href="#application.baseurl##request.lang_code#/#link.getalias()
#">#link.getnavititle()#</a>
<cfset sub=EntityLoad("page",{parent = link.getpageid()})>
<cfif arraylen(sub)>
<ul>
<!--- Third level --->
<cfloop array="#sub#" index="link">

<li><a href="#application.baseurl##request.lang_code#/#link.getalias()
#">#link.getnavititle()#</a>

</li>

</cfloop>
</ul>
</cfif>
</li>

</cfloop>
</ul>
</cfif>
</li>

</cfloop>
</ul>

<!--- Render content --->
<cfloop array="#page.getcontents()#" index="item"><!--- Here I should
be able to do soemthing like: page.getcontents({col=0}) --->
<h1>#item.getheader()#</h1>
<!--- If content element contains CFML, we have to save it to temp
file for include --->

<cfif item.getcfml()>
<cfset tmp=listlast(createuuid(),"-")>
<cfsavecontent variable="stdout">
#item.getBodytext()#

</cfsavecontent>
<cffile action="write" file="#application.temp##tmp#.cfm"
output="#stdout#">

<cfinclude template="/temp/#tmp#.cfm">
<cffile action="delete" file="#application.temp##tmp#.cfm">
<!--- If content element doesn't contain CFML, we can output it
directly --->
<cfelse>
#item.getBodytext()#
</cfif>
<cfif item.getIncludefile() neq ""><cfinclude template="includes/
#item.getincludefile()#"></cfif>

</cfloop>
</body>
</cfoutput>

Now what I need is the ability to pass an argument to page.getContents
(), without creating an own getter!

The Content has a property "col", so if i had a multi-column layout, i
should be able to do something like:


<html>

<head>
</head>

<body>
<div id="left">page.getContents({col=0})</div>
<div id="middle">page.getContents({col=1})</div>
<div id="right">page.getContents({col=2})</div>
</body>
</html>

This is, of course, a very simplified example.

Any idea?

Thanks in advance.

Bob Silverberg

unread,
Jan 17, 2010, 2:59:09 PM1/17/10
to cf-or...@googlegroups.com
I'm not sure I totally follow, but it sounds like making the contents property a structure (rather than an array), and making the key to the structure the col property of content, might be the answer.  This will only work if col will be unique across all contents for a page.

Bob

--
You received this message because you are subscribed to the Google Groups "cf-orm-dev" group.
To post to this group, send email to cf-or...@googlegroups.com.
To unsubscribe from this group, send email to cf-orm-dev+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cf-orm-dev?hl=en.






--
Bob Silverberg
www.silverwareconsulting.com

Hands-on ColdFusion ORM Training @ cf.Objective() 2010
www.ColdFusionOrmTraining.com/


slashwalker

unread,
Jan 18, 2010, 4:08:13 AM1/18/10
to cf-orm-dev
Hello Bob,
thanks for the hint. The col property is not unique, because a column
in the layout could have many content elements. I'm not sure if this
could be managed with ORM.
Think i have to switch to good old cfquery...

On 17 Jan., 20:59, Bob Silverberg <bob.silverb...@gmail.com> wrote:
> I'm not sure I totally follow, but it sounds like making the contents
> property a structure (rather than an array), and making the key to the
> structure the col property of content, might be the answer.  This will only
> work if col will be unique across all contents for a page.
>
> Bob
>

> > cf-orm-dev+...@googlegroups.com<cf-orm-dev%2Bunsu...@googlegroups.com>

Bob Silverberg

unread,
Jan 18, 2010, 11:15:13 AM1/18/10
to cf-or...@googlegroups.com
It sounds a bit early to give up, but that's obviously your call. I imagine that you could probably do something in your Page.cfc to deal with the issue. If you can distill your problem down to a few lines of code which demonstrate exactly what you want to do (and only that) then we might be able to come up with some more suggestions.

Cheers,
Bob


On Mon, Jan 18, 2010 at 4:08 AM, slashwalker <slash...@googlemail.com> wrote:
Hello Bob,
thanks for the hint. The col property is not unique, because a column
in the layout could have many content elements. I'm not sure if this
could be managed with ORM.
Think i have to switch to good old cfquery...


--
Bob Silverberg

slashwalker

unread,
Jan 18, 2010, 12:22:53 PM1/18/10
to cf-orm-dev
Hello Bob,
okay, here a very simple example:
I should be able to do:

page=EntityLoad("page",
{alias=request.id,lang_code=request.lang_code},true); <-- Returns the
current page and all of its contents
page.getcontents({col=0});

This should return only content in col 0. The problem is, that col is
a property of content and not of page.

For better reading i postet my page.cfc and content.cfc on
pastebin.com:

Content.cfc http://pastebin.com/f7585415d
Page.cfc http://pastebin.com/f67327d9b

Cheerio
slashwalker

On 18 Jan., 17:15, Bob Silverberg <bob.silverb...@gmail.com> wrote:
> It sounds a bit early to give up, but that's obviously your call. I imagine
> that you could probably do something in your Page.cfc to deal with the
> issue. If you can distill your problem down to a few lines of code which
> demonstrate exactly what you want to do (and only that) then we might be
> able to come up with some more suggestions.
>
> Cheers,
> Bob
>

Bob Silverberg

unread,
Jan 18, 2010, 1:18:07 PM1/18/10
to cf-or...@googlegroups.com
There are a number of different approaches you could take to this, but I don't think any of the built-in hibernate features will give it to you "out of the box", although I could be wrong about that.  Some ideas:

1. Use an HQL query, rather than EntityLoad, to return the contents.
- You could then pass criteria into the query to grab only contents for a certain col.  However, that probably wouldn't be performant, as you'd need to issue as many queries as columns.
- Or, you could return everything in your HQL statement, but sort it based on col, in which case you might be able to use the sorted results as you output your page without having to loop multiple times.
You could put either of these calls into your Page object (or a Service/Gateway injected into the Page object) which would allow you to call a method on the Page object to return just what you need.

2. Create a method on your Page object that allows you to pass in a value for col, and returns the corresponding set of Content objects.
This could be implemented a number of different ways. It could involve a simple loop of the results of getContents. If you think you need to squeeze out more performance, then you could do the loop once (perhaps on init()), cache the info in a struct, and then return data from that struct when requested.  Then, when outputting your page, rather than using something like Page.getContents({col=0}), you'd do something like Page.getCols(0).

These ideas are just ottomh - I'm sure there are even better ways of doing it, but the point is that you can probably find a way of doing this in an object-oriented fashion, by allowing the Page object to take responsibility for its collection of Content objects, rather than simply resorting to cfquery.

Cheers,
Bob

--
You received this message because you are subscribed to the Google Groups "cf-orm-dev" group.
To post to this group, send email to cf-or...@googlegroups.com.
To unsubscribe from this group, send email to cf-orm-dev+...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/cf-orm-dev?hl=en.



slashwalker

unread,
Jan 19, 2010, 11:08:21 AM1/19/10
to cf-orm-dev
Hello Bob,
thanks for your suggestions. I now added the following function to my
page.cfc:

<cffunction name="getContentByCol" access="public" returntype="any">
<cfargument name="rid" required="yes">
<cfargument name="lang_code" required="yes" default="de">
<cfargument default="0" name="col" required="yes">
<cfset temp= EntityLoad("page",
{alias=arguments.rid,lang_code=arguments.lang_code},true)>
<cfset c=structnew()>
<cfset i=1>
<cfloop array="#temp.getContents()#" index="item">
<cfif item.getcol() eq arguments.col>
<cfset c[i]["header"]=item.getheader()>
<cfset c[i]["bodytext"]=item.getBodytext()>
<cfset c[i]["includefile"]=item.getincludefile()>
<cfset c[i]["contentid"]=item.getcontentid()>
<cfset c[i]["cfml"]=item.getcfml()>
<cfset i++>
</cfif>

</cfloop>
<cfreturn c>
</cffunction>

Not the nicest code I ever wrote, but it works as needed ;)

Cheerio
slashwalker

> On Mon, Jan 18, 2010 at 12:22 PM, slashwalker <slashwal...@googlemail.com>wrote:
>
>
>
> > Hello Bob,
> > okay, here a very simple example:
> > I should be able to do:
>
> > page=EntityLoad("page",
> > {alias=request.id,lang_code=request.lang_code},true); <-- Returns the
> > current page and all of its contents
> > page.getcontents({col=0});
>
> > This should return only content in col 0. The problem is, that col is
> > a property of content and not of page.
>
> > For better reading i postet my page.cfc and content.cfc on
> > pastebin.com:
>
> > Content.cfchttp://pastebin.com/f7585415d

> > Page.cfchttp://pastebin.com/f67327d9b

> > cf-orm-dev+...@googlegroups.com<cf-orm-dev%2Bunsu...@googlegroups.com>


> > .
> > For more options, visit this group at
> >http://groups.google.com/group/cf-orm-dev?hl=en.
>
> --

Bob Silverberg

unread,
Jan 19, 2010, 3:26:39 PM1/19/10
to cf-or...@googlegroups.com
That's the general idea.  A couple of suggestions:

1. You shouldn't need to use EntityLoad or that temp object at all.  This code is in Page.cfc, so when the code is executing it is already running in the context of a given page.  So you'd do your entityLoad first, and then you would call the getContentByCol() method on that Page object.  That would change your code so that instead of "temp" you'd use "this".  E.g., 

<cfloop array="#this.getContents()#" index="item">

2. I wouldn't bother taking values from the Content object and copying them to a structure, I'd just return the Content objects themselves.  Just loop through this.getContents() and add any valid Content objects to an array and then return that array. Then you'd have an array of Content objects for the given col that you can use when you render your page.

One other thing, I realized while writing this that perhaps using Hibernate filters would be a good solution. I've been doing some experimentation with them, and they will work, but I have performance concerns.  I'm going to do a bit more experimenting and then perhaps post some questions/observations to the group about them.

Cheers,
Bob

To unsubscribe from this group, send email to cf-orm-dev+...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/cf-orm-dev?hl=en.



Bob Silverberg

unread,
Jan 19, 2010, 4:29:32 PM1/19/10
to cf-or...@googlegroups.com
I put together a blog post describing how to use Hibernate filters to do what you've described.  After doing some testing I'm not sure that they would be the best approach from a performance perspective, but it's still an interesting approach. More info available at: http://www.silverwareconsulting.com/index.cfm/2010/1/19/Filtering-Collections-in-ColdFusion-ORM

Cheers,
Bob

On Tue, Jan 19, 2010 at 3:26 PM, Bob Silverberg <bob.sil...@gmail.com> wrote:
That's the general idea.  A couple of suggestions:

1. You shouldn't need to use EntityLoad or that temp object at all.  This code is in Page.cfc, so when the code is executing it is already running in the context of a given page.  So you'd do your entityLoad first, and then you would call the getContentByCol() method on that Page object.  That would change your code so that instead of "temp" you'd use "this".  E.g., 

<cfloop array="#this.getContents()#" index="item">

2. I wouldn't bother taking values from the Content object and copying them to a structure, I'd just return the Content objects themselves.  Just loop through this.getContents() and add any valid Content objects to an array and then return that array. Then you'd have an array of Content objects for the given col that you can use when you render your page.

One other thing, I realized while writing this that perhaps using Hibernate filters would be a good solution. I've been doing some experimentation with them, and they will work, but I have performance concerns.  I'm going to do a bit more experimenting and then perhaps post some questions/observations to the group about them.

Cheers,
Bob



Reply all
Reply to author
Forward
0 new messages