I needed to block the shipments tab from orders in Commerce when the order is complete. The shipment info is available on the order view page, but I wanted to block the ability to make changes.
To do this, I had to alter the route with a RouteSubscriber and add a custom access check. Then I had to implement the check.
The documentation pages explain both separately, but I'll show you how to put them together.
A RouteSubscriber is an EventSubscriber, with some defaults set in RouteSubscriberBase, like the event to subscribe to. I'm checking for the route I need, and add a '_custom_access' that points to my custom Access checker.
<?php
namespace Drupal\alter_route\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Listens to the dynamic route events.
*/
class RouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
if ($route = $collection->get('entity.commerce_shipment.collection')) {
$route->setRequirement('_custom_access', '\Drupal\alter_route\Access\SynchronizedAccessCheck::access');
}
}
}
I had to check the commerce_order route parameter - it's a loaded entity on the order page, and an id on the shipments list. Then check the order state and block access if completed, or check the original permission.
<?php
namespace Drupal\alter_route\Access;
use Drupal\commerce_order\Entity\Order;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Checks access for displaying order edit form.
*/
class SynchronizedAccessCheck implements AccessInterface {
/**
* A custom access check.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
*/
public function access(AccountInterface $account) {
$order = \Drupal::routeMatch()->getParameter('commerce_order');
if (!$order instanceof \Drupal\commerce_order\Entity\Order) {
$order = Order::load($order);
}
$state_properties = $order->getState()->getProperties();
$order_state = $state_properties['value']->getValue();
if ($order_state == 'completed') {
return \Drupal\Core\Access\AccessResult::forbidden();
}
else {
return \Drupal\Core\Access\AccessResult::allowedIfHasPermission($account, 'administer commerce_shipment');
}
}
}
Both of these are added to alter_route.services.php.
services:
alter_route.access_checker:
class: Drupal\alter_route\Access\SynchronizedAccessCheck
arguments: ['@current_user']
tags:
- { name: access_check }
alter_route.route_subscriber:
class: Drupal\alter_route\Routing\RouteSubscriber
tags:
- { name: event_subscriber }