I have been in several situations where I have wanted a “complex data source” in Symphony. A couple of examples of what I mean by a complex data source are:

  • Retrieve entries from more than one section, if no entries are found in both sections, show 404 page.
  • Retrieve entries from a single section where one field is equal to a URL segment, OR another field is equal to that URL segment. If no entries are found, redirect to 404.

As far as I can tell, Symphony has no nice way of dealing with this situation.

I found an example of this problem while build this Blog - I wanted visitors to either see a summary of ALL posts, or a summary of posts for a specific tag. Easy enough, but I also wanted to share the URL for tags AND posts -

http://jamesmorrish.co.uk/blog/ - show a summary of the latest blog posts

http://jamesmorrish.co.uk/blog/tag-name/ - show a list of posts whose tag matches the URL segment

http://jamesmorrish.co.uk/blog/post-name/ - show the blog post whose title matches the URL segment

http://jamesmorrish.co.uk/blog/anything-else/ - redirect to 404 page

Blog Datasource

When setting up a datasource using multiple filters, you are effectively performing an AND query. For example “SELECT * FROM blog_entries WHERE TITLE = ‘URL_Segment’ AND tags like (‘%URL_Segment%’)

(Tags are not actually stored as a comma delimited string in Symphony, but you get the idea)

So we have to create two data sources - one to retrieve posts where the tag matches the URL segment, and one to retrieve a single post where the name matches the URL segment.

In Symphony you can easily redirect to 404 if a single data source returns no results, but there is nothing in Symphony to allow you to redirect to 404 if no results are found in two data sources.

The solution

A custom DataSource.

I created the data source “Latest Blog Posts”, which retrieves the three most recent posts for the tag specified in the URL. If no tag is specified, the DataSource retrieves the three most recent posts for any tag. I set the “Parameter Output” option in the data source settings to “System ID”.

I then created the data source “Blog Post” - this retrieves a single blog post where “Title” matches the URL segment.

By selecting “System ID” as the output for “Latest Blog Posts”, by customising “Blog Post” we can determine whether any posts were retrieved with a matching tag OR title.

So finally, to customise the “Blog Post” data source, I made two changes:

  1. I adding the following line to the Constructor which tells Symphony to evaluate “Latest Blog Posts” before running the custom “Blog Post”:

    $this->_dependencies = array(‘$ds-latest-blog-posts’);

  2. I edited the grab function as follows:

    public function grab(&$param_pool=NULL){
        $result = new XMLElement($this->dsParamROOTELEMENT);
        try{
            // grab any blog post that matches the URL segment
            include(TOOLKIT . '/data-sources/datasource.section.php');
    
            $resultXML = simplexml_load_string($result->generate());
    
            // if there is a URL segment, AND if no blog post matches the URL segment, AND the "Latest Blog Posts" data source returns no entries
            if (($this->{'_env'}['env']['url']['title'] > '') && isset($resultXML->error) && ($this->{'_env'}['env']['pool']['ds-latest-blog-posts'][0] == '')) 
    
            // redirect to 404
            FrontendPageNotFoundExceptionHandler::render('404');
        }
        catch(FrontendPageNotFoundException $e){
            FrontendPageNotFoundExceptionHandler::render($e);
        }           
        catch(Exception $e){
            $result->appendChild(new XMLElement('error', $e->getMessage()));
            return $result;
        }
    
        if($this->_force_empty_result) $result = $this->emptyXMLSet();
        return $result;
    }
    

Finally…

This is a scrappy solution to a common problem. Hopefully with the release of Symphony 3 next year, hopefully this functionality will be achievable in a more elegant way.

« Back