1

In a try to get code cleaner, I thought I could write a service in a custom module and use its methods within mytheme.theme, but obviously the preprocess functions signatures don't accept extra parameters.

In modules/custom/custom_module/src/Service/FooThemeHelpers.php

namespace Drupal\custom_module\Service;

class FooThemeHelpers 
{
  // Add a specific custom format 
  public function format_profile_adresse($field_profile_adresse){

    // do stuff ...

    return $markup;
  }
}

Then in mytheme.theme

use Drupal\custom_module\Service\FooThemeHelpers;

function mytheme_preprocess_views_view_fields(&$variables, FooThemeHelpers $themeHelpers) {

    if($variables['view']->id() == 'bar') {

      $markup = $themeHelpers->format_profile_adresse($variables['fields']['field_profile_adresse']->content);
      $variables['fields']['field_profile_adresse']->content = $markup;
  }
}

TypeError: Argument 2 passed to mytheme_preprocess_views_view_fields() must be an instance of Drupal\custom_module\Service\FooThemeHelpers, string given

I tried also with template_preprocess_page_title(), same error.

Should I declare and use static methods instead in my service ? Is there any Drupal coding standard way to export these helpers functions in a dedicated class, so that .theme remains lighter and readable... ?

3
  • 3
    Because nothing is instantiating and passing that argument. You need to instantiate it from inside the function.
    – Kevin
    Commented Jan 20, 2020 at 17:41
  • 2
    Does this answer your question? How to use helper/private methods with a .module file?
    – leymannx
    Commented Jan 20, 2020 at 18:30
  • @leymannx tks, sort of. It answers the debug part, but that one goes a step further with explanations about static vs service. In the end, each one, including the answer below gave me something complementary I believe (DI standard here). Thanks !
    – Kojo
    Commented Jan 20, 2020 at 18:58

2 Answers 2

4

The dependency injection coding standards only apply to most * classes.
In functions, use $service = \Drupal::service('service_name') to get the service.

To test code written in these functions, mock the services then build a custom container for them and assign it to Drupal using \Drupal::setContainer($container).

See Drupal\Tests\Core\Annotation\TranslationTest for an example of building a test container.

* Entity types, classes implementing TypedDataInterface, and certain plugins with custom plugin managers do not support dependency injection.

0
2

This is not exact answer to the question but to the code example there, so if you really trying to achieve the task in the example code, then the right way is to use custom FieldFormatter and you can set this one on any views and/or entity view_mode for field type address. You can easy get a boilerplate with drupal console command

drupal generate:plugin:fieldformatter

name it CustomAddressFieldFormatter for ex. This should happen in custom module. Pay attention to viewElements() method, address data is in $item, and you should return renderable array such as #markup, or any other valid,

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = [];

    foreach ($items as $delta => $item) {
      //ksm($item);
      $elements[$delta] = ['#markup' => $item->getLocality()];
    }

    return $elements;
  }

or create your theme for an element and pass the data to it here. Above code will print the address City only for ex.

Look here https://www.drupal.org/docs/8/creating-custom-modules/creating-custom-field-types-widgets-and-formatters/create-a-custom-0 but instead in field_types Random you should use address

Then in views or entity viev_mode for field address, for Formatter you will have your custom formatter (Custom Address Field Formatter) as option to select - how this field to be rendered in the page.

5
  • This is probably the way to do it. A field formatter would make it DRY, and usable outside of Views or in other views without repeating code.
    – Kevin
    Commented Jan 20, 2020 at 20:49
  • Sounds good, thanks ! Not sure I could achieve it this way though, because this was indeed a simplified example code : the real formatter treatment relies on the values of 2 fields.
    – Kojo
    Commented Jan 21, 2020 at 9:20
  • $item->getEntity() will give you whole entity so you can use any fields value to prepare your output, if this make sense
    – svetlio
    Commented Jan 21, 2020 at 10:53
  • Great ! Unfortunately I don't have enough time now to refactor the code again for this feature, since the implemented solution with Drupal::service is clean, but I'll keep your answer as a reference for later :-)
    – Kojo
    Commented Jan 21, 2020 at 16:54
  • because it is interesting topic here is another approach - to use extra field - this will keep the original field for further usage available alongside the new field - api.drupal.org/api/drupal/…
    – svetlio
    Commented Jan 27, 2020 at 9:32

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.