30 Awesome Drupal 8 API Functions You Should Already Know – Quick Intro – Paid Gig



30 Awesome Drupal 8 API Functions You Should Already Know – Quick Intro – Paid Gig

2 0


d8apifuncnyccamp2014

NYC Camp 2014 Presentation - 30 Drupal 8 API Functions You Should Already Know

On Github fmitchell / d8apifuncnyccamp2014

30 Awesome Drupal 8 API Functions You Should Already Know

Created by Fredric Mitchell / @fredricmitchell

http://brightplumbox.com/d8apifuncnyccamp2014/#/

Assumptions

Show Me the Code! Written something custom (familiar with PHP) You like using APIs Drupal as framework KDS: http://bit.ly/p2-kds

Quick Intro

Paid Gig

Senior Developer at Phase2 Technology

Quick Intro

Passions

Drupal, DRY / KISS, Strat. Comm., Graphic Novels

Disclaimer! Disclaimer!

In D7, "API functions" == Common procedural functions

In D8, "API functions" == Common class methods

Some cases, procedurals are wrappers for methods

Then, and now

Additional Disclaimer! Disclaimer!

All recommendations are based on D8 as of March 25, 2014

Things may (and probably will) change

Strings

Print stuff.

String::checkPlain()

D7: check_plain()

Wrapper for htmlspecialchars with UTF-8 encoding

String::placeholder()

D7: drupal_placeholder()

Wrap <em> around String:checkPlain()

t()

translate, sanitize

                            $text = t(“@name's blog”, array('@name' => $value))
// Note:
@name (String::checkPlain())
vs. %name (String::placeholder())
vs. !name
                        

l()

internal, external url

                            $link = l(t('Link text'), 'node/34', array(
    'attributes' => array(
        'class' => array('about-link'),
    )
));
// Note: automatic alias replacement via url()
                        

Rendering

Make stuff pretty.

drupal_render()

given render array, return HTML

                            $render = array(
    'first_para' => array(
        '#type' => 'markup',
        '#markup' => 'A first paragraph',
    ),
    'second_para' => array(
        array('first item', 'second item', 'third item'),
            '#theme' => 'item_list',
    ),
);
return drupal_render($render);
                        

D8: drupal_render() is EVERYTHING!

Bye-bye theme()

_theme() is an internal function

D7

                            // Theme a table with header and row data.
$markup = theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array('id' => 'my-module-table')
));

// Render a pager.
$markup .= theme('pager');
                        

D8

                            $table = array(
    '#type' => 'table',
    '#header' => $header,
    '#rows' => $rows,
    '#attributes' => array('id' => 'my-module-table'),
);
$markup = drupal_render($table);

// Pager is not an element type, use #theme directly.
$pager = array('#theme' => 'pager');
$markup .= drupal_render($pager);
                        

Nodes

Store content, data.

Wait...what!??! Hello? Entities!

Everything is entities now, right?

Yes. I was just getting there.

entity_load_multiple()

D7: node_load_multiple()

load nodes from db

node_load_multiple wraps around entity_load_multiple

entity_load_multiple wraps around $controller->loadMultiple()

$controller is the StorageController for the entity

The storage controller is likely DatabaseStorageController class

entity_load_multiple()

                            $nodes = entity_load_multiple('node', $nids = array())
// Note: Cached
                        

entity_view_multiple()

D7: node_view_multiple()

a render array of many nodes

node_view_multiple wraps around entity_view_multiple

entity_view_multiple wraps around $render_controller->viewMultiple()

$render_controller is the getViewBuilder for the entity

The view builder controller is likely EntityViewBuilder class

entity_view_multiple()

                            $nodes = entity_view_multiple($nodes);
                        

entity_create()

D7: node_save()

save a node to db

node_save() is gone

entity_create wraps around $controller->create()

$controller is the StorageController for the entity

The storage controller is likely DatabaseStorageController class

entity_create()

                            // Create a new forum instance.
$node = entity_create('node', array('otherValue' => 'foo'));
// Save entity to the database.
$node->save();
// Note: nothing is returned
                        

Menus

Allow users to navigate to content, data.

module.routing.yml

D7: hook_menu()

define menu items and callbacks

D7

                            function mymodule_menu() {
    $items['abc/def'] = array(
        'page callback' => 'mymodule_abc_view',
    );
    return $items;
}

function mymodule_abc_view($ghi = 0, $jkl = '') {
    // ...
}
                        

module.routing.yml

D8

                            mymodule.add_page:
    path: 'abc/def'
    defaults:
        _content: '\Drupal\mymodule\Controller\MyModuleController::addPage'
        _title: 'My Module'
    requirements:
        _permission: 'administer mymodule foobar'
                        

$request()->attributes->get()

D7: menu_get_item()

get a router item

Uses the new routing system from Symfony

                            $request = \Drupal::request();
$route = $request()->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
                        

$request()->attributes->get()

D7: menu_get_object()

load an object from router item

                            $request = \Drupal::request();
$node = $request()->attributes->get('node');
$type = $node->type;
// Note: Use instead of arg()
                        

Discussion: https://drupal.org/node/2124749

Possibly have $request, $route_match, and $context_container as separate objects.

menu_tree_page_data()

get data for menu tree

                            $tree = menu_tree_page_data('main-menu');
                        

menu_tree_output()

return a menu tree render array

                            // Build a HTML menu
$tree = menu_tree_page_data('main-menu');
$menu = menu_tree_output($tree, 1);
return drupal_render($menu);
                        

Taxonomy

Categorize content, data.

entity_load('taxonomy_vocabulary', 'forums')

D7: taxonomy_vocabulary_machine_name_load()

get vocabulary by machine name

D7

                            // Get a taxonomy object.
$vocabulary = taxonomy_vocabulary_machine_name_load('forums');
$options[$vocabulary->vid] = $vocabulary->name;
                        

D8

                            // Get a taxonomy object.
$vocabulary = entity_load('taxonomy_vocabulary', 'forums');
$options[$vocabulary->id()] = $vocabulary->label();
                        

taxonomy_get_tree()

create hierarchy from vocab

                            // Get a taxonomy object.
$v = entity_load('taxonomy_vocabulary', 'forums');

// Build an array of taxonomy terms.
$terms = taxonomy_get_tree($v->id());
foreach ($terms as $term) {
    $options[$term->tid] = $term->name;
}
                        

Fields

Organize content, data at various pivot points.

$fieldInfo->getField()

D7: field_info_field()

return field data

Wraps around entity_load('field_config', ...)

                            // Get info a field name.
$fieldInfo = \Drupal\field\Field::fieldInfo();
$fieldInfo->getField($entity_type, $field_name);
                        

$fieldInfo->getInstance()

D7: field_info_instance()

return field instance data

Wraps around entity_load_multiple('field_instance_config', ...)

                            // Get info on field name.
$fieldInfo = \Drupal\field\Field::fieldInfo();

// Get info on instance of field name from a bundle.
$fieldInfo->getInstance('node', 'article', 'field_name');
                        

entity_create('field_config', $values)

D7: field_create_field()

creates a field

The field configuration (field_config) is now an entity type

entity_create() invokes the create method of the StorageController

The field_config entity uses FieldConfigStorageController

All other methods are in \Drupal\field\Entity\FieldConfig

field_create_field()

D7

                            // Note: Requires proper field / instance data array which you can get from a features export.

$field_bases = array();

// Exported field_base: 'field_sc_html'
$field_bases['field_sc_html'] = array(
    'active' => 1,
    'cardinality' => 1,
    'deleted' => 0,
    'entity_types' => array(),
    'field_name' => 'field_sc_html',
    'foreign keys' => array(
        'format' => array(
            'columns' => array(
                'format' => 'format',
            ),
            'table' => 'filter_format',
        ),
    ),
    'indexes' => array(
        'format' => array(
            0 => 'format',
        ),
    ),
    'locked' => 0,
    'module' => 'text',
    'settings' => array(),
    'translatable' => 0,
    'type' => 'text_long',
);
field_create_field($field_bases);
                        

entity_create('field_config', $values)

D8

                            $field = entity_create('field_config', array(
    'name' => 'field_description',
    'entity_type' => 'taxonomy_term',
    'type' => 'text',
));
$field->save();
                        

entity_create('field_instance_config', $values)

D7: field_create_instance()

create instance of field, binding it to a bundle

The field instance configuration (field_instance_config) is now an entity type

entity_create() invokes the create method of the StorageController

The field_instance_config entity uses FieldInstanceConfigStorageController

All other methods are in \Drupal\field\Entity\FieldInstanceConfig

field_create_instance()

D7

                            // Note: Requires proper field / instance data array which you can get from a features export.

$field_instances = array();

// Exported field_instance: 'bean-field_sc_html'
$field_instances['bean-field_sc_html'] = array(
    'bundle' => 'oms_sidebar_content',
    'default_value' => NULL,
    'deleted' => 0,
    'description' => '',
    'display' => array(
        'default' => array(
        'label' => 'above',
        'settings' => array(),
        'type' => 'hidden',
        'weight' => 0,
        ),
    ),
    'entity_type' => 'bean',
    'field_name' => 'field_sc_html',
    'label' => 'HTML',
    'required' => 0,
    'settings' => array(
        'text_processing' => 1,
        'user_register_form' => FALSE,
    ),
    'widget' => array(
        'active' => 1,
        'module' => 'text',
        'settings' => array(
            'rows' => 5,
        ),
        'type' => 'text_textarea',
        'weight' => 2,
    ),
);
field_create_instance($field_instances);
                        

entity_create('field_instance_config', $values)

D8

                            $field_instance = entity_create('field_instance_config', array(
    'field_name' => 'field_description',
    'entity_type' => 'taxonomy_term',
    'bundle' => 'tags',
    'label' => 'Custom tag description',
));
$field_instance->save();
                        

Bonus!

Using the field API functions

D7

                            function mymodule_install_fields($fields, $instance_fields, $type = 'node', $bundle) {
    foreach ($fields as $field) {
        // Create the field if it doesn't exist.
        if (!field_info_field($field['field_name'])) {
            field_create_field($field);
        }
    }
    unset($field);
    foreach ($instance_fields as $field) {
        if (!field_info_instance($type, $field['field_name'], $bundle)) {
            field_create_instance($field);
        }
    }
}
                        

save()

D7: field_update_field()

D7: field_update_instance()

update field, field instance configs

                            // Updating field.
$field->cardinality = 2;
$field->save();

// Deleting field.
entity_load('field_config', 'field_name')->delete();

// Deleting instance.
entity_load('field_instance_config', 'field_instance_name')->delete();
                        

Alters

Change the assumptions.

ModuleHandler::alter()

D7: drupal_alter()

passes variables to be changed

                            $data = array(
    'key1' => 'value1',
    'key2' => 'value2',
);

// Call all modules implementing hook_my_data_alter
\Drupal::moduleHandler()->alter('my_data', $data);

// Note: Use sparingly. hook_theme() is preferred for overriding styles.
                        

hook_form_alter()

change a form before rendered

                            function mymodule_form_alter(&$form, &$form_state) {
    $form['new_checkbox'] = array(
        '#type' => 'checkbox',
        '#title' => t ('Subscribe'),
    );
);
                        

hook_form_FORM_ID_alter()

change a specific form by FORM ID before rendered

getFormId() method defines id to alter

hook_form_FORM_ID_alter()

                            /**
 * @file
 * Contains \Drupal\example\Form\ExampleForm.
 */

namespace Drupal\example\Form;
use Drupal\Core\Form\FormBase;

/**
 * Implements an example form.
 */
class ExampleForm extends FormBase {
    /**
     * {@inheritdoc}.
     */
    public function getFormId() {
        return 'example_form';
    }
}
                        

hook_form_FORM_ID_alter()

                            function mymodule_form_example_form_alter() {
    ...
);
                        

More Info on Form API

https://drupal.org/node/2117411

Hooks

Engage other processes

moduleHandler::invokeAll()

D7: module_invoke_all()

Invoke the hooks

                            \Drupal::moduleHandler->invokeAll($hook, $args);
                        

moduleHandler::getImplementations()

D7: module_implements()

Check a module invokes the hook

                            \Drupal::moduleHandler->getImplementations($hook);
                        

URLs

Grab the $_GET

UrlHelper::filterQueryParameters()

D7: drupal_get_query_parameters()

process url query

                            use Drupal\Component\Utility\UrlHelper;

// URL: http://mysite.com?text=foo&date=2013-09-14

$url = \Drupal::request()->query->all(); // May not be best way
$param = UrlHelper::filterQueryParameters($url);

// Values
// $param['text'] = foo
// $param['date'] = 2013-09-14
                        

Paths

Where am I?

drupal_get_path()

returns path to a system item

                            // Path to a file
$path = drupal_get_path('theme', 'mytheme') . '/mycustom.js';
                        

User

Who am I?

D7: global $user

User

Who am I?

D8: \Drupal::currentUser()

https://drupal.org/node/2032447

User

Who am I?

                            $account = \Drupal::currentUser();
if ($account->id() == 1) {
    return "Hiya, boss!";
}
// $account is not an actual User object, but UserSession object
// Extends AccountInterface class
                        

moduleHandler->loadInclude()

D7: module_load_include()

loads a module include file

                        // Load node.admin.inc from the node module.
\Drupal::moduleHandler->loadInclude('node', 'inc', 'node.admin');
                    

Querying Entities

Join ALL the tables!

\Drupal::entityQuery()

D7: EntityFieldQuery

search a single entity

D7: EntityFieldQuery

                            // Query articles
$query = new EntityFieldQuery();

$query
    ->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'article')
    ->propertyCondition('status', 1);

$result = $query->execute();
$nids = array_keys($result['node']);
$nodes = node_load_multiple($nids);
                        

EFQ presentation at http://bit.ly/top10efq

\Drupal::entityQuery()

                            $query = \Drupal::entityQuery('taxonomy_term')
    ->condition('tid', $term->id())
    ->condition('vid', $term->bundle())
    ->range(0, 1)
    ->count()
    ->execute();
                        

https://drupal.org/node/1827278

Was that 30?

Kind of didn't feel like it.

What about functions completely removed?

#attached

D7: drupal_add_js, drupal_add_css, drupal_add_library

Gone!

https://drupal.org/node/2169605

#attached

                            // within CKEditor
public function settingsForm(array $form, array &$form_state, EditorEntity $editor) {
    $ckeditor_settings_toolbar = array(
        '#theme' => 'ckeditor_settings_toolbar',
        '#editor' => $editor,
        '#plugins' => $this->ckeditorPluginManager->getButtons(),
    );
    $form['toolbar'] = array(
        '#type' => 'container',
        '#attached' => array(
            'library' => array('ckeditor/drupal.ckeditor.admin'),
            'js' => array(
                array(
                    'type' => 'setting',
                    'data' => array('ckeditor' => array(
                    'toolbarAdmin' => drupal_render($ckeditor_settings_toolbar),
                )),
            )
        ),
    ),
    '#attributes' => array('class' => array('ckeditor-toolbar-configuration')),
);
                        

1 - 10

  • t()
  • String::checkPlain()
  • String::placeholder()
  • l()
  • drupal_render()
  • node_load_multiple() entity_load_multiple()
  • node_view_multiple() entity_view_multiple()
  • node_save() entity_create()
  • hook_menu() module.routing.yml
  • menu_get_item() $request()->attributes->get()

11 - 20 17

  • menu_get_object() $request()->attributes->get()
  • menu_tree_page_data()
  • menu_tree_output()
  • taxonomy_get_tree()
  • taxonomy_vocabulary_machine_name_load() entity_load('taxonomy_vocabulary', 'forums')
  • field_info_field() $fieldInfo->getField()
  • field_info_instance() $fieldInfo->getInstance()
  • field_create_field() entity_create('field_config', $values)
  • field_create_instance() entity_create('field_instance_config', $values)
  • field_update_field() save()

18 - 28

  • field_update_instance() save()
  • drupal_alter() ModuleHandler::alter()
  • hook_form_alter()
  • hook_form_FORM_ID_alter()
  • moduleHandler::invokeAll()
  • moduleHandler::getImplementations()
  • drupal_get_query_parameters() UrlHelper::filterQueryParameters()
  • drupal_get_path()
  • module_load_include() moduleHandler->loadInclude()
  • EntityFieldQuery \Drupal::entityQuery()

So, it wasn't 30

Technically, no.

#attached could be a +3

drupal_add_js, drupal_add_css, drupal_add_library

What's Next?

Contribute to Examples: https://drupal.org/project/examples

Read docs: https://drupal.org/developing/api/8

Scaffold: https://github.com/hechoendrupal/DrupalAppConsole

Questions?

By Fredric Mitchell / @fredricmitchell

Slides: http://brightplumbox.com/d8apifuncnyccamp2014/#/

Slides Repo: https://github.com/fmitchell/d8apifuncnyccamp2014