Skip to main content

Drupal 9: Creating a perfect rss feed page (based on the validation score from w3c)

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;
  }
}

 

custom module php rss