Drupal 11 Developer Certification Cheat Sheet

Complete study guide and quick reference for the Acquia Certified Drupal Developer exam covering Drupal 11 fundamentals, site building, theming, and module development.

13 min read
Updated 1/18/2026
Acquia Certified Drupal 11 Developer
intermediate
#drupal#acquia#php#cms#drupal-11#certification

Drupal 11 Developer Certification Cheat Sheet

Exam Details: 90 minutes | 60 questions | 65% passing score

This comprehensive cheat sheet covers all four domains of the Acquia Certified Drupal Developer exam.


Domain 1: Fundamental Web Development (10%)

HTML & CSS Essentials

HTMLHTML
1<!-- Semantic HTML5 Elements -->
2<header> - Page/section header
3<nav> - Navigation section
4<main> - Main content area
5<article>- Self-contained content
6<aside> - Sidebar content
7<footer> - Page/section footer

CSS Specificity Order (Low → High):

  1. Element selectors (h1, p)
  2. Class selectors (.class)
  3. ID selectors (#id)
  4. Inline styles (style="")
  5. !important

Flexbox Quick Reference:

CSSCSS
1.container {
2 display: flex;
3 justify-content: center; /* Main axis */
4 align-items: center; /* Cross axis */
5 flex-wrap: wrap;
6}

JavaScript Fundamentals

JSJavaScript
1// Variable declarations
2const CONSTANT = "Cannot reassign"; // Block-scoped, immutable
3let mutable = "Can reassign"; // Block-scoped, mutable
4var legacy = "Function-scoped"; // Avoid in modern JS
5
6// Arrow functions
7const greet = (name) => `Hello, ${name}`;
8
9// Promises
10fetch('/api/data')
11 .then(response => response.json())
12 .then(data => console.log(data))
13 .catch(error => console.error(error));
14
15// DOM manipulation
16document.addEventListener('DOMContentLoaded', function() {
17 const element = document.getElementById('myId');
18 element.addEventListener('click', handler);
19});
20
21// jQuery equivalent
22$(document).ready(function() {
23 $('#myId').on('click', handler);
24});

Git Version Control

CommandPurpose
git branch featureCreate new branch
git checkout -b featureCreate and switch to branch
git stashTemporarily save changes
git stash popRestore stashed changes
git merge featureMerge branch (creates merge commit)
git rebase mainReplay commits linearly
git push origin mainPush to remote
git fetch originDownload without merge

HTTP Basics

Status Codes:

  • 2xx Success: 200 OK, 201 Created
  • 3xx Redirect: 301 Moved Permanently, 302 Found
  • 4xx Client Error: 400 Bad Request, 404 Not Found, 403 Forbidden
  • 5xx Server Error: 500 Internal Server Error, 503 Service Unavailable

HTTP Methods:

  • GET - Retrieve data (idempotent)
  • POST - Create resource (not idempotent)
  • PUT - Update/replace resource (idempotent)
  • PATCH - Partial update
  • DELETE - Remove resource (idempotent)

Domain 2: Site Building (30%)

Content Types & Fields

Default Content Types:

  • Article (with tags, body, image)
  • Basic page (simple body content)

Field Types:

TypeUse Case
Text (plain)Titles, short strings
Text (formatted)Rich content with HTML
Number (integer/decimal)Numeric values
BooleanOn/off toggles
Date/TimeDate and time values
Entity ReferenceLink to other entities
File/ImageMedia uploads
LinkURL references

Cardinality: How many values a field can store (1, limited, unlimited)

Widgets vs Formatters:

  • Widget = Form input element (edit form)
  • Formatter = Display output (view page)

Display Modes

View Modes (output display):

  • Default/Full - Complete content view
  • Teaser - Summary for listings
  • Search result - Search listings
  • Custom modes for specific contexts

Form Modes (input display):

  • Default - Standard edit form
  • Register - User registration form
  • Custom modes for simplified forms

Taxonomy

plaintext
1Vocabulary (container)
2└── Terms (items)
3 ├── Parent term
4 │ ├── Child term
5 │ └── Child term
6 └── Another term

Key Concepts:

  • Vocabulary = Category group (e.g., "Tags", "Categories")
  • Term = Individual classification label
  • Hierarchy = Parent/child relationships via "Parent term" setting

Blocks

Placement: Structure → Block layout

Visibility Conditions:

  • Pages - Show on specific URLs
  • Content types - Show on specific content types
  • User roles - Show to specific roles
  • Language - Show for specific language

Custom Block Types: Add custom fields to blocks (like content types)

Default Menus:

  • Main navigation - Primary site nav
  • Footer - Footer links
  • Administration - Admin menu
  • User account menu - Login/logout links

Creating Submenus: Set "Parent link" on menu items

Views

Display Types:

TypeCreates
PageStandalone URL path
BlockPlaceable block
FeedRSS/XML output
AttachmentAttaches to other displays

Key Components:

  • Fields - What to display
  • Filter criteria - What to include (fixed)
  • Exposed filters - User-controlled filtering
  • Sort criteria - Order of results
  • Contextual filters - Dynamic from URL/context
  • Relationships - JOIN related entities
  • Pager - Pagination settings

Views Admin Path: /admin/structure/views

Configuration Management

Terminal
1# Export config
2drush config:export # or drush cex
3
4# Import config
5drush config:import # or drush cim
6
7# Single config export
8drush config:get system.site
9
10# Rebuild cache
11drush cache:rebuild # or drush cr

Sync Directory: sites/default/files/config_*/sync or ../config/sync

Config vs Content:

  • Config entities - Exportable (views, content types, fields)
  • Content entities - User data (nodes, users, terms)

Multilingual

Core Modules:

  • Language - Base multilingual framework
  • Interface Translation - Translate UI strings
  • Content Translation - Translate content entities
  • Configuration Translation - Translate config

Language Detection Methods:

  • URL prefix (/en/, /fr/)
  • Domain (en.site.com)
  • Browser settings
  • User preference

Domain 3: Front-end Development / Theming (25%)

Theme Structure

plaintext
1themes/custom/mytheme/
2├── mytheme.info.yml # Required: Theme metadata
3├── mytheme.libraries.yml # CSS/JS libraries
4├── mytheme.theme # PHP preprocess functions
5├── mytheme.breakpoints.yml # Responsive breakpoints
6├── config/
7│ └── install/
8│ └── mytheme.settings.yml
9├── css/
10│ └── style.css
11├── js/
12│ └── scripts.js
13├── templates/ # Twig template overrides
14│ ├── page.html.twig
15│ └── node--article.html.twig
16└── logo.svg

Theme Info File

YAMLYAML
1# mytheme.info.yml
2name: My Theme
3type: theme
4description: 'A custom Drupal theme'
5core_version_requirement: ^10 || ^11
6base theme: starterkit_theme # or 'false' for no base
7
8libraries:
9 - mytheme/global
10
11regions:
12 header: 'Header'
13 content: 'Content'
14 sidebar: 'Sidebar'
15 footer: 'Footer'

Libraries Definition

YAMLYAML
1# mytheme.libraries.yml
2global:
3 version: 1.0
4 css:
5 theme:
6 css/style.css: {}
7 js:
8 js/scripts.js: {}
9 dependencies:
10 - core/jquery
11 - core/drupal
12
13slider:
14 css:
15 component:
16 css/slider.css: {}
17 js:
18 js/slider.js: {}

Twig Syntax

TWIGTwig
1{# Comment - not rendered #}
2
3{{ variable }} {# Output #}
4{{ variable|filter }} {# Apply filter #}
5{% if condition %}...{% endif %} {# Control structure #}
6
7{# Common Filters #}
8{{ title|upper }} {# Uppercase #}
9{{ body|raw }} {# Output raw HTML (careful!) #}
10{{ "Hello"|t }} {# Translation #}
11{{ items|length }} {# Count items #}
12{{ date|date("Y-m-d") }} {# Format date #}
13
14{# Control Structures #}
15{% if user.logged_in %}
16 Welcome, {{ user.name }}
17{% else %}
18 Please log in
19{% endif %}
20
21{% for item in items %}
22 {{ item.title }}
23{% empty %}
24 No items found
25{% endfor %}
26
27{# Including Templates #}
28{% include 'partial.html.twig' %}
29{% include 'partial.html.twig' with {'title': 'Hello'} %}
30
31{# Template Inheritance #}
32{% extends 'base.html.twig' %}
33{% block content %}...{% endblock %}
34
35{# Drupal-specific #}
36{{ attach_library('mytheme/slider') }}
37{{ content|without('field_image') }}

Template Suggestions

Enable Twig Debugging:

YAMLYAML
1# development.services.yml
2parameters:
3 twig.config:
4 debug: true
5 cache: false

Common Suggestion Patterns:

plaintext
1node.html.twig # All nodes
2node--article.html.twig # Article content type
3node--article--teaser.html.twig # Article teaser view
4node--123.html.twig # Specific node ID
5
6field.html.twig # All fields
7field--body.html.twig # Body field
8field--node--body.html.twig # Body field on nodes
9
10block.html.twig # All blocks
11block--system-branding-block.html.twig

Preprocess Functions

PHPPHP
1// mytheme.theme
2
3/**
4 * Implements hook_preprocess_HOOK() for page templates.
5 */
6function mytheme_preprocess_page(&$variables) {
7 $variables['my_custom_var'] = 'Hello World';
8}
9
10/**
11 * Implements hook_preprocess_HOOK() for node templates.
12 */
13function mytheme_preprocess_node(&$variables) {
14 $node = $variables['node'];
15
16 // Add variable for specific content type
17 if ($node->bundle() === 'article') {
18 $variables['is_article'] = TRUE;
19 }
20}
21
22/**
23 * Implements hook_theme_suggestions_HOOK_alter().
24 */
25function mytheme_theme_suggestions_node_alter(array &$suggestions, array $variables) {
26 $node = $variables['elements']['#node'];
27 // Add custom suggestion based on field value
28 if ($node->hasField('field_layout')) {
29 $suggestions[] = 'node__' . $node->field_layout->value;
30 }
31}

Attributes Object

TWIGTwig
1{# Default attributes #}
2<div{{ attributes }}>
3
4{# Add classes #}
5<div{{ attributes.addClass('my-class', 'another-class') }}>
6
7{# Remove class #}
8<div{{ attributes.removeClass('unwanted') }}>
9
10{# Set attribute #}
11<div{{ attributes.setAttribute('data-id', node.id) }}>
12
13{# Check if has class #}
14{% if attributes.hasClass('active') %}

Domain 4: Back-end Development / Coding (35%)

Module Structure

plaintext
1modules/custom/mymodule/
2├── mymodule.info.yml # Required: Module metadata
3├── mymodule.module # Hooks and procedural code
4├── mymodule.install # Install/update hooks
5├── mymodule.routing.yml # URL routes
6├── mymodule.services.yml # Service definitions
7├── mymodule.permissions.yml # Permissions
8├── mymodule.libraries.yml # Frontend assets
9├── config/
10│ ├── install/ # Default config
11│ └── schema/ # Config schema
12├── src/
13│ ├── Controller/ # Page controllers
14│ ├── Form/ # Form classes
15│ ├── Plugin/ # Plugin classes
16│ │ └── Block/ # Custom blocks
17│ ├── Service/ # Service classes
18│ └── EventSubscriber/ # Event subscribers
19└── templates/ # Module templates

Module Info File

YAMLYAML
1# mymodule.info.yml
2name: My Module
3type: module
4description: 'Module description'
5core_version_requirement: ^10 || ^11
6package: Custom
7
8dependencies:
9 - drupal:node
10 - drupal:views

Routing

YAMLYAML
1# mymodule.routing.yml
2mymodule.my_page:
3 path: '/my-path/{node}'
4 defaults:
5 _controller: '\Drupal\mymodule\Controller\MyController::content'
6 _title: 'My Page'
7 requirements:
8 _permission: 'access content'
9 node: \d+
10 options:
11 parameters:
12 node:
13 type: entity:node

Services

YAMLYAML
1# mymodule.services.yml
2services:
3 mymodule.my_service:
4 class: Drupal\mymodule\Service\MyService
5 arguments: ['@entity_type.manager', '@current_user']
6
7 mymodule.event_subscriber:
8 class: Drupal\mymodule\EventSubscriber\MySubscriber
9 tags:
10 - { name: event_subscriber }

Controller

PHPPHP
1<?php
2namespace Drupal\mymodule\Controller;
3
4use Drupal\Core\Controller\ControllerBase;
5use Drupal\node\NodeInterface;
6
7class MyController extends ControllerBase {
8
9 public function content(NodeInterface $node) {
10 return [
11 '#theme' => 'my_template',
12 '#node' => $node,
13 '#cache' => [
14 'tags' => $node->getCacheTags(),
15 ],
16 ];
17 }
18}

Form API

PHPPHP
1<?php
2namespace Drupal\mymodule\Form;
3
4use Drupal\Core\Form\FormBase;
5use Drupal\Core\Form\FormStateInterface;
6
7class MyForm extends FormBase {
8
9 public function getFormId() {
10 return 'mymodule_my_form';
11 }
12
13 public function buildForm(array $form, FormStateInterface $form_state) {
14 $form['name'] = [
15 '#type' => 'textfield',
16 '#title' => $this->t('Name'),
17 '#required' => TRUE,
18 ];
19
20 $form['submit'] = [
21 '#type' => 'submit',
22 '#value' => $this->t('Submit'),
23 ];
24
25 return $form;
26 }
27
28 public function validateForm(array &$form, FormStateInterface $form_state) {
29 if (strlen($form_state->getValue('name')) < 3) {
30 $form_state->setErrorByName('name', $this->t('Name must be at least 3 characters.'));
31 }
32 }
33
34 public function submitForm(array &$form, FormStateInterface $form_state) {
35 $this->messenger()->addMessage($this->t('Hello, @name!', [
36 '@name' => $form_state->getValue('name'),
37 ]));
38 }
39}

Entity API

PHPPHP
1// Load entities
2$node = Node::load($nid);
3$nodes = Node::loadMultiple([1, 2, 3]);
4
5// Entity query
6$nids = \Drupal::entityQuery('node')
7 ->condition('type', 'article')
8 ->condition('status', 1)
9 ->sort('created', 'DESC')
10 ->range(0, 10)
11 ->accessCheck(TRUE)
12 ->execute();
13
14// Entity type manager
15$storage = \Drupal::entityTypeManager()->getStorage('node');
16$node = $storage->load($nid);
17
18// Create entity
19$node = Node::create([
20 'type' => 'article',
21 'title' => 'My Title',
22]);
23$node->save();
24
25// Field access
26$title = $node->getTitle(); // or $node->label()
27$body = $node->get('body')->value;
28$node->set('field_name', 'value');

Database API

PHPPHP
1// Select query
2$results = \Drupal::database()->select('node_field_data', 'n')
3 ->fields('n', ['nid', 'title'])
4 ->condition('type', 'article')
5 ->condition('status', 1)
6 ->orderBy('created', 'DESC')
7 ->range(0, 10)
8 ->execute()
9 ->fetchAll();
10
11// Insert
12\Drupal::database()->insert('mytable')
13 ->fields(['name' => 'value'])
14 ->execute();
15
16// Update
17\Drupal::database()->update('mytable')
18 ->fields(['name' => 'new_value'])
19 ->condition('id', 1)
20 ->execute();
21
22// Delete
23\Drupal::database()->delete('mytable')
24 ->condition('id', 1)
25 ->execute();

Common Services

PHPPHP
1// Current user
2$account = \Drupal::currentUser();
3$uid = $account->id();
4$is_admin = $account->hasPermission('administer site configuration');
5
6// Entity type manager
7$storage = \Drupal::entityTypeManager()->getStorage('node');
8
9// Config
10$config = \Drupal::config('system.site');
11$site_name = $config->get('name');
12
13// Editable config
14$config = \Drupal::configFactory()->getEditable('mymodule.settings');
15$config->set('my_setting', 'value')->save();
16
17// State API (environment-specific values)
18$last_run = \Drupal::state()->get('mymodule.last_run');
19\Drupal::state()->set('mymodule.last_run', time());
20
21// Logger
22\Drupal::logger('mymodule')->error('Error message: @error', [
23 '@error' => $error_message,
24]);
25
26// Messenger
27\Drupal::messenger()->addStatus('Success!');
28\Drupal::messenger()->addWarning('Warning!');
29\Drupal::messenger()->addError('Error!');
30
31// Cache
32$cache = \Drupal::cache();
33$item = $cache->get('my_cache_key');
34$cache->set('my_cache_key', $data, Cache::PERMANENT, ['node:1']);

Security Best Practices

PHPPHP
1// NEVER do this
2echo $user_input; // XSS vulnerability!
3$query = "SELECT * FROM users WHERE id = $user_id"; // SQL injection!
4
5// DO this instead
6use Drupal\Component\Utility\Html;
7use Drupal\Component\Utility\Xss;
8
9// Escape output
10$safe = Html::escape($user_input);
11
12// Filter HTML (allows some tags)
13$filtered = Xss::filter($user_input);
14
15// Database placeholders (automatic escaping)
16$result = \Drupal::database()->query(
17 'SELECT * FROM {users} WHERE uid = :uid',
18 [':uid' => $user_id]
19);
20
21// Check access
22if ($node->access('view')) { ... }
23if ($account->hasPermission('administer nodes')) { ... }

Hooks Reference

PHPPHP
1/**
2 * Implements hook_help().
3 */
4function mymodule_help($route_name, $route_match) {
5 if ($route_name === 'help.page.mymodule') {
6 return '<p>' . t('Help text here.') . '</p>';
7 }
8}
9
10/**
11 * Implements hook_cron().
12 */
13function mymodule_cron() {
14 // Run periodic tasks
15}
16
17/**
18 * Implements hook_ENTITY_TYPE_presave().
19 */
20function mymodule_node_presave(NodeInterface $node) {
21 // Modify node before saving
22}
23
24/**
25 * Implements hook_ENTITY_TYPE_insert().
26 */
27function mymodule_node_insert(NodeInterface $node) {
28 // React after node is created
29}
30
31/**
32 * Implements hook_form_alter().
33 */
34function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
35 // Modify any form
36}
37
38/**
39 * Implements hook_form_FORM_ID_alter().
40 */
41function mymodule_form_node_article_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
42 // Modify specific form
43}

Testing

PHPPHP
1// Unit Test - No Drupal bootstrap
2namespace Drupal\Tests\mymodule\Unit;
3
4use Drupal\Tests\UnitTestCase;
5
6class MyUnitTest extends UnitTestCase {
7 public function testSomething() {
8 $this->assertEquals(1, 1);
9 }
10}
11
12// Kernel Test - Minimal Drupal
13namespace Drupal\Tests\mymodule\Kernel;
14
15use Drupal\KernelTests\KernelTestBase;
16
17class MyKernelTest extends KernelTestBase {
18 protected static $modules = ['mymodule'];
19
20 public function testSomething() {
21 // Has database access
22 }
23}
24
25// Functional Test - Full Drupal
26namespace Drupal\Tests\mymodule\Functional;
27
28use Drupal\Tests\BrowserTestBase;
29
30class MyFunctionalTest extends BrowserTestBase {
31 protected static $modules = ['mymodule'];
32 protected $defaultTheme = 'stark';
33
34 public function testSomething() {
35 $this->drupalGet('/my-path');
36 $this->assertSession()->statusCodeEquals(200);
37 }
38}

Drush Commands

Terminal
1# Cache
2drush cr # Clear all caches
3drush cc render # Clear specific cache bin
4
5# Config
6drush cex # Export configuration
7drush cim # Import configuration
8drush cget system.site # View config item
9
10# Database
11drush sql-cli # Open database CLI
12drush sql-dump > backup.sql # Export database
13drush sql-query "SELECT..." # Run query
14
15# User
16drush user:create username --mail="email@test.com"
17drush user:password username newpass
18drush user:role:add administrator username
19
20# Modules
21drush pm:list # List modules
22drush pm:enable mymodule # Enable module
23drush pm:uninstall mymodule # Uninstall module
24
25# Updates
26drush updb # Run database updates
27drush updatedb:status # Check pending updates
28
29# Queue
30drush queue:list # List queues
31drush queue:run myqueue # Process queue

Quick Reference Card

Key Admin Paths

PathPurpose
/admin/contentContent management
/admin/structure/typesContent types
/admin/structure/viewsViews
/admin/structure/blockBlock layout
/admin/structure/menuMenus
/admin/structure/taxonomyTaxonomy
/admin/config/development/configurationConfig sync
/admin/config/development/performanceCaching
/admin/modulesModules
/admin/appearanceThemes
/admin/people/permissionsPermissions

Essential Drush Commands

Terminal
1drush cr # Clear cache (most used!)
2drush cex # Export config
3drush cim # Import config
4drush updb # Database updates
5drush uli # Generate login link

File Naming Quick Reference

FilePurpose
*.info.ymlModule/theme metadata
*.moduleProcedural PHP hooks
*.themeTheme preprocess functions
*.routing.ymlURL routes
*.services.ymlService definitions
*.libraries.ymlCSS/JS assets
*.permissions.ymlPermissions
*.installInstall/update hooks

Exam Tips

  1. Time Management: 90 seconds per question average
  2. Read Carefully: Watch for "NOT" and "EXCEPT" in questions
  3. Multi-select: Count the correct answers; they may say "Select TWO"
  4. Code Syntax: Exact method names matter (loadMultiple not load_multiple)
  5. Drupal 11 Focus: Know deprecated vs current methods
  6. Practice: Use the review queue to practice before the exam

Good luck on your certification! 🎓

Ready to Test Your Knowledge?

Put what you've learned into practice with our interactive exams.