Upload
acquia
View
1.970
Download
0
Embed Size (px)
Citation preview
Drupal 8 Every DayAn Intro to Developing With Drupal 8
Erin Marchak
● Acquia Certified Front End Specialist, and Developer
● dgo.to/@emarchak
Geoff Appleby
● Acquia Certified Developer
● dgo.to/@gapple
● Persistent Login & drupalreleasedate.com
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
Unboxing
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 structureCore is now in ./core
Modules are now in ./modules
Profiles are now in ./profiles
Themes are now in ./themes
Drupal Console
Interactive console
Scaffolding
Site Management
What about Drush?
curl -LSs http://drupalconsole.com/installer | php
Installation
Site Install - site:new
Site Install - site:install
Configuration Management
ProductionDevelopment
Active
Storage
Active
Storage
Code Repository
Sync Storage
ImportExport
Configuration Management
Move database settings to settings.local.php
Uncomment include() statement
Set config sync directory in settings.php
$config_directories[CONFIG_SYNC_DIRECTORY] = __DIR__ . '/config';
cp example.gitignore .gitignore
git add -f sites/default/settings.php
mkdir sites/default/config
Configuration Management - Export
Console creates an archive of YML files
drupal config:export
Drush outputs YML files directly to the sync directory
drush config-export
Configuration Management - Import
Console imports an archive of YML files
drupal config:import [file name]
Drush imports YML files directly from the sync directory
drush config-import
Development
Development - Generate a Custom Module
Development - Generate a Page Controller
Development - ./photography.routing.yml
photography.gallery_controller_gallery:path: '/photography'defaults:
_controller: '\Drupal\photography\Controller\GalleryController::gallery'_title: 'Photography Gallery'
requirements:_permission: 'access content'
photography.order_form:path: '/photography/order'defaults:
_form: '\Drupal\photography\Form\OrderForm'_title: 'Photo Order Form'
requirements:_permission: 'access content'
Development - ./src/Controller/GalleryController.php
/*** @file* Contains Drupal\photography\Controller\GalleryController.*/namespace Drupal\photography\Controller;
use Drupal\Core\Controller\ControllerBase;
/*** Class GalleryController.** @package Drupal\photography\Controller*/class GalleryController extends ControllerBase {
/*** Helper function to load all published photographs.*/public function loadAllPhotos($bundle_type = 'photograph') {...}
/*** Gallery display.*/public function gallery() {...}
}
Development - Generate an Entity Bundle
Development - GalleryController->loadAllPhotos()
/*** Helper function to load all published photographs.** @param string $bundle_type* @return \Drupal\Core\Entity\EntityInterface[]* 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,
));
}
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,
];}
Development - ./src/Form/OrderForm.php
/*** @file* Contains Drupal\photography\Form\OrderForm.*/namespace Drupal\photography\Form;
use Drupal\Core\Form\FormBase;use Drupal\Core\Form\FormStateInterface;
/*** Class OrderForm.** @package Drupal\photography\Form*/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) {...}
}
Development - ./Tests/src/OrderFormTests.php
/*** @file* Contains Drupal\photography\Tests\OrderFormTest.*/namespace Drupal\photography\Tests;
use Drupal\Tests\UnitTestCase;use Drupal\photography\Form\OrderForm;
/*** 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() {...}
}
Theming
Theming with Twig - generate:theme
Twig Syntax
PHPTemplate Twig
<?php print $variable ?> {{ variable }}
<?php if (condition): ?> {% if condition %}
<?php foreach ($users as $user): ?>
{% for user in users %}
<?php print t(“string”) ?> {{ “string”|t }}
{%set classes = [
'node','node--type-' ~ node.bundle|clean_class,node.isPromoted() ? 'node--promoted',node.isSticky() ? 'node--sticky',not node.isPublished() ? 'node--unpublished',view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
]%}
{{ attach_library('classy/node') }}
<article{{ attributes.addClass(classes) }}>{{ title_prefix }}
{% if not page %}<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a></h2>
{% endif %}
{{ title_suffix }}
<div{{ content_attributes.addClass('node__content') }}>{{ content }}
</div>
</article>
Twig Debug
sites/default/services.ymlparameters:
twig.config:debug: true
<!-- THEME DEBUG --><!-- THEME HOOK: 'block' --><!-- FILE NAME SUGGESTIONS:
* block--bartik-main-menu.html.twig* block--system-menu-block--main.html.twigx block--system-menu-block.html.twig* block--system.html.twig* block.html.twig
--><!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block--system-menu-block.html.twig' --><nav role="navigation"
aria-labelledby="block-bartik-main-menu-menu" id="block-bartik-main-menu" class="contextual-region block block-menu navigation menu--main">
Adding CSS
portfolio.libraries.ymlglobal-styling:
version: 1.xcss:
theme:css/portfolio-global.css: {}
portfolio.info.ymllibraries:
- portfolio/global-styling
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!
Thank you
Resources
Github Repository https://github.com/gapple/drupal-8-every-day
Database Dump https://drive.google.com/file/d/0B-PTT5Vfuw18RTdId2poQUFNR1E/view
Drupal Console https://www.drupal.org/project/console
Theming Guide https://www.drupal.org/theme-guide/8
CMI Documentation https://www.drupal.org/documentation/administer/config