from procedural to object-oriented php in drupal

85
From Procedural to Object-Oriented PHP in Drupal Presented by Amber Matz PNWPHP, Seattle, WA September 11, 2015

Upload: amber-matz

Post on 13-Apr-2017

568 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: From Procedural to Object-Oriented PHP in Drupal

From Procedural to

Object-Oriented PHP in

DrupalPresented by Amber Matz

PNWPHP, Seattle, WA September 11, 2015

Page 2: From Procedural to Object-Oriented PHP in Drupal

Amber Matz@amberhimesmatz

Use #PNWPHP

Drupal trainer and developer

Web developer since 1999-ish

Not an astronaut

Page 3: From Procedural to Object-Oriented PHP in Drupal

What This Session Is About

Page 4: From Procedural to Object-Oriented PHP in Drupal

What Do I Mean By Procedural PHP?

• Code is organized into functions

• Functions are grouped into PHP files (.inc)

• Output is accessed through variables (usually strings or arrays)

Page 5: From Procedural to Object-Oriented PHP in Drupal

Problems with Procedural Code

• To re-use code means to duplicate code into a new module, then make changes

• Code is hard to test when it’s tightly coupled

• Function loading order is magical and mysterious

• StdClass Obj provides only values, no hinting

Page 6: From Procedural to Object-Oriented PHP in Drupal

What Is Object-Oriented PHP?

• Encapsulation provides centralized access to methods and values

• Inheritance of values and function made possible

• Interfaces set expectations

• OO-PHP allows for design patterns

• Easier to re-use, maintain, and extend

Page 7: From Procedural to Object-Oriented PHP in Drupal

Before & After: Contact Module in D7 & D8

Page 8: From Procedural to Object-Oriented PHP in Drupal

How It Works: D7’s Contact Module

Page 9: From Procedural to Object-Oriented PHP in Drupal
Page 10: From Procedural to Object-Oriented PHP in Drupal

D7 Contact Module Structure• drupalroot/

• modules/ • contact/

• contact.info • contact.module • contact.install • contact.admin.inc • contact.pages.inc • contact.test

Page 11: From Procedural to Object-Oriented PHP in Drupal

D7: contact.info

• INI syntax (with Drupal additions)

• name, description, package, version

• extra files (the contact.test)

• path to configuration page

Page 12: From Procedural to Object-Oriented PHP in Drupal

D7: contact.module• hook_help

• hook_permission

• hook_menu (admin UI pages, local tabs, form paths and callbacks)

• contact_load (loads a contact category)

• hook_mail

• hook_form_FORM_ID_alter (contact_form_user_profile_form_alter)

• alters the user profile form and adds a tab to the personal contact form

• hook_user_presave

• contact_form_user_admin_settings_alter

• adds default personal contact form settings to user settings page

Page 13: From Procedural to Object-Oriented PHP in Drupal

D7: contact.admin.inc• Admin page callbacks for Contact module

• contact_category_list

• categories list, theme function

• contact_category_edit_form

• + validate and submit handlers

• contact_category_delete_form

• + confirm_form()

• + submit handler

Page 14: From Procedural to Object-Oriented PHP in Drupal

D7: contact.pages.inc

• Form constructor + handlers for site-wide contact form

• Form constructor + handlers for personal contact form

Page 15: From Procedural to Object-Oriented PHP in Drupal

D7: contact.install

• hook_schema

• db schema for data storage

• hook_uninstall

• various update hooks

Page 16: From Procedural to Object-Oriented PHP in Drupal

D7: contact.test

• Tests for the Contact module

• Uses SimpleTest

• For tests that need to fully or partially bootstrap Drupal

• Uses OO-PHP

• class ContactSitewideTestCase extends DrupalWebTestCase

Page 17: From Procedural to Object-Oriented PHP in Drupal

Why Use OO-PHP in Drupal 8?

• Testable code

• Better organization

• Decoupling reduces dependencies

• Interfaces & Data Types improve insight

Page 18: From Procedural to Object-Oriented PHP in Drupal

How Is OO-PHP Done in Drupal 8?

• Much of the code base has been refactored to be object-oriented

• Each class is in its own file

• Classes must be namespaced

• External libraries introduced that Drupal extends

• Design pattern-based best practices in use

Page 19: From Procedural to Object-Oriented PHP in Drupal

How It Works: D8’s Contact Module

Page 20: From Procedural to Object-Oriented PHP in Drupal
Page 21: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Module Location

• drupalroot/

• core/

• modules/

• contact/

Page 22: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Module Base Files

• contact/

• contact.info.yml

• contact.module

Page 23: From Procedural to Object-Oriented PHP in Drupal

.info Replaced by YAML filesD7: contact.info

name = Contactdescription = Enables the use of both personal and site-wide contact forms.package = Coreversion = VERSIONcore = 7.xfiles[] = contact.testconfigure = admin/structure/contact

D8: contact.info.yml

name: Contacttype: moduledescription: 'Enables the use of both personal and site-wide contact forms.'package: Core# version: VERSION# core: 8.xconfigure: entity.contact_form.collection

Page 24: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Module Files (cont’d)• contact/

• contact.routing.yml

• contact.links.action.yml

• contact.links.task.yml

• contact.links.menu.yml

• contact.permissions.yml

• contact.services.yml => Defines Services

• contact.views.inc

Page 25: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Module Directories• contact/

• config/

• migration_templates/

• src/

• tests/

Page 26: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Config Files• contact/

• config/

• install/

• contact.form.personal.yml

• contact.settings.yml

• contact/

• config/

• schema/

• contact.views.schema.yml

• contact.schema.yml

replaces hook_schema in contact.install

Page 27: From Procedural to Object-Oriented PHP in Drupal

D8: Migration Templates

• contact/

• migration_templates/

• d6_contact_category.yml

• d6_contact_settings.yml

Page 28: From Procedural to Object-Oriented PHP in Drupal

Drupal 6 to 8 Migrationhttps://www.drupal.org/node/2350521

Page 29: From Procedural to Object-Oriented PHP in Drupal

PSR-4 Namespaces and Autoloading• Drupal 8 uses PSR-4 standard for PHP namespace autoloading

• Upgrading a module from D7 to D8 requires use of PSR-4

• Each module has a namespace that corresponds to its module name

• The module’s namespace is mapped to the src directory in the module’s directory.

• Anything after the namespace directly correlates to the directory and file structure in the src directory.

• The class name correlates to the file name with a php extension.

Page 30: From Procedural to Object-Oriented PHP in Drupal

namespace Drupal\contact\Access

Namespace Mapping

namespace Drupal\contact

Page 31: From Procedural to Object-Oriented PHP in Drupal

D8: Contact’s Classes• contact/

• src/

• ContactFormAccessControlHandler.php

• ContactFormEditForm.php

• ContactFormInterface.php

• ContactFormListBuilder.php

• ContactMessageAccessControlHandler.php

• MailHandler.php

• MailHandlerException.php

• MailHandlerInterface.php

• MessageForm.php

• MessageInterface.php

• MessageViewBuilder.php

Page 32: From Procedural to Object-Oriented PHP in Drupal

PSR-4 namespaces & autoloading in Drupal 8

https://www.drupal.org/node/2156625

Page 33: From Procedural to Object-Oriented PHP in Drupal

Upgrading contact.module

Page 34: From Procedural to Object-Oriented PHP in Drupal

We still have hooks in D8

Page 35: From Procedural to Object-Oriented PHP in Drupal

contact.module• Functions/Hooks added to accommodate changes in functionality or API

• i.e. hook_entity_extra_field_info_alter, which adds “pseudo-field" components on contact form

• Still contains some hooks/functions (i.e. hook_help), but…

• Values and parameters updated to use new D8 objects, interfaces, or architecture

function contact_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.contact':

function contact_help($path, $arg) { switch ($path) { case 'admin/help#contact': vs

Page 36: From Procedural to Object-Oriented PHP in Drupal

Menus & Routes

• Where is hook_menu?

• D7’s hook_menu replaced by new systems for routing, menu links, local tasks, actions, and contextual links https://www.drupal.org/node/1800686

• Oversimplification:

• Instead of hook_menu in .module, use .yml files

Page 37: From Procedural to Object-Oriented PHP in Drupal

D7 contact_menu$items['contact'] = array( 'title' => 'Contact', 'page callback' => 'drupal_get_form', 'page arguments' => array('contact_site_form'), 'access arguments' => array('access site-wide contact form'), 'type' => MENU_SUGGESTED_ITEM, 'file' => 'contact.pages.inc', );

D8 contact.

routing.yml

contact.site_page: path: '/contact' defaults: _title: 'Contact' _controller: '\Drupal\contact\Controller\ContactController::contactSitePage' contact_form: NULL requirements: _permission: 'access site-wide contact form'

Page 38: From Procedural to Object-Oriented PHP in Drupal

Routing system in Drupal 8https://www.drupal.org/developing/api/8/routing

Page 39: From Procedural to Object-Oriented PHP in Drupal

D7 to D8 upgrade tutorial: Convert hook_menu() and

hook_menu_alter() to Drupal 8 APIshttps://www.drupal.org/node/2118147

Page 40: From Procedural to Object-Oriented PHP in Drupal

hook_form_FORM_ID_alterD7: contact.module

/** * Implements hook_form_FORM_ID_alter(). * * Add the default personal contact setting on the user settings page. * * @see user_admin_settings() */function contact_form_user_admin_settings_alter(&$form, &$form_state)

Page 41: From Procedural to Object-Oriented PHP in Drupal

hook_form_FORM_ID_alterD8: contact.module

/** * Implements hook_form_FORM_ID_alter(). * * Add the default personal contact setting on the user settings page. * * @see \Drupal\user\AccountSettingsForm */function contact_form_user_admin_settings_alter(&$form, FormStateInterface $form_state)

Page 42: From Procedural to Object-Oriented PHP in Drupal

@see \Drupal\user\AccountSettingsForm

• Get form ID from method getFormID()*

public function getFormId() { return 'user_admin_settings'; }

* sometimes this isn’t so straightforward

Page 43: From Procedural to Object-Oriented PHP in Drupal

Permissions

• D7: Permissions defined in hook_permission in contact.module

• D8: Permissions defined in contact.permissions.yml

• Change record: https://www.drupal.org/node/2311427

Page 44: From Procedural to Object-Oriented PHP in Drupal

D7 contact_permissonfunction contact_permission() { return array( 'administer contact forms' => array( 'title' => t('Administer contact forms and contact form settings'), ), 'access site-wide contact form' => array( 'title' => t('Use the site-wide contact form'), ), 'access user contact forms' => array( 'title' => t("Use users' personal contact forms"), ), );}

D8 contact.

permissions. yml

administer contact forms: title: 'Administer contact forms and contact form settings'access site-wide contact form: title: 'Use the site-wide contact form'access user contact forms: title: 'Use users'' personal contact forms'

Page 45: From Procedural to Object-Oriented PHP in Drupal

Access Control

Page 46: From Procedural to Object-Oriented PHP in Drupal

Access to Personal Contact FormD7

contact.module

_contact_personal_tab_access

• defines access control to a user’s personal contact form

D8 src/Access/ContactPageAccess.php

class ContactPageAccess

• defines access control to a user’s personal contact form

Page 47: From Procedural to Object-Oriented PHP in Drupal

Complex Access Control as a Service

• A more complex access check is used for the personal contact form (beyond just checking for a permission)

• Complex access control must be registered as a service

• Benefit: greater testing flexibility because you can mock the service

Page 48: From Procedural to Object-Oriented PHP in Drupal

Service: Personal Tab AccessIn contact.services.yml:

services: access_check.contact_personal: class: Drupal\contact\Access\ContactPageAccess tags: - { name: access_check, applies_to: _access_contact_personal_tab } arguments: ['@config.factory', '@user.data']

Page 49: From Procedural to Object-Oriented PHP in Drupal

Access Checking on Routeshttps://www.drupal.org/node/2122195

Services and dependency injection in Drupal 8

https://www.drupal.org/node/2133171

Page 50: From Procedural to Object-Oriented PHP in Drupal

D7: Message Building• Implements hook_mail in contact.module

• Builds email message

• Sets site name, subject, (optional) category, message, sender name, sender email

• Builds auto-reply message

• Builds “send copy to myself” message

Page 51: From Procedural to Object-Oriented PHP in Drupal

D8: Message Building• Defines Message entity in src/Entity/Message.php

• class Message extends ContentEntityBase implements MessageInterface

• src/MessageInterface.php

• Builds message form, workflow, and actions in src/MessageForm.php

• class MessageForm extends ContentEntityForm

• Builds and formats the message into a format suitable for email in src/MessageViewBuilder.php

Page 52: From Procedural to Object-Oriented PHP in Drupal

D8: Mail Handling

• MailHandler, MailHandlerException, MailHandlerInterface

• handles assembly and dispatch of contact mail messages

• Drupal\contact\MailHandler is registered as a service in contact.services.yml

Page 53: From Procedural to Object-Oriented PHP in Drupal

New in D8: MailHandler Unit Test

• The Drupal\Contact\MailHandler has its own unit test!

• Location: tests/src/Unit/MailHandlerTest.php

• Uses PHPUnit

Page 54: From Procedural to Object-Oriented PHP in Drupal

MailHandler _construct()

public function __construct( MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, LoggerInterface $logger, TranslationInterface $string_translation, EntityManagerInterface $entity_manager)

Page 55: From Procedural to Object-Oriented PHP in Drupal

MailHandlerTest Setup$this->mailManager = $this->getMock('\Drupal\Core\Mail\MailManagerInterface'); $this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface'); $this->logger = $this->getMock('\Psr\Log\LoggerInterface'); $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');$this->userStorage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');

Page 56: From Procedural to Object-Oriented PHP in Drupal

Testing in Drupal 8

• SimpleTest is joined by PHPUnit testing framework

• Use PHPUnit for unit tests

• SimpleTest should only be used when a full or partial bootstrap of Drupal is necessary

Page 57: From Procedural to Object-Oriented PHP in Drupal

Drupal's implementation of PHPUnit

https://www.drupal.org/phpunit

Page 58: From Procedural to Object-Oriented PHP in Drupal

D8FTW: Unit Testing For Realsies

by Larry Garfield

https://www.palantir.net/blog/d8ftw-unit-testing-realsies

An article to read about unit testing in Drupal 8:

Page 59: From Procedural to Object-Oriented PHP in Drupal

D8: Contact Module Tests

• SimpleTest Tests:

• src/Tests

• PHPUnit Tests

• tests

Page 60: From Procedural to Object-Oriented PHP in Drupal

Upgrading & Refactoring contact.admin.inc

Page 61: From Procedural to Object-Oriented PHP in Drupal

admin.inc refactor

• Functionality changed to utilize D8 Entities and Fields

• Instead of add categories, add form

• Instead of configuring recipients per category, set per form

• Instead of only adding categories, add fields of any type

Page 62: From Procedural to Object-Oriented PHP in Drupal

D7• contact.admin.inc

• Procedural functions:

• Categories: get from db and list in a table (theme)

• Form: edit categories

D8• src/ContactFormInterface.php

— interface for defining a contact form entity

• src/ContactFormEditForm.php — base form class for contact form edit forms

• src/ContactFormListBuilder.php— class that builds a listing of contact forms

• src/Entity/ContactForm.php— class that defines the contact form entity (implements the ContactFormInterface)

Admin refactoring

Page 63: From Procedural to Object-Oriented PHP in Drupal

Changes in Form API

Page 64: From Procedural to Object-Oriented PHP in Drupal

Before: D7 Form API

• Magically named functions

• Form ID = name of the function that builds and return $form array

• Validate and submit handlers should exist…but no enforcement except for hitting your head against the wall

Page 65: From Procedural to Object-Oriented PHP in Drupal

Before: D7 Form FunctionsFunction: Build and return the form (function name = Form ID)

• function contact_category_edit_form($form, &$form_state, array $category = array())

Function: Validate the form

• function contact_category_edit_form_validate($form, &$form_state)

Function: Submit the form

• function contact_category_edit_form_submit($form, &$form_state)

Page 66: From Procedural to Object-Oriented PHP in Drupal

After: D8 Form MethodsName and Return the Form IDpublic function getFormID()

Build the Formpublic function buildForm(array $form, FormStateInterface $form_state)

Validate the Formpublic function validateForm(array &$form, FormStateInterface $form_state)

Submit the Formpublic function submitForm(array &$form, FormStateInterface $form_state)

Page 67: From Procedural to Object-Oriented PHP in Drupal

FormInterfacenamespace Drupal\Core\Form;

interface FormInterface {

public function getFormId();

public function buildForm(array $form, FormStateInterface $form_state);

public function validateForm(array &$form, FormStateInterface $form_state);

public function submitForm(array &$form, FormStateInterface $form_state);

}

Page 68: From Procedural to Object-Oriented PHP in Drupal

D8 Forms: Extend & Use Interface

• Choose a class or “inheritance tree” that closely matches your use-case and extend it!

• For basic forms, extend FormBase and use the FormInterface to learn about which methods to use

• $form_state uses FormStateInterface

• See the FormInterface for parameter hints!

Page 69: From Procedural to Object-Oriented PHP in Drupal

One more thing about contact.admin.inc…

Page 70: From Procedural to Object-Oriented PHP in Drupal

Delete Handling: “Abstracted Out”

function contact_category_delete_form ($form, &$form_state, array $contact) { $form['contact'] = array( '#type' => 'value', '#value' => $contact, ); return confirm_form( $form, t('Are you sure you want to delete %category?', array('%category' => $contact['category'])), 'admin/structure/contact', t('This action cannot be undone.'), t('Delete'), t('Cancel') );} function contact_category_delete_form_submit ($form, &$form_state)…

/** * Defines the contact form entity. * * @ConfigEntityType( * id = "contact_form", * label = @Translation("Contact form"), * handlers = { * "access" = "Drupal\contact\ContactFormAccessControlHandler", * "list_builder" = "Drupal\contact\ContactFormListBuilder", * "form" = { * "add" = "Drupal\contact\ContactFormEditForm", * "edit" = "Drupal\contact\ContactFormEditForm", * "delete" = "Drupal\Core\Entity\EntityDeleteForm" * } * },

Annotation in src/Entity/ContactForm.phpProcedural functions in contact.admin.inc

`

Page 71: From Procedural to Object-Oriented PHP in Drupal

That was a plugin annotation

Page 72: From Procedural to Object-Oriented PHP in Drupal

An Overview of the Drupal 8 Plugin System

by Joe Shindelar

https://www.youtube.com/watch?v=gd6s4wC_bP4

DrupalCon Los Angeles 2015

Page 73: From Procedural to Object-Oriented PHP in Drupal

pages.inc refactor

• contact.pages.inc replaced by src/Controller/ContactController.php

• Refactored to use Controller class (extends ControllerBase)

• Uses new menu and routing system for paths and menu items

Page 74: From Procedural to Object-Oriented PHP in Drupal

Got module.pages.inc? Extend ControllerBase

Page 75: From Procedural to Object-Oriented PHP in Drupal

D7: Displaying Site-Wide Contact Form on a Page

contact.module contact_menu

page callback = ‘drupal_get_form’

page argument = ‘contact_site_form’

file = ‘contact.pages.inc’

contact.pages.incfunction

contact_site_form$items[‘contact’] (where ‘contact’ = the path)

/contact

Page 76: From Procedural to Object-Oriented PHP in Drupal

D8: Displaying Site-Wide Contact Form on a Page

contact.routing.yml contact.site_page:

path: ‘/contact’

_controller: ‘\Drupal\contact \Controller \ContactController ::contactSitePage'

contact_form: NULL

src/Controller/ ContactController.php

class ContactController extends ControllerBase

public function ContactSitePage(

ContactFormInterface $contact_form = NULL)

Page 77: From Procedural to Object-Oriented PHP in Drupal

State of OO-PHP in Contact Module

• Much of the code base has been refactored to be object-oriented

• Each class is in its own file

• Classes are namespaced

• Unit tests introduced

• Classes extend others, use interfaces

Page 78: From Procedural to Object-Oriented PHP in Drupal

:wipes brow:

Page 79: From Procedural to Object-Oriented PHP in Drupal

What Do I Need to Know?

• Language features of OO-PHP, such as Constructors, Inheritance, Interfaces, Namespaces, and Type Hinting

• Design patterns and conventions such as Dependency Injection, Services Container, PSR-4

• Other Drupal 8-isms such as Plugins and Annotations

Page 80: From Procedural to Object-Oriented PHP in Drupal

Porting Drupal 7 Modules

• You could try an automated solution to help you learn about changes in the API

• Drupal Module Upgraderhttps://www.drupal.org/project/drupalmoduleupgrader

Page 81: From Procedural to Object-Oriented PHP in Drupal

https://www.drupal.org/project/drupalmoduleupgrader

Page 82: From Procedural to Object-Oriented PHP in Drupal

D8 Code Generator/Scaffolding

• Generate boilerplate code and scaffolding to jump-start refactoring

• Drupal Consolehttp://drupalconsole.com/

Page 83: From Procedural to Object-Oriented PHP in Drupal
Page 84: From Procedural to Object-Oriented PHP in Drupal

D8 Module Developer Resources• api.drupal.org

• Handbook docs on drupal.org

• Change recordsdrupal.org/list-changes

• Blog posts

• DrupalCon Session Recordingshttps://www.youtube.com/user/DrupalAssociation/playlists

• Symfony2 Docs

• Training companies

Disclaimer: All sources contain outdated info, may or may not be accurate, and will undoubtedly be replaced by updated/new documentation once release candidate and stable versions are released.

Page 85: From Procedural to Object-Oriented PHP in Drupal

Happy D8 Developing!

@amberhimesmatz

Amber Matz