2

I have a node type recipe, which should only ever be created via a custom route that causes it to be linked to a parent food type, ie. if the user visits node/add/recipe/1 they will see a recipe creation form that is linked to node 1 of type food.

Now I want to disable the /node/add/recipe route and prevent the recipe node from appearing on node/add. What's the best way to do this?

Note that this is not just a permissions issue - with my module it simply doesn't make sense for a recipe type to be created without a food that is linked to it, so visiting the node/add/recipe page simply doesn't make sense, even for an administrator.

2 Answers 2

3

A different approach would be to deny access by throwing an exception, which also affects admins:

use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_node_recipe_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  throw new AccessDeniedHttpException();
}

The Form ID consists of ENTITY_TYPE_BUNDLE_FORM_MODE_form. This assumes you use for your specific recipe form a different form mode. If not, you can check the reference field value in $form_state->getFormObject()->getEntity() before throwing the exception.

By the way, the hook name doesn't contain a form mode because nodes use the form mode default for the add form, which is omitted in the Form ID. The node edit form for example has the form mode edit, so the form alter hook would then be named mymodule_form_node_recipe_edit_form_alter.

Since this approach is not based on entity access permissions it doesn't remove the link from the node add list. You can do this in a separate hook:

/**
 * Implements hook_preprocess_HOOK() for list of available node type templates
 */
function mymodule_preprocess_node_add_list(&$variables) {
  unset($variables['content']['recipe']);
  unset($variables['types']['recipe']);
}
0
3

You need to create a RouteSubscriber service to override the default behavior to access this page (flush the cache to use it).

Denying the access this way will automatically remove the links to add this type of nodes.

mymodule.services

services:
  mymodule.route_subscriber:
    class: Drupal\mymodule\Routing\MymoduleRouteSubscriber
    tags:
      - { name: event_subscriber, priority: 1 }

src/Routing/MymoduleRouteSubscriber.php

<?php

namespace Drupal\mymodule\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Custom route subscriber.
 */
class MymoduleRouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('node.add')) {
      $requirements = $route->getRequirements();
      $requirements['_custom_access'] = '\Drupal\mymodule\Access\MymoduleNodeAddAccessCheck::access';
      $route->setRequirements($requirements);
    }
  }

}

src/mymodule/Access/MymoduleNodeAddAccessCheck.php

<?php

namespace Drupal\mymodule\Access;

use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\Access\NodeAddAccessCheck;
use Drupal\node\NodeTypeInterface;
use Symfony\Component\Routing\Route;

/**
 * Custom access check for /node/add route.
 */
class MymoduleNodeAddAccessCheck implements AccessCheckInterface {

  /**
   * {@inheritdoc}
   */
  public function applies(Route $route) {
    return NULL;
  }


  /**
   * Checks access to the node add page for the node type.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The currently logged in account.
   * @param \Drupal\node\NodeTypeInterface $node_type
   *   (optional) The node type. If not specified, access is allowed if there
   *   exists at least one node type for which the user may create a node.
   *
   * @return string
   *   A \Drupal\Core\Access\AccessInterface constant value.
   */
  public function access(AccountInterface $account, NodeTypeInterface $node_type = NULL) {
    if ($node_type == 'recipe') {
      return AccessResult::forbidden();
    }
    // Else call the normal access check.
    return NodeAddAccessCheck::access($account, $node_type);
  }

}

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.