Drupal has a great and easy way of adding rss feeds via the views module. Unfortunately when you check your rss feed at https://validator.w3.org/feed/check.cgi various warnings come up and this was not so good for a recent client that needed a perfect w3 validation result. This is how i got a perfect score by building a custom rss controller page and testing until i get the perfect score at https://validator.w3.org/feed/check.cgi.
For generating quickly a new custom controller, i used the drupal console.
drupal generate:controller
Then i added a zero caching option for the routing path of my controller in order to be sure that we always get the latest data (the rss feed was linked with a social media sharing service).
pixelthis.latest_rss_index_rss:
path: '/latest_news.rss'
defaults:
_controller: '\Drupal\pixelthis\Controller\LatestRssController::index_xml'
_title: 'Latest News'
options:
no_cache: 'TRUE'
requirements:
_permission: 'access content'
options:
no_cache: 'TRUE'
With this option we make sure that we will always show the latest (time desc) data.
Then in my controller (web/modules/custom/src/Controller/LatestRssController.php) i wrote this code
<?php
namespace Drupal\pixelthis\Controller;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Controller\ControllerBase;
use Drupal\media\Entity\Media;
use Drupal\image\Entity\ImageStyle;
use Symfony\Component\HttpFoundation\Response;
/**
* Class LatestRssController.
*/
class LatestRssController extends ControllerBase {
/**
* Index.
*
* @return Object
* Index XML RSS DATA
*/
public function index_xml() {
$response = new Response();
$defaultPubDate = DrupalDateTime::createFromTimestamp(
time()
);
$header = '<?xml version="1.0" encoding="utf-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel xmlns:media="http://search.yahoo.com/mrss/" xml:base="https://www.awesome-website.gr/latest_news_xml">
<link>https://www.awesome-website.gr/latest_news_xml</link>
<title>Awesome FM RSS</title>
<description/>
<atom:link href="https://www.awesome-website.gr/latest_news_xml" rel="self" type="application/rss+xml" />
<lastBuildDate>' . $defaultPubDate->format('r') . '</lastBuildDate>
<generator>Pixelthis</generator>';
$content = '';
$nids = $this->getLatestArticles();
$content_data = $this->buildRssArticleFormat($nids);
foreach ($content_data as $row) {
$content .= '<item>' . implode('', $row) . '</item>';
}
$footer = '</channel></rss>';
$response->headers->set('Content-Type', 'text/xml');
$response->setContent($header . $content . $footer);
return $response;
}
/**
* Get the Latest Articles
*
* @return array
* nids of the articles
*/
private function getLatestArticles() {
$query = \Drupal::entityQuery('node')
->condition('type', 'article')
->condition('status', 1)
->condition('field_article_features', '3')
->condition('created', time(), '<=') //Do not show articles with a future date
->sort('created', DESC)
->range(0, 10);
$result = $query->execute();
return $result;
}
private function buildRssArticleFormat(array $nids) {
$rssData = [];
$nodes = Node::loadMultiple($nids);
$width_default = 1150;
$height_default = 674;
foreach ($nodes as $node) {
$first_cat = $node->get('field_main_category')->first()->target_id;
$cat_term = Term::load($first_cat);
$defaultPubDate = DrupalDateTime::createFromTimestamp(
$node->getCreatedTime()
);
$media = Media::load($node->get('field_media')->target_id);
$uri = $media->field_media_image->entity->getFileUri();
$image_style = ImageStyle::load('article_');
$build_url = $image_style->buildUrl($uri);
$image_factory = \Drupal::service('image.factory')->get($build_uri);
$height = $image_factory->getToolkit()->getHeight() ? $image_factory->getToolkit()->getHeight() : $height_default;
$width = $image_factory->getToolkit()->getWidth() ? $image_factory->getToolkit()->getWidth() : $width_default;
$rssData[$node->id()] = [
'guid' => '<guid isPermaLink="true">' . $node->toUrl('canonical', ['absolute' => TRUE])->toString() . '</guid>',
'link' => '<link>' . $node->toUrl('canonical', ['absolute' => TRUE])->toString() . '</link>',
'category' => '<category>' . $cat_term->label() . '</category>',
'title' => '<title>' . $node->label() . '</title>',
'pubDate' => '<pubDate>' . $defaultPubDate->format('r') . '</pubDate>',
'description' => '<description>' . preg_replace("/\r|\n/", "", $node->get('body')->summary) . '</description>',
'media:thumbnail' => '<media:thumbnail url="' . $build_url . '" width="' . $width . '" height="' . $height . '"/>',
];
}
return $rssData;
}
}