5

My company has just purchased a Drupal theme to apply to some new pages they want to add to an existing site. This theme is different to the existing theme of the site.

They want to create a new content type which will work with the new theme and not affect any of the existing pages using the default theme. So basically we want to switches themes based on what the content type of the page is. They also want the freedom to create any URL they choose so using a multi-site configuration is out of the question.

I've seen a number of posts on the internet showing this is possible in Drupal 7. Is this possible in Drupal 8 and how can I make it work?

4 Answers 4

4

In Drupal 7, it was straight forward possible by implementing hook_custom_theme() in your custom module.

As for Drupal 8, you will need to create a ThemeNegotiator. Link to the respective change record : 'theme callback' and hook_custom_theme() replaced by theme negotiators.

In the applies() function of the class you can apply your logic and return the theme you wish to have in the determineActiveTheme() function.

Note that, you might have to create multiple ThemeNegotiators for multiple conditions.

2

Thanks AjitS for the info, I've used your answer to write the following working code:

class ThemeNegotiator implements ThemeNegotiatorInterface {
  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    // Use this theme on a certain route.
    $node = $route_match->getParameter('node');
    if (!is_null($node) && $node instanceof \Drupal\node\Entity\Node) {
      return $node->getType() == 'content_type_machine_name';
    }

    // apply default theme
    return false;
  }

  /**
   * {@inheritdoc}
   */
  public function determineActiveTheme(RouteMatchInterface $route_match) {
    // Here you return the actual theme name.
    return 'new_theme_machine_name';
  }
}
0
1

Building on @adrianp, this is how you would do it for the form pages (edit/add).

my_module.services.yml

services:
  theme.negotiator.my_module:
    class: Drupal\my_module\Theme\MyModuleThemeNegotiator
    arguments: ['@config.factory']
    tags:
      - { name: theme_negotiator, priority: 1000  }

in my_module/Routing/MyModuleThemeNegotiator.php

namespace Drupal\my_module\Theme;

use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;
use Symfony\Component\Routing\Route;

/**
 * My ModuleTheme Negotiator for some content types add/edit.
 *
 * We must use a theme negotiator instead of routeSubscriberBase->alterRoutes
 * to make the route admin, as the same route works all content types
 * and we only want one.
 *
 * How determining themes work in Drupal:
 * - When is comes time for Drupal to render your page and select a theme,
 * it looks for all classes that implement ThemeNegotiatorInterface.
 * - All implementations begin to be called in priority order, from the highest
 * to the lowest.
 * - If the applies method returns false, Drupal moves on to the next
 * implementation.
 * - If the applies method returns true, Drupal calls the determineActiveTheme
 * method in the same implementation.
 * - If determineActiveTheme returns null, Drupal moves on to the next
 *  implementation.
 * - If determineActiveTheme returns a theme name, Drupal will use this theme,
 * and no further implementations are called.
 * - Drupal has its own default implementation. If no other implementations
 * selects a theme, the default will select a theme.
 *
 * @see https://jimconte.com/blog/web/dynamic-theme-switching-in-drupal-8
 */
class MyModuleThemeNegotiator implements ThemeNegotiatorInterface {

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactory
   */
  protected $configFactory;

  /**
   * Service constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactory $configFactory
   *   Configfactory.
   */
  public function __construct(ConfigFactory $configFactory) {
    $this->configFactory = $configFactory;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    $route = $route_match->getRouteObject();
    if (!$route instanceof Route) {
      return FALSE;
    }
    $routes_to_match = ['node.add', 'entity.node.edit_form'];
    if (in_array($route_match->getRouteName(), $routes_to_match)) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function determineActiveTheme(RouteMatchInterface $route_match) {
    $applicable_bundles = [
      'news',
      'standard_page',
    ];
    $switch_theme = FALSE;
    switch ($route_match->getRouteName()) {
      case 'node.add':
        if ($entity_type_param = $route_match->getParameter('node_type')) {
          if (in_array($entity_type_param->id(), $applicable_bundles)) {
            $switch_theme = TRUE;
          }
        }
        break;

      case 'entity.node.edit_form':
        if ($entity_param = $route_match->getParameter('node')) {
          if (in_array($entity_param->bundle(), $applicable_bundles)) {
            $switch_theme = TRUE;
          }
        }
        break;

    }

    if ($switch_theme) {
      // Return the machine name of the front-end theme.
      return $this->configFactory->get('system.theme')->get('default');
    }
    // Not applicable.
    return NULL;
  }

}
0

In addition to programmatic ways nowadays there are plenty modules for d8+. We using this one https://www.drupal.org/project/theme_switcher to apply a different theme on specific node types detail pages and URLs. Alternatives discussed here: https://www.drupal.org/project/theme_switcher/issues/3348676

Your Answer

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

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