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
    Because nothing is instantiating and passing that argument. You need to instantiate it from inside the function. – Kevin Jan 20 at 17:41
  • 2
    Does this answer your question? How to use helper/private methods with a .module file? – leymannx Jan 20 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 Jan 20 at 18:58
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.

|improve this answer|||||
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.

|improve this answer|||||
  • 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 Jan 20 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 Jan 21 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 Jan 21 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 Jan 21 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 Jan 27 at 9:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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