Skip to navigation

How To Use Custom View to Output Different Content Types in Concrete5.7, Part 1

Written by and published on
Note There may be a better way to achieve the same thing with less effort, but I couldn't find one.

Creating XML output in Concrete5 is relatively easy: you just need a custom template without the usual HTML header and footer, and you’re good to go. Except… with templates C5 uses text/html mime type by default, and as I’m writing this I’m not aware of any way to override it.

In a previous post I outlined how to output JSON with the correct mime type, and it included using a custom response. We can use the same technique here, but since building XML in the controller is a pain (and wrong), we are going to use a template file too. But because the Views in Concrete5 are very HTML oriented, we are going to need a custom View.

Let’s get to work. Here’s what we need:

File Purpose
application/themes/[theme]/rss.php The Theme Template
application/controllers/single_page/feed.php The Page Controller
application/views/rss_items.php The View Template
application/src/View/RawView.php Our custom, content-type agnostic View

The Theme Template

We start by creating a theme template for our RSS feeds:

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title><?php echo $feedTitle; ?></title>
    <description><?php echo $feedDescription; ?></description>
    <?php echo $innerContent; ?>
  </channel>
</rss>

We are going to set $feedTitle and $feedDescription in the controller, and they will be available in our template when we render it.

Probably not the smartest way to do it, but it serves our example well enough. Just remember to register it in the Dashboard.

The View Template

Next up is our view template, or inner content template/file (I admit I’m having trouble keeping up with the official names for these files):

<?php foreach( $items as $item ): ?>
  <item>
    <title><![CDATA[<?php echo $item->title; ?>]]></title>
    <description><![CDATA[<?php echo $item->description; ?>]]></description>
    <link><?php echo $item->url; ?></link>
    <pubDate><?php echo $item->pubDate; ?></pubDate>
  </item>
<?php endforeach; ?>

We have a hypothetical list of items and loop through it to create the actual meat of the file. (We are assuming that pubDate is already in the correct format.)

The Page Controller

The third step is to create a Page Controller for our feed. We’ll keep it simple:

<?php
namespace Application\Controller\SinglePage;

use Application\Src\View\RawView;
use PageController;
use Response;

class Feed extends PageController
{
  // Use the rss.php template in our theme folder
  protected $themeViewTemplate = 'rss';

  /**
   * Main view action used by default when this page is viewed.
   *
   * @param   string    $category  The category of the requested feed
   * @return  Response             A Response object
   */
  public function view( $category = null ) {
    // Get the category param from the request. All the rest is just
    // let's pretend stuff.
    $items = $this->fetchItemsForCategory( $category );
    if ( count( $items ) > 0 ) {
      $this->set( 'items', $items );
      $this->set( 'feedTitle', $category );
      $this->set( 'feedDescription', $this->getCategoryDesc( $category ) );
      // Render the view using the default view and the theme template
      // we have set in self::themeViewTemplate
      $xml = $this->renderFeed();
      // Send the response
      return $this->send( $xml );
    } else {
        return $this->send( 'Unknown category', 422 );
    }
  }

  /**
   * Renders the feed content.
   *
   * @return  string  Rendered content
   */
  protected function renderFeed() {
    // $this gets passed to the view's constructView() method
    $view = new RawView( $this );
    // Set the file we want to use for rendering the content
    $view->setContentFile( 'rss_items' );
    // Render the view
    return $view->render();
  }

  /**
   * Generates a Response object with the correct content-type for RSS feed.
   *
   * @param   string     $content     The resoponse body
   * @param   int        $statusCode  HTTP status code for this response
   * @return  Response
   */
  public function send( $content, $statusCode = 200 ) {
    // Simplistic solution for this example.
    $mime = $statusCode == 200 ? 'application/rss+xml' : 'text/html';
    return new Response(
      $content,
      $statusCode,
      [ 'Content-Type', $mime ]
    );
  }

  /**
   * A mock method for fetching feed items.
   *
   * @return  array  An array of feed items.
   */
  protected function fetchItemsForCategory() {
    return [
      (object)['title' => 'Item 1', 'description' => 'Description 1', 'url' => 'example.com', 'pubDate' => '2016-01-01' ],
      (object)['title' => 'Foobar', 'description' => 'Flowery', 'url' => 'saimiri.io', 'pubDate' => '2016-03-29' ]
    ];
  }

  /**
   * A mock method for returning a description for a category.
   *
   * @param   string  Feed category
   * @return  string  Category description
   */
  protected function getCategoryDesc( $category ) {
    return ucfirst( $category ) . ' is a wonderful category';
  }
}

Remember to register this in the Dashboard, too.

We set the namespace as usual and use PageController, Response and the View\RawView we are about to create. The view() method is called automatically when our feed page is accessed. We use $category parameter to get the category from the URL, which should be in the form of www.example.com/feed/mycategory.

The RenderFeed() simply creates a new View object and calls its render() method, passing the content file we wish to use as an argument. The variables we have set() in the PageController are automatically extracted during the rendering.

Once the view is rendered and we have the final content, we send it using a Response object with a custom Content-Type header that matches the mime type of our XML content.

In the next part, we’re going to create the custom View.

Comments

Commenting has been disabled until I get a proper spam protection working. =(

External Links

Back to beginning