Acquia Developer (D10) cheat sheet
Acquia Developer (D10) · Keep the key ideas within reach.
Drupal Fundamentals and Site Building
Content entities (nodes, users, taxonomy terms, media) are fieldable and revisionable; config entities (views, vocabularies, content types) are exported to YAML and not fieldable.
Entity IDs and revision IDs are sequential integers; UUIDs identify config entities across environments and must stay stable during config import.
A content type is a bundle of the node entity type; fields attach to bundles, so the same field name can exist on multiple bundles with separate storage.
Taxonomy vocabularies hold terms; a term reference field can target one or more vocabularies, and a single-value field stores one delta=0 row in its field table.
Views are config entities; a page display has a path and menu settings, a block display is placed via Block Layout, and caching is controlled per display.
Aliases are managed by the Pathauto and Redirect modules; the path_alias entity stores alias-to-system-path mappings and survives content edits.
Content moderation applies workflows to entity bundles; each workflow defines states and transitions, and revisions carry the moderation state.
Node access is grant-based: node_access rows are written per node per realm per gid, and a module implementing hook_node_grants adds realms.
The default admin theme is independent of the default front-end theme; both are set in Appearance, and a base theme's templates are inherited.
Module and Theme Development
A module requires an .info.yml file with name, type: module, core_version_requirement, and a .module file for procedural hooks; PSR-4 classes go under src/ with a matching namespace.
A theme requires .info.yml with type: theme and a base theme key; libraries.yml declares CSS/JS assets and dependencies, attached via {{ attach_library() }} or a #attached array.
hook_theme() declares theme hooks and their variables; templates named {hook}.html.twig live in templates/ and receive only the declared variables plus preprocessing additions.
Twig autoescape is on by default; use the |raw filter only for trusted markup, and use t() or the |t filter for translatable strings.
hook_form_alter() and hook_form_FORM_ID_alter() modify forms; the $form_state is an object, and submit handlers are added to $form['actions']['submit']['#submit'].
The render array keys #theme, #markup, #type, #cache, #attached, and #access control output; #access FALSE removes the element before rendering.
Plugin managers discover plugins via annotations or attributes in a namespace declared in the module's *.services.yml; derivatives generate dynamic plugin IDs.
Services are registered in {module}.services.yml and injected via the create() factory or autowiring; \Drupal::service() is for procedural code only.
Drush commands live in src/Commands and use PHP attributes (#[CLI\Command]) or an annotated class; the command name defaults to the class name in kebab-case.
Drupal APIs and Data Modeling
Entity queries (\Drupal::entityQuery()) return IDs and respect access by default; set accessCheck(FALSE) only when access has been verified elsewhere.
The database API uses the query builder: db_select()/\Drupal::database()->select() with join(), condition(), and range(); static queries use db_query() with named placeholders.
Field storage definitions live in the field_storage_config config entity; field instances (bundle-specific) live in field_config; schema is generated into field tables.
hook_schema() defines custom tables; the Schema API types map to driver-specific SQL, and updates run via hook_update_N() with sequential numbering.
The Entity API's base classes are ContentEntityBase and ConfigEntityBase; annotations or #[ContentEntityType] attributes declare entity keys, bundles, and handlers.
Typed Data and the Field API back entity fields; computed fields implement ComputedItemListTrait and are not stored in the database.
hook_entity_presave(), hook_entity_insert(), and hook_entity_update() fire during entity lifecycle; use the entity's original property to detect changes.
Configuration is read via \Drupal::config() (immutable) and modified via getEditable(); config schema in config/schema/*.schema.yml enables validation and translation.
The batch API and queue API handle long operations; QueueWorker plugins process items via cron or a queue worker manager, with lease time controlling re-claim.
Performance, Caching, and Security
Render array #cache keys include keys, contexts, tags, and max-age; max-age 0 means uncacheable, and Cache::PERMANENT means no expiry.
Cache contexts (user.roles, url.path, url.query_args) vary cached output; cache tags (node:5, config:system.site) invalidate entries when referenced data changes.
The Dynamic Page Cache caches rendered responses for anonymous and authenticated users; the Internal Page Cache serves only anonymous requests.
Cache bins are declared in settings or services; the default database backend can be replaced with a memory or key-value backend for high-traffic sites.
BigPipe streams placeholders and flushes early, improving perceived performance; it requires the render pipeline to leave cacheable placeholders.
Twig template and render cache are separate; in development set twig.config debug/auto_reload and disable render cache, but never in production.
Output must be escaped or passed through Xss::filter(); SQL must use placeholders or the query builder to prevent injection.
CSRF protection uses a per-session token verified by _csrf_token route requirements; forms include a form_token and form_build_id.
File permissions and trusted host patterns (settings trusted_host_patterns) protect against host header attacks; private files use a private:// stream wrapper.
Acquia Platform and Deployment Workflow
Acquia Cloud environments are Dev, Stage, and Prod; code moves via Git branches, and databases/files move via the platform's environment copy or backup restore.
The Acquia Cloud API and the acquia-cli/acquia-cloud-cli tools automate environment operations; credentials are managed per user and never committed.
Acquia Cloud IDE provides a containerized development environment; commits push to the platform's Git remote and trigger a pipeline deployment.
Acquia Cloud Hooks (hooks/common/post-code-deploy, post-db-copy) run scripts during events; they execute on the application servers and must be idempotent.
Configuration management: export config in Dev, commit to Git, import on Stage and Prod; config_split separates environment-specific config.
Acquia Cloud Next / Cloud Platform uses pipelines with automated and manual stages; database updates run via drush updb in post-code-deploy hooks.
Acquia Search replaces the core search backend; the connector module and index configuration must be set per environment, and indexes rebuild after config changes.
Varnish sits in front of the web tier; cache tags and purge configuration control invalidation, and Acquia Purge integrates with Drupal's cache tag system.
BLT (Build and Launch Tool) and Acquia's Composer-based workflows require a composer.json with drupal-scaffold; the docroot is typically docroot/.
Drupal Fundamentals and Site Building
A content type is a bundle of the node entity type; fields attach to bundles, so the same field name can exist on multiple bundles with separate storage.
Taxonomy vocabularies hold terms; a term reference field can target one or more vocabularies, and a single-value field stores one delta=0 row in its field table.
Views are config entities; a page display has a path and menu settings, a block display is placed via Block Layout, and caching is controlled per display.
Aliases are managed by the Pathauto and Redirect modules; the path_alias entity stores alias-to-system-path mappings and survives content edits.
Content moderation applies workflows to entity bundles; each workflow defines states and transitions, and revisions carry the moderation state.
Node access is grant-based: node_access rows are written per node per realm per gid, and a module implementing hook_node_grants adds realms.
The default admin theme is independent of the default front-end theme; both are set in Appearance, and a base theme's templates are inherited.
Module and Theme Development
A module requires an .info.yml file with name, type: module, core_version_requirement, and a .module file for procedural hooks; PSR-4 classes go under src/ with a matching namespace.
A theme requires .info.yml with type: theme and a base theme key; libraries.yml declares CSS/JS assets and dependencies, attached via {{ attach_library() }} or a #attached array.
hook_theme() declares theme hooks and their variables; templates named {hook}.html.twig live in templates/ and receive only the declared variables plus preprocessing additions.
Twig autoescape is on by default; use the |raw filter only for trusted markup, and use t() or the |t filter for translatable strings.
hook_form_alter() and hook_form_FORM_ID_alter() modify forms; the $form_state is an object, and submit handlers are added to $form['actions']['submit']['#submit'].
The render array keys #theme, #markup, #type, #cache, #attached, and #access control output; #access FALSE removes the element before rendering.
Plugin managers discover plugins via annotations or attributes in a namespace declared in the module's *.services.yml; derivatives generate dynamic plugin IDs.
Services are registered in {module}.services.yml and injected via the create() factory or autowiring; \Drupal::service() is for procedural code only.
Drush commands live in src/Commands and use PHP attributes (#[CLI\Command]) or an annotated class; the command name defaults to the class name in kebab-case.
Drupal APIs and Data Modeling
Entity queries (\Drupal::entityQuery()) return IDs and respect access by default; set accessCheck(FALSE) only when access has been verified elsewhere.
The database API uses the query builder: db_select()/\Drupal::database()->select() with join(), condition(), and range(); static queries use db_query() with named placeholders.
Field storage definitions live in the field_storage_config config entity; field instances (bundle-specific) live in field_config; schema is generated into field tables.
hook_schema() defines custom tables; the Schema API types map to driver-specific SQL, and updates run via hook_update_N() with sequential numbering.
The Entity API's base classes are ContentEntityBase and ConfigEntityBase; annotations or #[ContentEntityType] attributes declare entity keys, bundles, and handlers.
Typed Data and the Field API back entity fields; computed fields implement ComputedItemListTrait and are not stored in the database.
hook_entity_presave(), hook_entity_insert(), and hook_entity_update() fire during entity lifecycle; use the entity's original property to detect changes.
Configuration is read via \Drupal::config() (immutable) and modified via getEditable(); config schema in config/schema/*.schema.yml enables validation and translation.
The batch API and queue API handle long operations; QueueWorker plugins process items via cron or a queue worker manager, with lease time controlling re-claim.
Performance, Caching, and Security
Render array #cache keys include keys, contexts, tags, and max-age; max-age 0 means uncacheable, and Cache::PERMANENT means no expiry.
Cache contexts (user.roles, url.path, url.query_args) vary cached output; cache tags (node:5, config:system.site) invalidate entries when referenced data changes.
The Dynamic Page Cache caches rendered responses for anonymous and authenticated users; the Internal Page Cache serves only anonymous requests.
Cache bins are declared in settings or services; the default database backend can be replaced with a memory or key-value backend for high-traffic sites.
BigPipe streams placeholders and flushes early, improving perceived performance; it requires the render pipeline to leave cacheable placeholders.
Twig template and render cache are separate; in development set twig.config debug/auto_reload and disable render cache, but never in production.
Output must be escaped or passed through Xss::filter(); SQL must use placeholders or the query builder to prevent injection.
CSRF protection uses a per-session token verified by _csrf_token route requirements; forms include a form_token and form_build_id.
File permissions and trusted host patterns (settings trusted_host_patterns) protect against host header attacks; private files use a private:// stream wrapper.
Acquia Platform and Deployment Workflow
Acquia Cloud environments are Dev, Stage, and Prod; code moves via Git branches, and databases/files move via the platform's environment copy or backup restore.
The Acquia Cloud API and the acquia-cli/acquia-cloud-cli tools automate environment operations; credentials are managed per user and never committed.
Acquia Cloud IDE provides a containerized development environment; commits push to the platform's Git remote and trigger a pipeline deployment.
Acquia Cloud Hooks (hooks/common/post-code-deploy, post-db-copy) run scripts during events; they execute on the application servers and must be idempotent.
Configuration management: export config in Dev, commit to Git, import on Stage and Prod; config_split separates environment-specific config.
Acquia Cloud Next / Cloud Platform uses pipelines with automated and manual stages; database updates run via drush updb in post-code-deploy hooks.
Acquia Search replaces the core search backend; the connector module and index configuration must be set per environment, and indexes rebuild after config changes.
Varnish sits in front of the web tier; cache tags and purge configuration control invalidation, and Acquia Purge integrates with Drupal's cache tag system.
BLT (Build and Launch Tool) and Acquia's Composer-based workflows require a composer.json with drupal-scaffold; the docroot is typically docroot/.