How To Use Custom View to Output Different Content Types in Concrete5.7, Part 1
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. =(