1

This is my first post - so hello everyone :) I am trying to write a custom module that displays a certain number of posts from a certain category. I need a few of these blocks to display posts from different categories. The purpose of this is to create a custom theme. I figured it would be easier to do this with the module than with the views - the theme is quite complex. Besides, I want to exclude posts that will be displayed in the block with the latest 4 posts from different categories from showing in each block later. I created a controller:

<?php

namespace Drupal\fashion\Controller;

use Drupal\Core\Controller\ControllerBase;

class FashionController extends ControllerBase {
  
  public function FashionQuery() {

    $database = \Drupal::database();
    $query = $database->select('node_field_data', 'n');
    $query->join('node__field_news_category', 'nc', 'n.nid = nc.entity_id');
    $query
      ->fields('n', ['nid', 'type', 'created', 'title'])
      ->fields('nc', ['entity_id'])
      ->condition('nc.field_news_category_target_id', '2')
      ->condition('n.type', 'news')
      ->range(0,4);

    $result = $query->execute();

    return $result;
   }

}

Then I created a block:

<?php

/**
 * @file
 * Creates a block displays Fashion created in FashionController.php
 */

namespace Drupal\fashion\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\fashion\Controller\FashionController;

/**
 * Provides the Fashion main block.
 * 
 * @Block(
 *   id = "fashion_block",
 *   admin_label = @Translation("Fashion Block Main"),
 *   category = @Translation("Custom Block"),
 * )
 */
class FashionBlock extends BlockBase implements BlockPluginInterface {

  /**
   * {@inheritdoc}
   */
  public function build() {
      
    $fashion_variable = new FashionController;
    $render_fashion_ids = $fashion_variable->FashionQuery();
    
    return [
      '#theme' => 'fashion_block',
      '#variables' => [
        '#ids' => $render_fashion_ids->entity_id,
        '#titles' => $render_fashion_ids->title,
      ],
    ];
  }
}

And a .module file:

<?php

/**
 * Implements hook_theme().
 */
function fashion_theme($existing, $type, $theme, $path) {
  return [
    'fashion_block' => [
      'variables' => [
        'ids' => NULL,
        'titles' => NULL,
      ],
    ],
  ];
}

and twig file (for now only for check if it works):

<ul>
    {% for id in ids %}
        <li>{{ id }}</li>
    {% endfor %}
    {% for title in titles %}
        <li>{{ title }}</li>
    {% endfor %}
</ul>

...and nothing is displayed.

I know that I am doing something wrong - I am a beginner in writing modules. If anyone could say what I am doing wrong, I would be very grateful.

Best wishes Arek

4
  • 2
    Just one thing that is odd: That controller isn’t a controller because it doesn’t process HTTP requests on a route. You don’t need a controller class to create a block.
    – cilefen
    Aug 14, 2022 at 20:17
  • 2
    With a particular view mode and the view displaying entities in that view mode theming is as easy as node--fashion--my-view-mode.html.twig. No need for a custom query or a custom block.
    – leymannx
    Aug 14, 2022 at 21:05
  • @cilefen thanks for thanks for making me aware that in this case the controller is not needed and all the code can be in the file ... block.php.
    – arek_s
    Aug 15, 2022 at 9:48
  • @leymannx - thanks for your answer. I will try to create what I planned using a views.
    – arek_s
    Aug 15, 2022 at 9:50

2 Answers 2

1

Assuming your FashionQuery method yields actual results, the way to print that result set would probably be something like this (untested):

$render_fashion_ids = $fashion_variable->FashionQuery();
$ids = array_map(function ($item) {
  return $item->entity_id;
}, $render_fashion_ids);
$titles = array_map(function ($item) {
  return $item->title;
}, $render_fashion_ids);

return [
  '#theme' => 'fashion_block',
  '#ids' => $ids,
  '#titles' => $titles,
];

Besides the preprocessing of the result set to create 2 distinct new variables for the $ids and the $titles, also note the absence of the #variables key in the final render array.

You also want to check if the twig file is actually found (e.g. by adding some markup that is always visible). Please refer to the hook_theme() documentation to see how to specify the name of the twig file if necessary.

1
  • Berliner, thank you very much. It works. I spent several hours looking for answer and nothing. I tried to find a proper solution to pass data from ...block.php to .module file and failed. And now it works. Thanks again.
    – arek_s
    Aug 15, 2022 at 9:46
1

Personally, I would use the Views module for this.

It's very easy to build the queries in Views, plus there are useful features like Contextual Filters if you need to handle large numbers of entities.

Views also has really robust support for theming. As noted by @leymannx, you can use theme templates to easily theme the view. You can create theme templates for each field, entity, view mode, the view itself, the particular block display-- whatever you need. You can find examples in Views twig naming conventions.

In addition, if you are displaying your view as fields, you can easily remove the default markup or customize the classes from the Views UI. See Customize the output style of a view field.

The hardest part of theming in Views is deciding which template to override. Understanding how Drupal models data is a bit tricky at first, so if you need help with that, feel free to ask a new question.

2
  • Patrick, thank you very much for your answer. I have some experience with views, but I see one big issue for me - how to exclude nodes displayed in another view on the same page in one view. Is there such a possibility in context filters? Another problem - different styling of nodes in one view - maybe {{ loop.index }} can do it?
    – arek_s
    Aug 15, 2022 at 9:42
  • @arek_s There are several options for styling rows differently; you can assign unique classes to each row by adding the row ID or node ID, for example. You can also exclude nodes with contextual filters; there's no "nodes shown on this page" argument, but if there is a rule for which nodes are shown on the page, you can configure a contextual filter to do that. Aug 15, 2022 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.