Drupal at your fingertips
Free!
With Membership
$40.00
Minimum paid price

Drupal at your fingertips

A Drupal 9 & 10 developer's quick code reference

About the Book

This book is a quick reference for developers creating Drupal sites. While working on such sites during my career, I have gathered a large collection of notes and code which I use for my reference. I hope it will be useful for you, too. I've provided tested code samples for almost every topic. My goal is to save you having to dig through lots of search results to find the code snippet you need to get your job done.

  • Share this book

  • Categories

    • Computers and Programming
    • PHP
    • MySQL
    • Web Development
    • Software Engineering
    • HTML
    • JavaScript
    • Software
    • Automated Software Testing
  • Feedback

    Email the Author(s)

About the Author

Selwyn Polit
Selwyn Polit

Selwyn has over two decades' experience building and designing software applications. He is a passionate proponent of open source development, and has been working with Drupal since version 4.7. Currently focused on back-end Drupal development for progressive, mission-driven organizations and businesses, Selwyn has worked with many software languages and environments in both the for-profit and non-profit worlds, including PHP, MySQL, Javascript, Oracle, and Linux. Originally from Cape Town, South Africa, Selwyn lives with his son in Austin, TX. Selwyn's other areas of expertise include electrical engineering, psychology, Permaculture, and building Indonesian- and Japanese-style tea houses. You can find Selwyn on LinkedIn, and as @selwynpolit on Twitter and Drupal.org.

Table of Contents

    • 1:Introduction
    • 2:Blocks
      • 2.1:Create a block with Drush generate
      • 2.2:Anatomy of a custom block with dependency injection
      • 2.3:Create a block with an entityQuery
      • 2.4:Create a Block with a corresponding config form
        • 2.4.1:The config form definition
        • 2.4.2:The routing.yml file
        • 2.4.3:The Block definition
      • 2.5:Modify a block with hook_block_view_alter or hook_block_build_alter
      • 2.6:Disable caching in a block
      • 2.7:Add a configuration form to your block
      • 2.8:Block display not updating after changing block content
      • 2.9:Block Permission (blockAccess)
        • 2.9.1:Blocks shouldn’t talk to the router, NodeRouteContext and friends should
        • 2.9.2:Values returned by blockAccess()
    • 3:Batch Processing and the Drupal Queue System
      • 3.1:Batch Processing Using the Batch API
        • 3.1.1:Overview
        • 3.1.2:Using the Batch API with a form
        • 3.1.3:Using the Batch API from a controller
        • 3.1.4:Using the Batch API with hook_update
        • 3.1.5:Important rules about functions when using Batch API
        • 3.1.6:Looking at the source
          • 3.1.6.1:Passing parameters to the functions in a batch operation
      • 3.2:Queue System
      • 3.3:Resources
    • 4:Caching and cache tags
      • 4.1:How to uncache a particular page or node
      • 4.2:Don’t cache data returned from a controller
      • 4.3:Disable caching for a content type
      • 4.4:Considering caching when retrieving query, get or post parameters
      • 4.5:Debugging Cache tags
      • 4.6:Using cache tags
      • 4.7:Setting cache keys in a block
      • 4.8:Getting Cache Tags and Contexts for a block
      • 4.9:Caching REST Resources
      • 4.10:Caching in an API class wrapper
      • 4.11:Caching in a .module file
      • 4.12:Logic for caching render arrays
      • 4.13:Development Setup
        • 4.13.1:Disable caching and enable TWIG debugging
        • 4.13.2:Disable Cache for development
      • 4.14:How to specify the cache backend
        • 4.14.1:class ChainedFastBackend
        • 4.14.2:APCu
      • 4.15:Reference
    • 5:Composer, Updates and Patches
      • 5.1:Creating a local patch to a contrib module
      • 5.2:Patch modules using patches on Drupal.org
        • 5.2.1:Step by step
      • 5.3:Patches from a Gitlab merge request
      • 5.4:composer.json patches in separate file
      • 5.5:Stop files being overwritten during composer operations
      • 5.6:Updating Drupal Core
      • 5.7:Test composer (dry run)
      • 5.8:Version constraints
      • 5.9:Allowing multiple versions
      • 5.10:Troubleshooting
        • 5.10.1:Composer won't update Drupal core
        • 5.10.2:The big reset button
      • 5.11:Reference
    • 6:Configuration and Settings
      • 6.1:Load some config
      • 6.2:Views
      • 6.3:Add config to an existing module
      • 6.4:Import something you changed in your module
      • 6.5:Config Storage in the database
      • 6.6:Add some config to site config form
      • 6.7:Override config in settings.php
      • 6.8:Setup a testing variable in config for a project
      • 6.9:Getting and setting configuration with drush
      • 6.10:Creating a module allowing users to edit/update some config
      • 6.11:Drush config commands
        • 6.11.1:View config
        • 6.11.2:Viewing overridden config values
        • 6.11.3:Delete from config
        • 6.11.4:Check what has changed with config:status
        • 6.11.5:Export entire config
        • 6.11.6:Import config changes
    • 7:CRON
      • 7.1:Overview
      • 7.2:How does it work?
      • 7.3:Enable Drupal Cron
      • 7.4:The cron command
      • 7.5:Setting up cron
      • 7.6:Disable Drupal cron
      • 7.7:hook_cron()
      • 7.8:Common inquiries regarding cron jobs
        • 7.8.1:When did the cron job last run?
        • 7.8.2:How to stop Cron from continuously executing things?
        • 7.8.3:Resolving the ip and name for cron
      • 7.9:Resources:
    • 8:Dates and Times
      • 8.1:Overview
      • 8.2:Retrieve a date field
      • 8.3:Retrieve date range field
      • 8.4:Formatting date range fields
      • 8.5:Formatting a date string with an embedded timezone
      • 8.6:Formatting a date range for display
      • 8.7:Saving date fields
      • 8.8:Create DrupalDateTime objects
        • 8.8.1:Create DrupalDateTime objects with timezones
      • 8.9:Create a DrupalDateTime object and display as a year only
      • 8.10:Formatting node created time with Drupal date.formatter service
      • 8.11:Date arithmetic example 1
      • 8.12:Date arithmetic example 2
      • 8.13:Comparing DrupalDateTime values
      • 8.14:Comparing dates (without comparing times)
      • 8.15:Comparing Dates to see if a node has expired
      • 8.16:Node creation and changed dates
      • 8.17:Query the creation date using entityQuery
      • 8.18:Query a date field with no time
      • 8.19:Query a date field with a time
      • 8.20:Smart Date
        • 8.20.1:Smart date: Load and format
        • 8.20.2:Smart date: all-day
        • 8.20.3:Smart date: Range of values
      • 8.21:Reference
        • 8.21.1:Date field storage
        • 8.21.2:DrupalDateTime API reference
        • 8.21.3:UTC
        • 8.21.4:Unix epoch timestamps
        • 8.21.5:Links
    • 9:Debugging
      • 9.1:Overview
      • 9.2:Enable error reporting
      • 9.3:Disable caches and enable Twig debugging
      • 9.4:Enable/Disable Xdebug
      • 9.5:Xdebug Port
      • 9.6:Drupal code debugging
      • 9.7:Command line or drush debugging
      • 9.8:Add a breakpoint in code
      • 9.9:Troubleshooting Xdebug with DDEV
        • 9.9.1:Could not connect to debugging client
        • 9.9.2:PhpStorm refuses to debug
          • 9.9.2.1:Curl
          • 9.9.2.2:Logs
          • 9.9.2.3:Telnet
          • 9.9.2.4:Is Xdebug enabled?
      • 9.10:What is listening on the debug port?
      • 9.11:Enable twig debugging output in source
      • 9.12:Devel and Devel Kint Extras
        • 9.12.1:Setup
        • 9.12.2:Add kint to a custom module
        • 9.12.3:Dump variables in a TWIG template
        • 9.12.4:Kint::dump
        • 9.12.5:Set max levels to avoid running out of memory
      • 9.13:Resources
    • 10:Development
      • 10.1:Local Drupal site setup
          • 10.1.0.1:First Option
          • 10.1.0.2:Second Option
      • 10.2:Checking Your Permissions
      • 10.3:Converting existing site (non-composer based) to use composer
      • 10.4:Composer best practices for Drupal 8
      • 10.5:DDEV
        • 10.5.1:Local config - your .ddev/config.local.yaml
          • 10.5.1.1:NFS
          • 10.5.1.2:Mutagen
        • 10.5.2:setup aliases in ddev
        • 10.5.3:Upgrading ddev
        • 10.5.4:Show others your ddev local site
        • 10.5.5:Email Capture and Review
        • 10.5.6:DDEV and Xdebug
        • 10.5.7:Command line or drush debugging
        • 10.5.8:Use drush commands in your shell with DDEV
        • 10.5.9:Load your data from an Acquia site
        • 10.5.10:Cleanup some disk space
        • 10.5.11:Accessing specific containers
      • 10.6:DDEV Troubleshooting
        • 10.6.1:Running out of docker disk space
        • 10.6.2:DDEV won’t start
      • 10.7:PHPStorm
        • 10.7.1:Setting up PHPStorm and Drupal
        • 10.7.2:PHPStorm and Xdebug
          • 10.7.2.1:add a breakpoint in code
        • 10.7.3:Collecting PhpStorm debugging logs
      • 10.8:Troubleshooting Xdebug with DDEV
      • 10.9:What is listening on port 9000?
      • 10.10:Setup settings.local.php and disable Cache
      • 10.11:Development.services.yml
      • 10.12:Enable twig debugging output in source
      • 10.13:Kint
        • 10.13.1:Setup
        • 10.13.2:Add kint to a custom module
        • 10.13.3:Dump variables in a TWIG template
        • 10.13.4:Kint::dump
        • 10.13.5:Set max levels to avoid running out of memory
      • 10.14:Replacing deprecated functions
      • 10.15:Missing module
      • 10.16:You have requested a non-existent service
      • 10.17:Resources
    • 11:Email
      • 11.1:Send email
      • 11.2:Using tokens in hook_mail
      • 11.3:Reference
    • 12:Entities
      • 12.1:Overview
      • 12.2:Config entity types
      • 12.3:Content entity types
      • 12.4:Query an entity by title and type
      • 12.5:Create an entity
      • 12.6:Save an entity
      • 12.7:Create article node entity with attached image
      • 12.8:Update a node entity and add some terms
      • 12.9:Get the entity type and content type (or bundle type)
      • 12.10:Identify entities
      • 12.11:Create a file entity
      • 12.12:Entity Validation
      • 12.13:Resources
    • 13:Forms, Form API and AJAX
      • 13.1:Overview
      • 13.2:Find a form id in the page source
      • 13.3:Add buttons to your custom forms
      • 13.4:Modify a button on a form with hook_form_alter
      • 13.5:Hide a field with hook_form_alter
      • 13.6:Hide revision info and moderation state
      • 13.7:Multiple fields on the same controller/page
      • 13.8:Conditional fields and field states API (#states)
        • 13.8.1:Conditional fields in a form
        • 13.8.2:Conditional fields in node add or edit form
      • 13.9:Get the key and value from a select drop-down
      • 13.10:Autocomplete
        • 13.10.1:Add an autocomplete taxonomy field
        • 13.10.2:Add a views-driven entity autocomplete field
        • 13.10.3:Disable autocomplete for user login and password fields
      • 13.11:Validating input
        • 13.11.1:Validate string length
        • 13.11.2:Validate an email
        • 13.11.3:Validate date
        • 13.11.4:Validate a node add or edit
      • 13.12:Displaying Forms
        • 13.12.1:Embedding a form:
        • 13.12.2:Show a form in a block
        • 13.12.3:Provide a block template for a form in a block
      • 13.13:Redirecting
        • 13.13.1:Form submission with redirect
        • 13.13.2:Ajax redirect
        • 13.13.3:AJAX redirect from a select element (dropdown)
      • 13.14:Add Javascript to a form
      • 13.15:AJAX Forms
        • 13.15.1:Popup an AJAX modal dialog
        • 13.15.2:AJAX modal dialog with redirect example
          • 13.15.2.1:Ajax submit
          • 13.15.2.2:Ajax redirect
        • 13.15.3:AJAX redirect from a select element (dropdown)
        • 13.15.4:Update a value in another field(I am I want) using AJAX
        • 13.15.5:I Am I Want revisited
          • 13.15.5.1:Custom responses
        • 13.15.6:How do you find all the possible AJAX commands to use with addCommand()?
        • 13.15.7:Another AJAX Submit example
      • 13.16:Config Forms
        • 13.16.1:Generate a config form with drush
        • 13.16.2:Config forms overview
      • 13.17:The basics of implementing forms
        • 13.17.1:Location
        • 13.17.2:Base Classes for forms
        • 13.17.3:Create your form class by extending Formbase
        • 13.17.4:The main methods
          • 13.17.4.1:getFormId()
          • 13.17.4.2:buildForm()
          • 13.17.4.3:submitForm()
        • 13.17.5:Form validation example #1
        • 13.17.6:Form Validation example #2
        • 13.17.7:Field attributes
        • 13.17.8:Form Elements
        • 13.17.9:Retrieving field values
      • 13.18:Resources
    • 14:General
      • 14.1:Get the current user
      • 14.2:Get the logged in user name and email
      • 14.3:Check if you are on the Front page
      • 14.4:Check if site is in system maintenance mode
      • 14.5:Get Node URL alias or Taxonomy Alias by Node id or Term ID
      • 14.6:Taxonomy alias
      • 14.7:Get current Path
      • 14.8:Get current nid, node type and title
      • 14.9:How to check whether a module is installed or not
      • 14.10:Get current Route name
      • 14.11:Get the current page title
      • 14.12:Get the current user
      • 14.13:Check if you are on the Front page
      • 14.14:Check if site in system maintenance mode
      • 14.15:Retrieve query and get or post parameters ($_POST and $_GET)
      • 14.16:Retrieve URL argument parameters
      • 14.17:Get Current Language in a constructor
      • 14.18:Add a variable to any page on the site
      • 14.19:Add a variable to be rendered in a node.
      • 14.20:Add a bunch of variables to be rendered in a node
      • 14.21:Grabbing entity reference fields in hook_preprocess_node for injection into the twig template
      • 14.22:Render a list created in the template_preprocess_node()
      • 14.23:Indexing paragraphs so you can theme the first one
      • 14.24:Add meta tags using template_preprocess_html
      • 14.25:How to strip % characters from a string
      • 14.26:Remote media entities
      • 14.27:Deprecated functions like drupal_set_message
    • 15:Hooks
      • 15.1:Overview
      • 15.2:Modify the login form
      • 15.3:Modify the node edit form
      • 15.4:Modify fields in a node
      • 15.5:hook_update
      • 15.6:Theme hooks
        • 15.6.1:Hook_preprocess
        • 15.6.2:hook_preprocess_node example 1
        • 15.6.3:hook_preprocess_node example 2
      • 15.7:Organizing your hooks code the OOP way
      • 15.8:Reference
        • 15.8.1:Entity hooks
          • 15.8.1.1:Create operations
          • 15.8.1.2:Read/Load operations
          • 15.8.1.3:Save operations
          • 15.8.1.4:Editing operations
          • 15.8.1.5:Delete operations
          • 15.8.1.6:View/render operations
          • 15.8.1.7:Other entity hooks
        • 15.8.2:Theme hooks
          • 15.8.2.1:Overriding Theme Hooks
          • 15.8.2.2:Preprocessing for Template Files
          • 15.8.2.3:Theme hook suggestions
          • 15.8.2.4:Altering theme hook suggestions
        • 15.8.3:Reference Links
    • 16:Learning and keeping up with Drupal
      • 16.1:Free videos
      • 16.2:Blogs and articles
      • 16.3:Pay videos
      • 16.4:Drupal Training
      • 16.5:Keep up with Drupal news
      • 16.6:Drupal Podcasts
      • 16.7:Books
    • 17:Links, Aliases and URLs
      • 17.1:Create an external url
      • 17.2:Create an internal url
      • 17.3:The Drupal Core Url Class
      • 17.4:The Drupal Core Link Class
        • 17.4.1:Create a link to a node
        • 17.4.2:Create a link to a path with parameters
      • 17.5:Another way to create a link to a node:
      • 17.6:Create a link from an internal URL
      • 17.7:Check if a link field is empty
      • 17.8:Retrieve a link field from a node or a paragraph
      • 17.9:Retrieve a URL field
        • 17.9.1:External links
        • 17.9.2:Internal links
      • 17.10:Get the NID from a URL Alias
      • 17.11:Get the Taxonomy Term ID from a URL alias
      • 17.12:Get URL alias for a taxonomy term
      • 17.13:Get the User ID from a URL alias
      • 17.14:Get the URL alias for a node
      • 17.15:Create a Node Alias
      • 17.16:Get the current Path
      • 17.17:Get current nid, node type and title
      • 17.18:How to get current Route name
      • 17.19:Get current Document root path
      • 17.20:Retrieve URL argument parameters
      • 17.21:Retrieve query and GET or POST parameters ($_POST and $_GET)
      • 17.22:Modify URL Aliases programmatically with hook_pathauto_alias_alter
      • 17.23:Drupal l() is deprecated
      • 17.24:Reference links
    • 18:Logging
      • 18.1:Quick log to watchdog
      • 18.2:Log an email notification was sent to the the email address for the site.
      • 18.3:Logging from a service using dependency injection
      • 18.4:Another example using the logging via dependency injection
      • 18.5:Logging exceptions from a try catch block
      • 18.6:Display a message in the notification area
      • 18.7:Display a variable while debugging
      • 18.8:Reference
    • 19:Menus
      • 19.1:Dynamically change menu items with hook_preprocess_menu
      • 19.2:Permanently update menu links in a hook_update using entityQuery
      • 19.3:Add menu items with hook_update
      • 19.4:Permanently modify or delete menu items with hook_update
      • 19.5:Peer up a menu to its parents to see if it is a child of a content type
      • 19.6:Find all the children of a menu
      • 19.7:Build a menu and all its children
      • 19.8:Create custom Twig extension for rendering a menu
      • 19.9:Active Trail
      • 19.10:Get a node’s menu item and more
      • 19.11:Create menu items in your custom module
      • 19.12:Resources
    • 20:Migration
      • 20.1:Import content from another Migration into Paragraphs
      • 20.2:Resources
    • 21:Modal Dialogs
      • 21.1:Overview
      • 21.2:Dialog title
      • 21.3:Links to slide-in dialogs
      • 21.4:Modal dialog example
        • 21.4.1:Passing entities as parameters
      • 21.5:Modal form example
      • 21.6:Slide-in dialog/Off-canvas dialog
      • 21.7:Slide-in Dialog Example
      • 21.8:Block with a link to popup a custom modal dialog
      • 21.9:No-code modal dialogs
      • 21.10:Resources
    • 22:Nodes and Fields
      • 22.1:Load a node and get a formatted text field
      • 22.2:Load a numeric field value
      • 22.3:Set field values
      • 22.4:Get current page title
      • 22.5:Test if variable is a node
      • 22.6:Get the current nid, node type and title
      • 22.7:Retrieve current node id (nid)
      • 22.8:Retrieve node info from current path
      • 22.9:Load the current node and get it’s node id (nid), field, type
      • 22.10:Load a node by nid and get its title, type and a field
      • 22.11:Load the current node and get the nid, field, type
      • 22.12:Load the user id (uid) for a node
      • 22.13:Test if a field is empty
      • 22.14:Load a node and update a field
      • 22.15:Load values from a date range field
      • 22.16:Load multivalue field
        • 22.16.1:Iterate through results
        • 22.16.2:Read a specific instance
      • 22.17:Update a multivalue field
        • 22.17.1:Function to read and write multivalue fields
        • 22.17.2:Save multivalue field, entity reference field
        • 22.17.3:Update a multivalue entity reference fields
        • 22.17.4:Generic Multivalue field writer
      • 22.18:Does this field exist in my entity?
      • 22.19:Get URL for an image or file in a media reference field
      • 22.20:Retrieve info about a file field
      • 22.21:Retrieve a link field
      • 22.22:Does this field exist in my entity?
      • 22.23:Create a node and write it to the database
      • 22.24:Create a node with an image
      • 22.25:Write a node with an attached file
      • 22.26:Write a date or datetime to a node
      • 22.27:Or just a date (no time)
      • 22.28:Set field values
      • 22.29:Set an entity reference field
      • 22.30:Set multivalue fields (regular and entity reference)
      • 22.31:Clear a text field
      • 22.32:Set or clear a body field
      • 22.33:Load a node and retrieve an entity reference node and nid (target_id)
        • 22.33.1:Load a multivalue reference field.
      • 22.34:Entity reference nodes and their fields
      • 22.35:Load the taxonomy terms from a term reference field
      • 22.36:Load a node and find the terms referenced in a paragraph in a term reference field
      • 22.37:Retrieve a URL field
        • 22.37.1:External links
        • 22.37.2:Internal links
      • 22.38:Load a node and retrieve a paragraph field
      • 22.39:How to get Node URL alias or Taxonomy Alias by Node id or Term ID
      • 22.40:How to set a URL Alias
      • 22.41:Get a node’s menu item and more
      • 22.42:Find a node using it’s uuid
      • 22.43:Retrieve Node ID(NID) or Taxonomy term ID from a Drupal alias or path
      • 22.44:Retrieve all nodes with a matching taxonomy term
      • 22.45:How to uncache a particular page or node
      • 22.46:Get boolean Field
      • 22.47:Date Field
      • 22.48:Date Fields
      • 22.49:Date Range Field doesn’t display correct timezone
      • 22.50:Date Range
      • 22.51:Date Range fields: Load start and end values
      • 22.52:Date Fields: Load or save them
      • 22.53:Comparing DrupalDateTime values
      • 22.54:Date with embedded timezone
      • 22.55:Has something expired?
      • 22.56:Load or save Drupal Date fields
      • 22.57:Retrieve node creation date and format it
      • 22.58:Retrieve node creation or changed date and format it
      • 22.59:Date Field and Date with no time (remove time)
      • 22.60:Smart date (smart_date) load and format
      • 22.61:Smart date (smart_date) all-day
      • 22.62:Smart date (smart_date) range of values
      • 22.63:hook_node_presave or hook_entity_type_presave
      • 22.64:Disable caching for a content type
      • 22.65:Writing some JSON data into a long text field
      • 22.66:Create a node with an image
      • 22.67:Paragraphs
      • 22.68:Load a node and find the terms referenced in a paragraph in a term reference field
      • 22.69:Custom Field Formatter
      • 22.70:Puzzles
        • 22.70.1:What can I do with a call to first() on an entity reference field?
      • 22.71:Great Cheat sheets
    • 23:Getting off the Island (formerly Reaching out of Drupal)
      • 23.1:Overview
      • 23.2:Guzzle example
      • 23.3:Guzzle POST example
      • 23.4:Magic methods to send synchronous requests
      • 23.5:HTTP basic authentication
      • 23.6:Exception handling
      • 23.7:Guzzle Exceptions
      • 23.8:HTTP response status codes
      • 23.9:Reading from an API
      • 23.10:Download a file using guzzle
      • 23.11:Download a file using curl in PHP
      • 23.12:Resources
    • 24:Queries
      • 24.1:entityQuery
        • 24.1.1:Find matching nodes - example 1
        • 24.1.2:Find matching nodes - example 2
        • 24.1.3:Find matching article nodes–example 3
        • 24.1.4:Find nodes that match a taxonomy term
        • 24.1.5:Find 5 nodes that have a matching taxonomy term
        • 24.1.6:Find matching nodes and delete them
        • 24.1.7:Slice up entityQuery results into batches of 100 nodes
        • 24.1.8:Query the creation date (among other things) using entityQuery
        • 24.1.9:entityQuery frequently used conditions
        • 24.1.10:Update menu items programatically
        • 24.1.11:Query multi-value fields
        • 24.1.12:Query entity reference fields if they have a value or no value
        • 24.1.13:Query entity reference fields
      • 24.2:Static and dynamic Queries
        • 24.2.1:Static Queries
        • 24.2.2:Get a connection object
        • 24.2.3:SQL select example
        • 24.2.4:Find the biggest value in a field
        • 24.2.5:SQL update query - example 1
        • 24.2.6:SQL update query - example 2
        • 24.2.7:SQL update query - example 3
        • 24.2.8:SQL insert
        • 24.2.9:SQL Insert Query
        • 24.2.10:SQL Delete query
        • 24.2.11:Paragraph query
        • 24.2.12:Create a custom table for your module
      • 24.3:Reference
    • 25:Redirects
      • 25.1:Redirect to an internal url
      • 25.2:Redirect in a form
      • 25.3:Redirect off-site (to a third-party URL)
      • 25.4:Redirect to an existing route with an anchor (or fragment)
      • 25.5:Redirect to a complex route
      • 25.6:Redirect in a controller
      • 25.7:Redirect user after login
      • 25.8:Redirect to the 403 or 404 page
      • 25.9:Redirect to a new page after node operation
      • 25.10:Redirect dynamically to wherever you came from
    • 26:Render Arrays
      • 26.1:Overview
      • 26.2:Overview of the Theme system and Render API.
      • 26.3:Caching
      • 26.4:Properties
      • 26.5:Image
      • 26.6:Simple Text
      • 26.7:Text with variable substitution (Placeholders)
      • 26.8:Wrap an element with a div with a class
      • 26.9:Prefix and suffix
      • 26.10:Date
      • 26.11:Image
      • 26.12:Several Url’s.
      • 26.13:Two paragraphs
      • 26.14:A button that opens a modal dialog
      • 26.15:A link
      • 26.16:A link with a class
      • 26.17:A link and its TWIG template
      • 26.18:A link with parameters and a template file
      • 26.19:Simple unordered list
      • 26.20:Unordered list of links for a menu
      • 26.21:Nested Unordered List
      • 26.22:Select (dropdown)
      • 26.23:Select (dropdown) Ajaxified
      • 26.24:Limit allowed tags in markup
      • 26.25:Disable an element
      • 26.26:Resources
    • 27:Routes and Controllers
      • 27.1:Overview
        • 27.1.1:Route
        • 27.1.2:Controller
        • 27.1.3:Connecting to a twig template
      • 27.2:Simple page without arguments
      • 27.3:Page with arguments
      • 27.4:Simple form
      • 27.5:Admin form (or settings form)
      • 27.6:Routing permissions
        • 27.6.1:A specific permission
        • 27.6.2:Multiple permissions
      • 27.7:Set the page title dynamically
      • 27.8:Disable caching on a route
      • 27.9:Generate route and controller with Drush
      • 27.10:Finding routes with Drush
        • 27.10.1:All routes
        • 27.10.2:Specific path
        • 27.10.3:Specific route name
      • 27.11:Getting some help from Chat GPT
      • 27.12:Resources
    • 28:Services and Dependency Injection
      • 28.1:Overview
      • 28.2:Static
      • 28.3:Static Shorthand methods
      • 28.4:Services in action
      • 28.5:ControllerBase shortcuts
      • 28.6:Injected/Dependency Injection
        • 28.6.1:Controller details
        • 28.6.2:Controller Example 1
        • 28.6.3:Controller Example 2
      • 28.7:Finding services
      • 28.8:Creating a custom service
        • 28.8.1:Arguments
        • 28.8.2:Passing the config factory to our service
        • 28.8.3:Taxonomy Tree Custom Service
      • 28.9:Using your custom service
      • 28.10:Dependency Injection
        • 28.10.1:Overview
        • 28.10.2:Service Container
        • 28.10.3:Controller Example 1
        • 28.10.4:Controller Example 2
        • 28.10.5:Blocks and other plugins
      • 28.11:Procedural to Class-based dependency injection
      • 28.12:Drush services commands
        • 28.12.1:List all services
        • 28.12.2:Generate custom service
      • 28.13:Resources
    • 29:State API, TempStore and UserData
      • 29.1:Overview
      • 29.2:State API
        • 29.2.1:Using Drush to read the State API
        • 29.2.2:Example accessing State API
        • 29.2.3:Long strings broken into paragraphs
      • 29.3:UserData
      • 29.4:TempStore
        • 29.4.1:PrivateTempStore
        • 29.4.2:SharedTempStore
          • 29.4.2.1:Injecting tempstore.shared
      • 29.5:Reference
    • 30:Taxonomy
      • 30.1:Lookup term by name
      • 30.2:Lookup term name using its tid
      • 30.3:Lookup term using its uuid
      • 30.4:Load terms from a term reference field
      • 30.5:Find terms referenced in a paragraph in a term reference field
      • 30.6:Get URL alias from a term ID
      • 30.7:Load all terms for a vocabulary
      • 30.8:Load all terms for a vocabulary and put them in a select (dropdown)
      • 30.9:Create taxonomy term programatically
      • 30.10:Find all nodes with a matching term
      • 30.11:Find nodes with a matching term using entityQuery
    • 31:TWIG
      • 31.1:Overview
        • 31.1.1:Theme System Overview
        • 31.1.2:Twig Templating Engine
      • 31.2:Displaying Data
        • 31.2.1:Fields or Logic
        • 31.2.2:Which template, which variables?
        • 31.2.3:Display fields or variables
        • 31.2.4:Node Title with and without a link
        • 31.2.5:Fields
        • 31.2.6:Paragraph field
        • 31.2.7:Loop thru paragraph reference fields
        • 31.2.8:Body
        • 31.2.9:Multi-value fields
        • 31.2.10:Fields with HTML
        • 31.2.11:The date/time a node is published, updated or created
        • 31.2.12:Format a date field
        • 31.2.13:Smart date field formatting
        • 31.2.14:Entity Reference field
        • 31.2.15:Entity reference destination content
        • 31.2.16:Taxonomy term
        • 31.2.17:Render a block
        • 31.2.18:Render a list created in the template_preprocess_node()
        • 31.2.19:Links
        • 31.2.20:Links to other pages on site
        • 31.2.21:Link to a user using user id
        • 31.2.22:External link in a field via an entity reference
        • 31.2.23:Render an internal link programatically
        • 31.2.24:Render an image with an image style
        • 31.2.25:Hide if there is no content in a field or image
        • 31.2.26:Hide if there is no image present
        • 31.2.27:Attributes
        • 31.2.28:Output the content but leave off the field_image
        • 31.2.29:Add a class
        • 31.2.30:Add a class conditionally
        • 31.2.31:Links to other pages on site
        • 31.2.32:Loop.index in a paragraph twig template
        • 31.2.33:Loop thru an array of items with a separator
      • 31.3:Add Javascript into a twig template
      • 31.4:Control/Logic
        • 31.4.1:Concatenate values into a string with join
        • 31.4.2:Include partial templates
        • 31.4.3:Loop through entity reference items
        • 31.4.4:IF OR
        • 31.4.5:Test if a formatted text field is empty
        • 31.4.6:Test empty variable
        • 31.4.7:Conditionals (empty, defined, even)
        • 31.4.8:Test if a paragraph is empty using striptags
        • 31.4.9:Comparing strings
        • 31.4.10:Include other templates as partials
        • 31.4.11:Check if an attribute has a class
        • 31.4.12:Remove an attribute
        • 31.4.13:Convert attributes to array
      • 31.5:Views
        • 31.5.1:Render a view with contextual filter
        • 31.5.2:Count how many rows returned from a view
        • 31.5.3:If view results empty, show a different view
        • 31.5.4:Selectively pass 1 termid or 2 to a view as the contextual filter
        • 31.5.5:Views templates
        • 31.5.6:Inject variables
          • 31.5.6.1:Same field used twice
        • 31.5.7:Concatenate values into a string with join
        • 31.5.8:Loop through entity reference items
      • 31.6:Twig filters and functions
      • 31.7:Twig Tweak
        • 31.7.1:Documentation
        • 31.7.2:Display a block with twig_tweak
        • 31.7.3:Display filter form block
        • 31.7.4:Embed view in twig template
        • 31.7.5:Some tricky quotes magic
      • 31.8:Troubleshooting
        • 31.8.1:Enable Twig debugging and disable caches
        • 31.8.2:Debugging - Dump a variable
        • 31.8.3:Dump taxonomy reference field
        • 31.8.4:Using kint or dump to display variable in a template
        • 31.8.5:502 bad gateway error
        • 31.8.6:Views error
        • 31.8.7:Striptags (when twig debug info causes if to fail)
      • 31.9:Reference

The Leanpub 60-day 100% Happiness Guarantee

Within 60 days of purchase you can get a 100% refund on any Leanpub purchase, in two clicks.

See full terms

80% Royalties. Earn $16 on a $20 book.

We pay 80% royalties. That's not a typo: you earn $16 on a $20 sale. If we sell 5000 non-refunded copies of your book or course for $20, you'll earn $80,000.

(Yes, some authors have already earned much more than that on Leanpub.)

In fact, authors have earnedover $12 millionwriting, publishing and selling on Leanpub.

Learn more about writing on Leanpub

Free Updates. DRM Free.

If you buy a Leanpub book, you get free updates for as long as the author updates the book! Many authors use Leanpub to publish their books in-progress, while they are writing them. All readers get free updates, regardless of when they bought the book or how much they paid (including free).

Most Leanpub books are available in PDF (for computers) and EPUB (for phones, tablets and Kindle). The formats that a book includes are shown at the top right corner of this page.

Finally, Leanpub books don't have any DRM copy-protection nonsense, so you can easily read them on any supported device.

Learn more about Leanpub's ebook formats and where to read them

Write and Publish on Leanpub

You can use Leanpub to easily write, publish and sell in-progress and completed ebooks and online courses!

Leanpub is a powerful platform for serious authors, combining a simple, elegant writing and publishing workflow with a store focused on selling in-progress ebooks.

Leanpub is a magical typewriter for authors: just write in plain text, and to publish your ebook, just click a button. (Or, if you are producing your ebook your own way, you can even upload your own PDF and/or EPUB files and then publish with one click!) It really is that easy.

Learn more about writing on Leanpub