Static approach

The static approach involves creating a method on our Controller (or somewhere else), usually called access(), and referencing it from the route definition. So, inside our controller we can have this:

/** 
 * Handles the access checking. 
 * 
 * @param DrupalCoreSessionAccountInterface $account 
 * 
 * @return DrupalCoreAccessAccessResultInterface 
 */ 
public function access(AccountInterface $account) { 
  return in_array('editor', $account->getRoles()) ? AccessResult::forbidden() : AccessResult::allowed(); 
} 

And the new use statements:

use DrupalCoreAccessAccessResult; 
use DrupalCoreSessionAccountInterface;  

This method receives the current user's AccountInterface, which we can use to determine the roles. Moreover, if we type hint some extra parameters, Drupal will pass them to the method as well:

  • SymfonyComponentRoutingRoute $route
  • DrupalCoreRoutingRouteMatch $route_match

We've already discussed the CurrentRouteMatch service in Chapter 2, Creating Your First Module, and we saw that we can use it to find out things about the route that has just been accessed. In reality, that service simply uses RouteMatch objects underneath. So in case our access rules for this route depend on something that relates to the route, this argument can be very important. Soon, I will demonstrate why that is in further detail.

Similarly, we can also type hint the actual Route object that contains data about the route. This plays to the same point I just made, and we can also use it in our logic. But alas, for our use case, these won't be necessary, so we will stick with the AccountInterface.

What we are returning in this method is very important, as it needs to be an instance of AccessResultInterface. This is the standard interface the access system in Drupal 8 works with. The following are the three main implementations of this interface you will often encounter:

  • AccessResultAllowed
  • AccessResultNeutral
  • AccessResultForbidden

The gateway to these objects, however, is typically the AccessResult abstract base class (which all of these implementations extend as well) and its static methods. As you saw in the previous example, we used the allowed() and forbidden() methods to instantiate these objects. Of course, there is also the corresponding neutral() method we can use to indicate that we don't have a say in the matter. Typically, this is used when there are multiple actors involved in deciding access to a certain resource and one such actor encounters a resource for which they don't need to control access.

With Drupal 8.3, the neutral and forbidden access results also support a reason. This is typically used in REST scenarios to display a message as to why the access has been denied or skipped. So for example, we can return something like this when denying access:

return AccessResult::forbidden('Editors are not allowed');

Some other built-in capabilities of the AccessResult base class are related to cacheability, but it also has convenience methods to achieve a bit more complex access logic. For example, the following methods can prove handy:

  • allowedIf($condition)
  • forbiddenIf($condition)

You simply pass a Boolean to these methods and they return the right access object. Do keep in mind that these methods return an AccessResultNeutral object if the condition evaluates to FALSE. So, you cannot use these methods if you need to map a Boolean to an explicitly allowed or explicitly denied result.

Additionally, we have methods like the following:

  • allowedIfHasPermission()
  • allowedIfHasPermissions()

This will check whether a given account has one or more permissions and returns the right access object depending on the case.

Finally, we also have the orIf() and andIf() methods with which we can build more complex access structures that combine multiple AccessResultInterface results.

Closing the parentheses on the AccessResultInterface, let's reference this method in our route in order to actually make use of it. This is what the route definition looks like now:

hello_world.hello: 
  path: '/hello' 
  defaults: 
    _controller: 'Drupalhello_worldControllerHelloWorldController::helloWorld' 
    _title: 'Our first route' 
  requirements: 
    _custom_access: 'Drupalhello_worldControllerHelloWorldController::access' 

Instead of the _permission requirement, we use _custom_access with a reference to our Controller method. After clearing the cache, our new access checker will "kick out" those pesky editor users.

This static approach, as you can imagine, is slightly more powerful than using permission or roles-based access checking because it allows you to write PHP logic in order to determine the access. However, it falls short in a number of respects, and this is where the service-based approach can be used.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset