All skills
grasmash avatar

/drupal-at-your-fingertips

@76e6435

Comprehensive Drupal patterns from "Drupal at Your Fingertips" by Selwyn Polit. Covers 50+ topics including services, hooks, forms, entities, caching, testing, and more.

Use this Skill: https://skilld.dev/gh/grasmash/drupal-claude-skills/drupal-at-your-fingertips

This session only. Nothing lands on disk.

referencesroutes.md

≈1.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Routes & Controllers

Source: Drupal at Your Fingertips - routes Author: Selwyn Polit

Quick reference for Drupal routing and controllers with practical code examples.


Core Concept

Routes map URLs to controller methods. Defined in my_module.routing.yml, they specify path, controller, title, and access requirements.

Basic Route Definition

In my_module.routing.yml:

my_module.hello:
  path: '/hello'
  defaults:
    _controller: '\Drupal\my_module\Controller\HelloController::hello'
    _title: 'Hello Page'
  requirements:
    _permission: 'access content'

Controller (src/Controller/HelloController.php):

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {

  public function hello() {
    return [
      '#markup' => $this->t('Hello World!'),
    ];
  }
}

Route Parameters

Dynamic URL segments:

my_module.user_page:
  path: '/user/{user}/profile'
  defaults:
    _controller: '\Drupal\my_module\Controller\UserController::viewProfile'
    _title: 'User Profile'
  requirements:
    _permission: 'access user profiles'
    user: \d+  # Numeric only

Controller receives parameters:

public function viewProfile($user) {
  $user_entity = User::load($user);

  if (!$user_entity) {
    throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
  }

  return [
    '#markup' => $this->t('Profile for @name', [
      '@name' => $user_entity->getDisplayName(),
    ]),
  ];
}

Auto-upcasting (load entity from parameter):

my_module.node_custom:
  path: '/node/{node}/custom'
  defaults:
    _controller: '\Drupal\my_module\Controller\NodeController::customView'
  requirements:
    _permission: 'access content'
  options:
    parameters:
      node:
        type: entity:node
use Drupal\node\NodeInterface;

public function customView(NodeInterface $node) {
  // $node is automatically loaded
  return [
    '#markup' => $this->t('Node title: @title', [
      '@title' => $node->getTitle(),
    ]),
  ];
}

Access Control

Permission-based:

requirements:
  _permission: 'administer site configuration'

Multiple permissions (OR logic with +):

requirements:
  _permission: 'edit own content+administer content'

Role-based:

requirements:
  _role: 'administrator+editor'

Custom access check:

requirements:
  _custom_access: '\Drupal\my_module\Controller\MyController::checkAccess'
public function checkAccess() {
  $user = \Drupal::currentUser();
  return AccessResult::allowedIf($user->id() > 1);
}

Dynamic Page Titles

Title callback:

my_module.dynamic_title:
  path: '/content/{node}'
  defaults:
    _controller: '\Drupal\my_module\Controller\ContentController::view'
    _title_callback: '\Drupal\my_module\Controller\ContentController::getTitle'
public function getTitle(NodeInterface $node) {
  return $this->t('@title Details', ['@title' => $node->getTitle()]);
}

Returning Different Response Types

Render array (most common):

public function buildPage() {
  return [
    '#theme' => 'my_template',
    '#data' => $this->getData(),
  ];
}

JSON response:

use Symfony\Component\HttpFoundation\JsonResponse;

public function apiEndpoint() {
  $data = [
    'status' => 'success',
    'items' => $this->getItems(),
  ];

  return new JsonResponse($data, 200, [
    'Cache-Control' => 'no-cache, must-revalidate',
  ]);
}

Redirect:

use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Url;

public function redirectExample() {
  $url = Url::fromRoute('my_module.other_page');
  return new RedirectResponse($url->toString());
}

File download:

use Symfony\Component\HttpFoundation\BinaryFileResponse;

public function downloadFile() {
  $file_path = '/path/to/file.pdf';
  return new BinaryFileResponse($file_path);
}

ControllerBase Shortcuts

No DI required for these:

class MyController extends ControllerBase {

  public function buildPage() {
    // Entity storage
    $storage = $this->entityTypeManager()->getStorage('node');

    // Current user
    $user = $this->currentUser();

    // Configuration
    $config = $this->config('system.site');

    // Messenger
    $this->messenger()->addStatus($this->t('Message'));

    // Module handler
    $this->moduleHandler()->moduleExists('views');

    // Form builder
    $form = $this->formBuilder()->getForm('Drupal\my_module\Form\MyForm');

    return $form;
  }
}

Route Options

Disable caching:

options:
  no_cache: TRUE

Admin route (uses admin theme):

options:
  _admin_route: TRUE

Parameter constraints:

options:
  parameters:
    node:
      type: entity:node
    user:
      type: entity:user

Common Route Patterns

Pattern Example Use Case
Simple page /about Static content
Entity view /node/{node} Entity display
Entity edit /node/{node}/edit Entity forms
User-specific /user/{user}/messages User-related pages
Admin config /admin/config/my-module Settings forms
API endpoint /api/v1/content JSON responses


Debugging Routes

List all routes:

drush route

Find route by path:

drush route --path=/admin/config

Find route by name:

drush route --name=my_module.hello

Generate controller:

drush generate controller

Key Guidelines

✅ Use meaningful route names - my_module.action_description ✅ Validate parameters - Check input before using ✅ Use auto-upcasting - Let Drupal load entities ✅ Return correct response types - Render array for pages, JsonResponse for APIs ✅ Set appropriate access - Always require permissions ✅ Use title callbacks - For dynamic titles ✅ Follow URL patterns - Use hyphens, not underscores

❌ Don't use internal IDs in public URLs - Use UUIDs for APIs ❌ Don't skip access checks - Always set requirements ❌ Don't hardcode redirects - Use Url::fromRoute() ❌ Don't forget 404 responses - Throw NotFoundHttpException ❌ Don't use _controller for forms - Use _form instead


Full documentation: https://drupalatyourfingertips.com/routes

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill is a comprehensive documentation resource for Drupal development, providing patterns, code examples, and best practices for various Drupal APIs. No security issues were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    55/55 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 76e6435. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 4 months ago

README badge

README badge for grasmash/drupal-claude-skills/drupal-at-your-fingertips