Drupal 8 is coming, everyone is excited, and your developers can’t wait to use all the shiny new features. There’s just one problem: you still need to get your daily work done. Every day Drupal development requires the execution of many common patterns and scenarios.
How well do these common scenarios translate to Drupal 8? We polled Drupal developers to find what tasks we do the most and built a practical guide to using Drupal 8 in every day development.
In this presentation we’ll cover the following real world scenarios, while sprinkling in Drupal 8 best practices along the way:
- Configuration Management
- Creating and altering pages and forms
- Menu callback patterns
- Managing dependencies
3. Erin Marchak
● Acquia Certified Front End Specialist, and Developer
● dgo.to/@emarchak
Geoff Appleby
● Acquia Certified Developer
● dgo.to/@gapple
● Persistent Login & drupalreleasedate.com
4. 1. Unboxing
2. Installation
3. Configuration Management
a. Development Workflow
b. Export
c. Import
4. Development
a. Module Generation
b. Page Controllers
c. Entity Bundles
d. Forms
5. Theming
a. Theme Generation
b. Twig Syntax
c. Debugging
d. Including Assets
6. Unboxing Drupal 8
New Features
Configuration Management
Twig replaces PHPTemplate
PHPUnit supplements SimpleTest
Responsive base theme & admin
Fast by Default
New modules in core
Requirements
Minimum
PHP 5.5.9
MySQL 5.5.3
Recommended
PHP 5.6.5
APCu
New file structure
Core is now in ./core
Modules are now in ./modules
Profiles are now in ./profiles
Themes are now in ./themes
21. Development - ./src/Controller/GalleryController.php
/**
* @file
* Contains DrupalphotographyControllerGalleryController.
*/
namespace DrupalphotographyController;
use DrupalCoreControllerControllerBase;
/**
* Class GalleryController.
*
* @package DrupalphotographyController
*/
class GalleryController extends ControllerBase {
/**
* Helper function to load all published photographs.
*/
public function loadAllPhotos($bundle_type = 'photograph') {...}
/**
* Gallery display.
*/
public function gallery() {...}
}
23. Development - GalleryController->loadAllPhotos()
/**
* Helper function to load all published photographs.
*
* @param string $bundle_type
* @return DrupalCoreEntityEntityInterface[]
* Array of node objects keyed by nid.
*/
public function loadAllPhotos($bundle_type = 'photograph') {
// Return the entity manager service and load the the storage instance for nodes.
// That way we have access to the enity api while keeping our controller lean.
$storage = $this->entityManager()->getStorage('node');
// loadByProperties() allows us to query and load entities in one line!
return $storage->loadByProperties(array(
'type' => $bundle_type,
'status' => 1,
));
}
24. Development - GalleryController->gallery()
/**
* Gallery display.
*/
public function gallery() {
$langcode = $this->languageManager()->getCurrentLanguage('language');
$photographs = $this->loadAllPhotos();
$view_mode = 'teaser';
$gallery = array();
// Loop through the loaded photograph node objects and output their rendered result into a list.
$viewBuilder = $this->entityManager->getViewBuilder('node');
foreach ($photographs as $photograph) {
$gallery[] = $viewBuilder->view($photograph, $view_mode);
}
// If the gallery is empty, we should apologise.
if (empty($gallery)) {...}
// Return an item list of photographs for our gallery.
return [
'#theme' => 'item_list',
'#items' => $gallery,
];
}
25. Development - ./src/Form/OrderForm.php
/**
* @file
* Contains DrupalphotographyFormOrderForm.
*/
namespace DrupalphotographyForm;
use DrupalCoreFormFormBase;
use DrupalCoreFormFormStateInterface;
/**
* Class OrderForm.
*
* @package DrupalphotographyForm
*/
class OrderForm extends FormBase {
public function getFormId() {...}
public function buildForm(array $form, FormStateInterface $form_state) {...}
protected function validPostalCode(string $postal_code) {...}
public function validateForm(array &$form, FormStateInterface $form_state) {...}
public function submitForm(array &$form, FormStateInterface $form_state) {...}
}
26. Development - ./Tests/src/OrderFormTests.php
/**
* @file
* Contains DrupalphotographyTestsOrderFormTest.
*/
namespace DrupalphotographyTests;
use DrupalTestsUnitTestCase;
use DrupalphotographyFormOrderForm;
/**
* Tests validation of postal codes.
*
* @group photography
*/
class OrderFormTest extends UnitTestCase {
...
/**
* Test valid postal codes.
*/
public function testPostalCodeValid() {...}
/**
* Test invalid postal codes.
*/
public function testPostalCodeInvalid() {...}
33. Questions or Comments? We’d love to hear from you!
twitter.com/emarchak
twitter.com/gappleca
twitter.com/myplanetHQ
We can also help with your Drupal projects.
We know how to nudge the big wigs to give D8 a chance.
Extra pair of hands to plug in and help out.
That’s all, folks!
Before we jump into it, we’re going to take a quick poll. At your company, are you guys planning on hopping on to D8?
a) Yup, we’re planning on or are currently developing a D8 site
b) Yes, we’re planning on or are currently migrating an existing site to D8
c) Nope not yet
d) Enter your own response (Blank field)
Twig: no php in templates, autoescape output
Fast by Default: Improved caching (cache tags), Page Caching on by default, uses APCu when available (ChainedFastCache)
New Modules: Views, field types, breakpoint + picture
This new file structure adds simplicity for new developers. We’re no longer pointing people into a rabbit hole of files to get started developing. (/sites/all/modules and /sites/default/modules still work, and override /modules)
PHP 5.5 still receives security fixes, but is the oldest supported version (7 will be out soon, with big performance improvements)
APCu is automatically used for some caching when available
A note to MAMP developers: versions prior to MAMP 3 don’t come with the minimum PHP version. Either expect to spend the time to roll your own, or upgrade.
Drupal Console’s main functionality is assisting in development by generating snippets.
Built on Symfony Console components, only supports D8
Compared to drush:
No remote management (yet)
Module / extension support may be better for drush
Collaborating with drush to improve both projects
Most of the console of the prompts have autocompletes and defaults, so it’s quite quick to get through the progress.
Before we jump into it, we’re going to take a quick poll. Which CMS(s) is your company currently on?
a) Drupal
b) Other open-souce (Wordpress, Joomla etc.)
c) A proprietary or in-house platform
d) Enter your own response (Blank field)
Active storage is in the database
Changes made in UI are applied to active store
By default the sync directory is within the site’s files directory; should be set to a version-controlled location
Previously called “Staging”; may still be used in some older documentation
Import/Export are all or nothing operations, replacing config in target location
Merge tools are being improved
Admin page allows comparing differences before import to active storage
Sites can’t yet be setup directly from config files; database has to be synced between environments initially.
Keep site specific or security conscious code out of settings.php (e.g. database config)
site install creates a config directory with random name in files; want to replace with folder tracked in version control
yml files are protected from public visibility by the web server
Drush offers simplest workflow for now, by storing YML to sync directory
drupal generate:module
drupal console can generate module scaffolding quickly and efficiently. stuff that was previously done through the GUI can be performed entirely in the command line.
it even allows you to include a composer.json file if you’re dependant on external libraries in your module.
YAML: info, menu
Create page controller
If you wanted to load services (language manager, entity manager, entity query, etc.) you could pre-load them during the generation. I found, however that
ControllerBase already had everything. drupalize.me’s Amber Matz said, “check your pantry to see if [you] already have it” before you start adding dependencies.
A good IDE really helps inspecting your available methods and properties.
console only generates simpletests right now, so unit tests will have to be included by hand (we’ll get into that later.)
If your route needs a menu item, that needs to be defined separately in module_name.links.menu.yml
defaults for routes can be: controller, form, entity view (finds entity in path and renders it), entity list (returns a list of entities) and entity form (edit for for entites)
If you have a parameter in your path that you identity as the entity type (e.g. {node}), you can pre-load it in the controller.
Route access can be permission or role defined,
you can control the method (get/post)
you can also control if the output is in json, html or both just from the routing.
This is the contents of the controller. All of the structure was pre-generated except for loadAllPhotos()
Namespace reflects the file location within the src directory.
No dependency injection, no services, no complex construction.
Since we’ll be loading the photos, we’ll need to create a content type.
drupal config:export:content:type [content] --module=[module]
generate basic bundles through drupal console,
field configuration through the UI (it may change!)
entity manager is available in the base Controller class already.
Updated entity api allows us to quickly query and load entites.
No need for multiple loops in custom code.
Check the pantry before you shop!
this is the action method that we defined in our router.
language manager is available in the base Controller class already.
getViewBuilder() creates an entity handler instance for your specific entity type.
consistent entity handlers mean that entities will behave consistently, no need to know multiple process just to render two types of entities
More things are entities in d8 (blocks, comments, nodes, users, taxonomies, etc.)
render array stays the same.
While the form is still a FAPI array, the form state is stored in a FormStateInterface object reading, creating and manipulating form data has become much easier.
Type hinting is encouraged and expected in your methods.
New HTML5 field widgets (url, tel, email, color, etc.), HTML5 input types (date, email, range, color, etc.), and HTML5 input attributes (placeholder, min, max, autocomplete, etc.). HTML5 form elements (progressbar) are en route
No private methods because of the desire to keep things visible. Keeping something protected means that no one else can change up the values on the object arbitrarily. Given how extensible drupal is, they've chosen to keep all functions public or protected to allow developers to edit their code/output. This is not in the coding standard, but comes from discussions with core developers.
The use of public properties is strongly discouraged, as it allows for unwanted side effects. It also exposes implementation-specific details, which in turn makes swapping out a class for another implementation (one of the key reasons to use objects) much harder. Properties should be considered internal to a class.
Protected properties and methods should not use an underscore prefix.
Running tests on my custom postal code validation function, since submit and validation unit tests already are covered.
You must run PHPUnit from the /core directory
PHPUnit can only find tests in ./Tests/src/Unit/*, whose class ends with “Test” and methods start with “test”.
Examples of this test can be found in our code repository.
Before we jump into it, we’re going to take a quick poll. 3. What is your biggest challenge to working with or adopting Drupal 8?
a) Unported modules
b) Switching to object orientated
c) Bureaucracy from the big wigs
d) Enter your own response (Blank field)
like modules, the *.info now changed to *.info.yml
*.theme instead of template.php
Base Themes: Stable & Classy
Stable provides basic HTML markup for complete control in a sub-theme
Classy (the default) provides classic drupal wrapper classes (though much improved)
Common components of a template are easily converted.
Can output any object that can be converted to a string
understands render arrays as well, so no need to call render() on children
More control over output given to template files:
templates are the norm, functions discouraged (Theme functions only used in performance sensitive areas)
classes defined in templates instead of preprocess functions
Arbitrary PHP is not allowed, but still many tools available
Setting and manipulating variables
Attributes is an object; methods are available
Adds comments to show possible template file names, marks template in use, and provides full path to template file (helps differentiate between parent / child theme files)
{{ dump() }} available to see all variables accessible within template
{} allows defining a media property for each file
Adding to *.info.yml will apply site-wide
Can also be added within a twig template with {{ attach_library('theme/library') }},
or during preprocess phase using #attached property of render array element
jQuery is not included by default, so must declare dependency in library definition. drupalSettings is also only included when necessary
Inline Javascript no longer available through #attached / libraries
Themes can place in template files
Modules should convert to JS file; dynamic components should use drupalSettings