19 - Implementing the Work Section - Part 3

This installment of the Build a Portfolio Site series brings us to what’s arguably the meat of the site - the project template of the Work section. This is where as a creative agency we get to cover in detail the solutions we have created for other clients and hopefully convince a site visitor that we are good enough, strong enough, and doggone it - people like us!

 
Download the EE Code for 19 - Implementing the Work Section - Part 3

Modifying my Content Model
When I started writing this section I loaded up the page-comps/project template to see what I was in for as the EE developer tasked with bringing the site to life. The first thing I noticed is that hey - that header image area is a slider and can handle multiple images. I hadn’t accounted for that in my content model, which only expected one image.

Dangit.

What to do? I saw two options:

  • Convert the existing {work_image} image field into a grid field so that it can accept multiple images, and then assume the first image in the grid was the one to use for the Work & Home page indexes.
  • Create a new grid field to power the header on the work/project template and use the existing {work_image} field for the Work and Home pages.

A second field felt a little less elegant from a developer perspective. However, the images have different uses and clients may want a different crop on the thumbnails. I also wasn’t comfortable that the first image in a grid would always be the “right” one for thumbnail use. Creating a second channel field also meant my existing code on the work/index page wouldn’t have to change.

Either approach has merits but in the end I chose to implement a second image field using the Grid fieldtype to power the header images on the work/project page. The Grid field will have one column for the image file and a column for the title/alt text.

Related Projects
At the bottom of the Project template there is a list of Related Projects. How to handle that? Again, we are working with EE so there are always options. The first option is to simply use the categories that we have already assigned to the Work channel. When viewing a project assigned to the Photography channel, the Related Projects area could show other projects in the Photography category.

But the project might be related in a different way than just category. It might feature a similar photography style, be for the same client, shot in the same location, or “relate” in any other number of ways.

I wanted more than a laundry list of “here’s a bunch of other stuff we hope you like”. So often “related” sections just feel random, unhelpful and kind of spammy. I wanted a way to have related projects but also be able to explain the relationship of the listed project to the main project being viewed.

I decided to add another Grid field with a column for the relationship and a column for a textbox to describe the relationship.

Adding the Fields
Here are the details on the two fields to add:

  • Grid field, named {work_gallery} and having two columns. The first column is a File field named {gallery_image_file}, allowed field types set to Image, and Allowed Directory set to Work Image. The second column is a Text Input field named {gallery_image_title} with no formatting added.
  • Grid field, named {work_related_projects} and having two columns. The first column is a relationship field named {related_project}, limited to open entries in the Work channel, and set to allow only one relationship. The second grid column is a textarea named {how_related} with no formatting added.

Edit Content
With the new fields added you’ll need to update your existing content to assign some images to the gallery and choose and describe some related projects.

Create the Detail Template
In your work template group create a new template named project. The complete template is available in the Companion Files linked to below - I want to step through it block by block and talk about a few aspects of it.

Here’s the block that pulls in our HTML header and sets the page title:

{embed="embeds/header" my_body_class="" my_active_nav="work" my_page_title="Our Work | {exp:channel:entries channel="work" limit="1" disable="member_data|pagination" dynamic="no" url_title="{segment:project}"}{title}{/exp:channel:entries}"

The big takeway here is that yes, you can have complete {exp:channel:entries} loops inside of an embed statement. The purpose here is to get the title of the entry and pass that off to embeds/header to set the browser title. You could use more fields than just title and pass off meta data if you wanted to, etc.

Admittedly - running an entire {exp:channel:entries} loop for this purpose isn’t ideal and there are a few ways around it. Natively we could look at using the recently-added Template Layouts and the 3rd party offers options like Stash. However we are a good ways into the development of this site and switching template implementation approaches would mean a lot of rework. I chose to move ahead with this approach and see how many queries the finished template required. The result was acceptable so I’m sticking with this code, knowing that on an actual client site I could look at reworking things if the performance didn’t prove acceptable.

But - why did the header code need its own {exp:channel:entries} loop to begin with? This whole template is designed to only spit out one entry.

Next/Previous Links
Next up is the culprit. This is the EE code that generates the “Previous Project” and “Next Project” links in the page header:

<!-- project pager -->
<
div class="project-pager cf">
    
{!-- Previous and next tags can't be nested inside a channel:entries loop --}
    {exp:channel:prev_entry channel="work"}
        <a class="previous-project" href="{route="work/project" project="{url_title}"}">&#8592; Previous project </a>
    {/exp:channel:prev_entry}    
    {exp:channel:next_entry channel="work"}
        <a class="next-project" href="{route="work/project" project="{url_title}"}">Next project &#8594;</a>
    {/exp:channel:next_entry}
</div>
<!-- ENDS project pager --> 

These EE tags let us easily handle the Next/Previous links, but the limitation with these tags is that they can’t be nested inside of an {exp:channel:entries} loop. This is what caused me to create one loop for getting the title tag, then below these tags have another {exp:channel:entries} loop to pull the rest of the page content. Another option would be to go back to the client and ask “how important are these links?” It could be that they are just UI cruft and by deleting them we could code the page to not only load faster but also be cleaner/simpler to the end user.

Notice the linking code? Intead of using our trusty {url_title_path} I’m instead using a {route} variable. More on that later - but the end goal is to load our projects at simple URLs that drop the /project/ template name from the URL.

Back to the main loop for the rest of the content on the page:

{!-- Open up the main entry loop again for rest of page content --}
{exp
:channel:entries channel="work" limit="1" disable="member_data|pagination" dynamic="no" url_title="{segment:project}" require_entry="yes"

The limit parameter just ensures that this page only ever loads one entry. Turning dynamic off and setting the url_title parameter allow us to use the Template Routes feature. The require_entry parameter sets up our 404 logic which we follow up with:

{if no_results}
    {redirect
="404"}
{
/if} 

Later on in this tutorial series we’ll create a dedicated 404 page that this code will load.

Grid Field Code
Moving on you’ll find this code:

{work_gallery}
    {if work_gallery
:count=="1"}
    
<!-- slider -->
    <
div class="project-slider">
        <
div class="flexslider">
            <
ul class="slides">
    
{/if}

    
<li>
    
{!-- remember to namespace variables inside a grid field loop --}
    
<img src="{work_gallery:gallery_image_file:main}" alt="{work_gallery:gallery_image_title}" />
    </
li>

    
{if work_gallery:count==work_gallery:field_total_rows}    
            
</ul>
        </
div>
    </
div>
    <!-- 
ENDS slider -->
    
{/if}            
{
/work_gallery} 

This is the Grid field code. Grid fields are looping pairs, and in between the looping pair variables are “namespaced” which means you preface the variable name with the Grid field name. This is done so that you can intermix like-named variables from outside the loop with variables inside the loop. The {count} variable, for example, exists in both the Grid field and in the parent {exp:channel:entries} loop that we are also in. With namespacing you can access both count variables within the Grid loop if you wanted to.

The other thing going on in this Grid loop is a bit of error-prevention. As the EE developer you often have to ask the question - what if this particular bit of content isn’t entered?  On the entry side you can make fields required, but in practice I find that there are always exceptions that make this a poor choice. Better to handle it on the template side if we can.

In this case we use two conditional clauses to prevent empty slider markup from being rendered if there are no images assigned to the project. The first conditional:

{if work_gallery:count=="1"

will only be true if there is at least one entry and will only be true on the first loop if there are more than one.

The second conditional:

{if work_gallery:count==work_gallery:field_total_rows} 

will find the last iteration of the loop and render all the closing HTML at that time.

The next interesting bit of code is the Grid field for Related Projects. This Grid field contains a Relationship field so the value of namespacing variables becomes more apparent:

Grid Field with Relationship Field Code

{!-- Work related projects is another grid field type so needs looping pair --}
{work_related_projects}
    {
!-- only return the markup if there are related projects --}
    {if work_related_projects
:count=="1"}
        
<!-- related -->
                <
div class="related-projects">
                    <
h4 class="related-heading">Related projects</h4>
                    <
div class="related-list cf">    
    
{/if}

    
<figure>
        
{!-- Related project is a relationship field inside our Grid field
        
namespacing is really important here to keep sane --}
        {work_related_projects
:related_project}
            
<a href="{route="work/project" project="{work_related_projects:related_project:url_title}"}" class="thumb"><img src="{work_related_projects:related_project:work_image:related}" 
            
alt="{work_related_projects:related_project:title}" /></a>

            <
figcaption>
                
{!-- Namespacing allows grabbing of how_related while still inside of relationship field tag pair.--}
                {work_related_projects
:how_related}
                
<a href="{route="work/project" project="{work_related_projects:related_project:url_title}"}">Details >></a>
            </
figcaption>
        
{/work_related_projects:related_project}
    
</figure>

    
{!-- Conditional will only run the last iteration of the loop and closes HTML out --}
    {if work_related_projects
:count==work_related_projects:field_total_rows}
            
</div>
        </
div>
        <!-- 
ENDS related -->
    
{/if} 

We end up with variables of different depths depending on where we are in the loop:

  • Two segment variables for the basic Grid fields: {grid_field_name:grid_column_name} - {work_related_projects:how_related} in our case.
  • Three segment variables for values inside the Relationship field inside the Grid field: {grid_field_name:relationship_column_name:related_field_name} - {work_related_projects:related_project:url_title} is one of these.
  • Four-segment variables for pulling image manipulations through a Relationship field stored inside a Grid field:{grid_field_name:relationship_column_name:related_field_name:image_manipulation_name} -{work_related_projects:related_project:work_image:related} in our code.

This chunk of code is a bit more complex due to being nested three levels deep - but the basic “error-checking” approach is the same here with the goal of not returning any Related Projects markup if there are no related projects to show.

Creating the Template Route
If you are new to Template Routes you can first visit the documentation for them on EllisLab.com. Basically they are a way to define URL patterns and map those patterns to specific templates in your EE installation.

In our case using native ExpresssionEngine URLs our Project detail pages would have URLs like:

http://mysite.com/index.php/work/project/website-for-acme
http://mysite.com/index.php/work/project/photos-for-obama 

My preference is to simplify those to:

http://mysite.com/index.php/work/website-for-acme
http://mysite.com/index.php/work/photos-for-obama 

This is where Template Routes come in.  I’ve covered the basics of setting one up in another tutorial so wont repeat all the detail here.

Basically you need to make sure Template Routes are turned on in the CP at Design > Template Manager > Global Template Preferences. Then in Design > Template Manager > Template Route Manager specify the following route for the new work/project template:

/work/{project:alpha_dash} 

Make sure to select Yes for ‘Require All Segments”.

Update the Work Index Template
On the work/index template find links that use the url_title_path and replace them with links using the route variable:

Find this:

<a href="{url_title_path='work/project'}"><h3 class="heading">{title} </h3></a

Replace with:

<a href="{route="work/project" project="{url_title}"}"><h3 class="heading">{title} </h3></a

There are linked images that need a similar update (and updated work/index template is included in the Companion Files).

Whew!
This was a longer chapter but I wanted to knock off the rest of the Work section. The attached Companion Files include the work/index template, work/project template and an updated stylesheet. The bulk of the site work is now done - the main things left are getting the Home page functional and site footer all running dynamically.

Category Navigation

<< Previous Entry   

Next Entry >>

 

Add Your Comment

Commenting is not available in this channel entry.

Unless otherwise stated all content is © Michael Boyink of Train-ee.com & Boyink Interactive. Please don't steal - I've got kids to feed...

dy>