To learn all about Skin Spider, click here.
To view the most updated application, click here.
To view the current code base, click here.
Until now, the only thing we set programmatically about the page was a variable called REQUEST.Page. We used this value to determine which primary navigational element was turned on. This was nice and simple, but perhaps a bit limiting. As any body who does web development knows, page titles are very important both for usability and for search engine optimization. And, I'm not just talking about the H1 tag, I'm referring to the TITLE tag as well. We should be setting these values.
In order to do this, we need to store more information than just REQUEST.Page. And in order to do that, we need some sort of structure that will hold page data. Now, I am not 100% comfortable with how I went about doing this, but it works for the moment. In the future we can always change it.
I built a ColdFusion component called PageData.cfc. Right now, it has the following methods:
I am creating the PageData.cfc in the OnRequestStart() event method of the Application.cfc ColdFusion component. This is where we used to set the REQUEST.Page variable. Notice also that I am adding a default title for the application. By doing this, we ensure that every page title will have the application name in it. While that doesn't mean that much for Skin Spider, that means a whole lot to any site that cares about search engine optimization.
Now, if you look at the PageData.cfc code, you will see that it keeps a queue of Titles and Sections. The title queue allows use to build the title as we drill down through the site. Similarly, the section queue allows us to keep track of where we are as we drill down through the site.
The titles are fairly obvious, but the sections are not as clear. To me, a section is more or less a logical grouping of functionality. Take a look at the _header_standard.cfm template. Notice that in the primary navigation of the site, I am now checking the value of the section at index "1" of the section queue. That's because to me, the primary navigation represents the first level of logical group functionality. The usefulness of this might be a bit lost on Skin Spider as it is a very simple system. But, try to imagine a system where you have nested groups (ex. CMS > Offices > Edit). Once you get into nested grouping, being able to queue the sections will become very useful.
Additionally, if you look in the _header_standard.cfm template and the _header_pop.cfm template, you will see that I am getting the page title for the TITLE tag out of PageData.cfc object via the GetTitle() method. This method joins all the queued titles into one string using the default " : " delimiter or one passed in (I am not passing one in). The ColdFusion component builds the title by looping backwards over the titles queue. It loops backwards because, more often than not, the last title entered is usually the most page-relevant. In our case this is true. And, notice that since we added the site name as the first queued title, the site name will be the last part of the page title for all pages.
Now, once, I started adding the page title of any given page to the PageData.cfc, I was repeating myself. I was setting the title into the PageData.cfc AND I was setting the title directly into the H1 on each individual page. This violates the DRY (don't repeat yourself) principle. We shouldn't have to set the page title twice. This is extra work and leaves more room for errors and inconsistency. To keep it DRY, I removed the H1 tag from the individual pages and put it into the header files (_header_standard.cfm and _header_pop.cfm). Now, in the header files, the H1 is set to contain whatever the last queued title is. It gets this through the PageData.cfc's method GetLastTitle().
Again, I am not 100% I like this methodology. What I do like though, is that it draws a distinction between the server page and the web page. The PageData.cfc takes care of information needed to properly render the web page (stuff the user sees). It doesn't get involved with which headers are included, what pages are executed... that to me is all server-page stuff. Ok, so that's a really horrible explanation of what I am seeing in my head. Perhaps as we get farther that will become more clear: the separation between application-relevant data and web-page-relevant data.
You might want to look at a page like an object as any other. That way you could use the same code (say base classes) to access/persist/filter/whatever your pages and it also makes it easier to throw together a cms allowing end users to list, add, edit and delete pages. Rather than getXbyindex and getYbytitle, I'd think getbyUniqueAttribute(AttributeName,AttributeValue) returning a page object so you get then getTitle() and getSection() and getWhateverElse() from your page object. It scales better as if you had (say) 3 ways of getting a page and 10 attributes down the line, you'd end up with 30 getters. This way you only need getPageByA(), getPageByB() and getPageByC() extending the getbyUnbiqueAttribute() base method and your 10 getters (or one generic getter if you'd like to use those in an IBO :->).
I am not sure that I am following what you mean. I don't treat a page as an object because, to me, a page is output; it's not a collection of functionality. I am very hesitant about putting output only type code into an object. What I am talking about in this post is information about the page, not the page itself.
It's hard to get into it since Skin Spider is such a simple project. What I was envisioning is that let's say I had a CMS that was split of up into application modules. In the "CMS" module, I was editing the contact information for a staff contact. This, to me, is nested functionality and each nested section would be a queued sections:
Sections[ 1 ] :: "cms"
Sections[ 2 ] :: "staff_contact"
Sections[ 3 ] :: "edit"
Sections[ 4 ] :: "contact_information"
Then, as the page rendered in the header file, each type of navigation would use the queued section to figure out how to render itself (ie. what is on, what is off, what is visible):
Primary Navigation :: Sections[ 1 ]
Secondary Navigation :: Sections[ 2 ]
Tertiary Navigation :: Sections[ 3 ]
Quaternary Navigation :: Sections[ 4 ]
What I do NOT like about this is that it hard codes the type of navigation to the index level of the section. I already know that this is NOT going to work in the long run. The coupling of the nav type to the index is high point of coupling and not flexible enough to handle a complex system.
If you could maybe explain your solution a bit more, I might be able to see where I can improve upon my model or just switch over to what you are saying.
Sorry for delay - was sick :-<
So, imagine a page object which is not the stuff sent to the browser, but just a set of properties and methods that relate to page display. I like to allow site admins to add, edit and delet pages, setting the various properties, I store the pages in the db like I store users and products and invoices and treat them as another business object like any other (so my site map would just be a pageservice.getall()). It allows me to find pages by any unique attribute (page ID, filepath, etc.) using the same base methods I use to do everything else. Once I get back up to speed I'll post on how I do pages and see if it makes more sense!
As always, looking forward to your posting. I am very curious to see what you have to say. Building a page from the top down on the server is extremely easy (that is, figuring out what the MAIN content is supposed to be). Building a physical page layout is, to me, somewhat more complicated to do in an elegant way.