AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Drupal Code Patterns

skill-trebormc-drupal-ai-agents-drupal-code-patterns · by trebormc

>-

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-trebormc-drupal-ai-agents-drupal-code-patterns

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-trebormc-drupal-ai-agents-drupal-code-patterns)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Drupal Code Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

About the "Reference:" paths below

Some patterns point to the contrib Examples module for extended reference. These templates are SELF-SUFFICIENT — the references are optional extra reading. Before following one, check it exists:

ssh web test -d $DDEV_DOCROOT/modules/contrib/examples && echo "examples available" || echo "examples not installed — skip the references"

Service Class

logger->info('Processing: @input', ['@input' => $input]);
    return ['result' => $input];
  }

}

Form (FormBase)

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/form_api_example

get('mymodule.my_service'));
  }

  public function getFormId(): string {
    return 'mymodule_my_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#required' => TRUE,
      '#maxlength' => 255,
    ];
    $form['actions'] = [
      '#type' => 'actions',
      'submit' => ['#type' => 'submit', '#value' => $this->t('Submit')],
    ];
    return $form;
  }

  public function validateForm(array &$form, FormStateInterface $form_state): void {
    if (strlen($form_state->getValue('name')) setErrorByName('name', $this->t('Name must be at least 3 characters.'));
    }
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $this->myService->process($form_state->getValue('name'));
    $this->messenger()->addStatus($this->t('Form submitted successfully.'));
  }

}

Block Plugin

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/block_example

get('mymodule.my_service'));
  }

  public function build(): array {
    return [
      '#theme' => 'mymodule_block',
      '#data' => $this->myService->getData(),
      '#cache' => [
        'contexts' => ['user'],
        'tags' => ['mymodule:data'],
        'max-age' => 3600,
      ],
    ];
  }

}

Routing & Controllers

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/page_example

mymodule.routing.yml

mymodule.example_page:
  path: '/mymodule/example/{parameter}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\ExampleController::content'
    _title: 'Example Page'
    parameter: 'default_value'
  requirements:
    _permission: 'access content'
    parameter: '\d+'

mymodule.form_page:
  path: '/mymodule/form'
  defaults:
    _form: '\Drupal\mymodule\Form\MyForm'
    _title: 'My Form'
  requirements:
    _permission: 'access mymodule'

Controller

get('mymodule.my_service'));
  }

  public function content(string $parameter): array {
    return [
      '#theme' => 'mymodule_example',
      '#data' => $this->myService->getData($parameter),
      '#cache' => [
        'contexts' => ['url.path'],
        'tags' => ['mymodule:data'],
        'max-age' => 3600,
      ],
    ];
  }

}

Hooks

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/hooks_example

 [
      'variables' => ['title' => '', 'items' => []],
      'template' => 'mymodule-custom',
    ],
  ];
}

Caching Best Practices

Every render array MUST have cache metadata:

public function build(): array {
  return [
    '#markup' => $this->getData(),
    '#cache' => [
      'contexts' => ['user', 'url.path', 'url.query_args'],
      'tags' => ['node:1', 'node_list', 'mymodule:data'],
      'max-age' => 3600,
    ],
  ];
}

For dynamic user-specific content, use lazy builders:

$build['dynamic_part'] = [
  '#lazy_builder' => ['mymodule.lazy_builder:build', [$entity_id]],
  '#create_placeholder' => TRUE,
];

For comprehensive caching strategies, use the performance-audit skill.

Batch API

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/batch_example

function mymodule_batch_process(array $items, array &$context): void {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = count($items);
  }
  $batch_size = 10;
  $slice = array_slice($items, $context['sandbox']['progress'], $batch_size);
  foreach ($slice as $item) {
    // Process item...
    $context['sandbox']['progress']++;
  }
  $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}

function mymodule_execute_batch(): void {
  $batch = [
    'title' => t('Processing items...'),
    'operations' => [['mymodule_batch_process', [range(1, 1000)]]],
    'finished' => 'mymodule_batch_finished',
    'progress_message' => t('Processed @current of @total.'),
  ];
  batch_set($batch);
}

Queue API

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/queue_example

mail(
      'mymodule', 'notification', $data['to'], $data['langcode'], $data['params']
    );
  }

}

Adding items: \Drupal::queue('mymodule.email_sender')->createItem($data);

AJAX Forms

Reference: $DDEV_DOCROOT/modules/contrib/examples/modules/ajax_example

 'select',
      '#title' => $this->t('Category'),
      '#options' => ['fruits' => $this->t('Fruits'), 'vegetables' => $this->t('Vegetables')],
      '#ajax' => [
        'callback' => '::updateItemsCallback',
        'wrapper' => 'items-wrapper',
        'event' => 'change',
      ],
    ];
    $form['items_wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'items-wrapper'],
    ];
    $category = $form_state->getValue('category', 'fruits');
    $form['items_wrapper']['item'] = [
      '#type' => 'select',
      '#title' => $this->t('Item'),
      '#options' => $this->getItemsByCategory($category),
    ];
    $form['actions'] = [
      '#type' => 'actions',
      'submit' => ['#type' => 'submit', '#value' => $this->t('Submit')],
    ];
    return $form;
  }

  public function updateItemsCallback(array &$form, FormStateInterface $form_state): array {
    return $form['items_wrapper'];
  }

  private function getItemsByCategory(string $category): array {
    $items = [
      'fruits' => ['apple' => 'Apple', 'banana' => 'Banana'],
      'vegetables' => ['carrot' => 'Carrot', 'broccoli' => 'Broccoli'],
    ];
    return $items[$category] ?? [];
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $this->messenger()->addStatus($this->t('Form submitted.'));
  }

}

After Applying Any Pattern

# 1. Register new classes/files and rebuild caches:
ssh web drush cr

# 2. If you added/changed a route, verify it exists:
ssh web drush route | grep mymodule

# 3. Run quality checks (see the quality-checks skill) before presenting the code.

Where to see the result: forms/controllers at their route path, blocks via Block Layout (/admin/structure/block) after placing them.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.