Writing code, both science and art

Jan Zavrl

Drupal Camp London, March 2019

Jan Zavrl

Full stack developer

Senior Drupal developer at NDP

Acquia certified Drupal 8 Grand Master

@jnzavrl | jzavrl | janzavrl.me

@ndp_studio | ndp-studio.com

Writing code, both science and art, London, March 2019

The what and the why

  • Rules, expectations, consistency
  • Two types - style and substance
  • Automating API documentation
  • For the guy next to you and after you

Writing code, both science and art, London, March 2019

Let's start with the basics

  • Code style and formatting
  • Indentation
  • Spaces
  • Opening tags, end with blank line
  • Line wrapping and length

Writing code, both science and art, London, March 2019

if (isset($view_modes['teaser'])) {
  entity_get_display('node', $type->id(), 'teaser')
    ->setComponent('body', array(
      'label' => 'hidden',
      'type' => 'text_summary_or_trimmed',
    ))
    ->save();
}
if(isset($view_modes['teaser'])){
  entity_get_display('node',$type->id(),'teaser')
    ->setComponent('body',array(
      'label'=>'hidden',
      'type'=>'text_summary_or_trimmed'
    ))->save();
}

Writing code, both science and art, London, March 2019

Control structures

  • If, switch and try statements
  • Template exceptions

Writing code, both science and art, London, March 2019

if ($actual_count == $expected_count) {
    $message = $this->t('Expected count is correct');
}
elseif ($expected_count == 0) {
    $message = $this->t('Expected count is 0');
}
else {
    $message = $this->t('Expected count is unknown');
}

Writing code, both science and art, London, March 2019

switch ($key) {
  case 'field_name':
    $checked_value = $field_storage->getName();
    break;

  case 'field_id':
  case 'field_storage_uuid':
    $checked_value = $field_storage->uuid();
    break;

  default:
    $checked_value = $field->get($key);
    break;
}

Writing code, both science and art, London, March 2019

try {
  $this->migration->checkRequirements();
}
catch (RequirementsException $e) {
  $this->message->display(
    $this->t(
      'Migration @id did not meet the requirements. @message @requirements',
      array(
        '@id' => $this->migration->id(),
        '@message' => $e->getMessage(),
        '@requirements' => $e->getRequirementsString(),
      )
    ),
    'error'
  );

  return MigrationInterface::RESULT_FAILED;
}

Writing code, both science and art, London, March 2019

{% if data.width %}
  {% trans %}
    width {{ data.width }}
  {% endtrans %}
{% elseif data.height %}
  {% trans %}
    height {{ data.height }}
  {% endtrans %}
{% endif %}
<?php if ($content): ?>
  <div class="content">
    <?php print $content; ?>
  </div>
<?php endif; ?>

Writing code, both science and art, London, March 2019

Arrays

  • Short array syntax
  • Multiline or single line
  • Trailing comma

Writing code, both science and art, London, March 2019

$test_samples = ['Save and continue', 'Anonymous', 'Language'];

$header['type'] = [
  'data' => $this->t('Field type'),
  'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
];

Writing code, both science and art, London, March 2019

Naming conventions

  • Variables as snake_case or lowerCamelCase
  • Constants as UPPERCASE
  • Classes as UpperCamelCase
  • Methods and properties as lowerCamelCase

Writing code, both science and art, London, March 2019

Comment your code

  • File doc block - @file, with exceptions
  • Functions and methods doc block
  • Hook implementations
  • {@inheritdoc}
  • Inline comments

Writing code, both science and art, London, March 2019

<?php

/**
 * @file
 * Allows to ban individual IP addresses.
 */

use Drupal\Core\Routing\RouteMatchInterface;
/**
 * Get a translated version of the field item instance.
 *
 * To indicate that a field item applies to one translation of an entity and
 * not another, the property path must originate with a translation of the
 * entity. This is the reason for using target_instances, from which the
 * property path can be traversed up to the root.
 *
 * @param \Drupal\Core\Field\FieldItemInterface $field_item
 *   The untranslated field item instance.
 * @param $langcode
 *   The langcode.
 *
 * @return \Drupal\Core\Field\FieldItemInterface
 *   The translated field item instance.
 */
protected function createTranslatedInstance(FieldItemInterface $item, $langcode) {

Writing code, both science and art, London, March 2019

/**
 * Implements hook_entity_bundle_info_alter().
 */
function forum_entity_bundle_info_alter(&$bundles) {

/**
 * Implements hook_ENTITY_TYPE_presave() for node entities.
 *
 * Assigns the forum taxonomy when adding a topic from within a forum.
 */
function forum_node_presave(EntityInterface $node) {

Writing code, both science and art, London, March 2019

CSS

  • Very similar to the default formatting
  • Indentation, spaces, whitespace
  • New lines between rulesets, none between related rulesets
  • File and inline comments

Writing code, both science and art, London, March 2019

JavaScript

  • File closure and strict mode
  • Naming conventions on variables, functions and constants
  • Do not use global variables
  • Function declaration and calls
  • Commenting your code

Writing code, both science and art, London, March 2019

/**
 * @file
 * Attaches behavior for the Editor module.
 */

(function ($, Drupal, drupalSettings) {

  'use strict';

  ...

})(jQuery, Drupal, drupalSettings);

Writing code, both science and art, London, March 2019

/**
  * Detaches editor behaviors from the field.
  *
  * @param {HTMLElement} field
  *   The textarea DOM element.
  * @param {object} format
  *   The text format that's being activated, from
  *   drupalSettings.editor.formats.
  * @param {string} trigger
  *   Trigger value from the detach behavior.
  */
Drupal.editorDetach = function (field, format, trigger) {
  if (format.editor) {
    Drupal.editors[format.editor].detach(field, format, trigger);

    // Restore the original value if the user didn't make any changes yet.
    if (field.getAttribute('data-editor-value-is-changed') === 'false') {
      field.value = field.getAttribute('data-editor-value-original');
    }
  }
};

Writing code, both science and art, London, March 2019

/**
  * Enables editors on text_format elements.
  *
  * @type {Drupal~behavior}
  *
  * @prop {Drupal~behaviorAttach} attach
  *   Attaches an editor to an input element.
  * @prop {Drupal~behaviorDetach} detach
  *   Detaches an editor from an input element.
  */
Drupal.behaviors.editor = {
  attach: function (context, settings) {
    // If there are no editor settings, there are no editors to enable.
    if (!settings.editor) {
      return;
    }

    $(context).find('[data-editor-for]').once('editor').each(function () {
      var $this = $(this);
      
      ...
    });

  }
};

Writing code, both science and art, London, March 2019

Twig

  • Looping over data
  • Attributes and filters
  • Comments - file doc block
  • Do not use any logic

Writing code, both science and art, London, March 2019

{% for container in containers %}
  <div class="layout-column layout-column--half">
    {% for block in container.blocks %}
      {{ block }}
    {% endfor %}
  </div>
{% endfor %}

Writing code, both science and art, London, March 2019

{#
/**
 * @file
 * Default theme implementation to display a block.
 *
 * Available variables:
 * - plugin_id: The ID of the block implementation.
 * - label: The configured label of the block if visible.
 * - configuration: A list of the block's configuration values.
 *   - label: The configured label for the block.
 *   - label_display: The display settings for the label.
 *   - provider: The module or other provider that provided this block plugin.
 *   - Block plugin specific settings will also be stored here.
 * - content: The content of this block.
 * - attributes: array of HTML attributes populated by modules, intended to
 *   be added to the main container tag of this template.
 *   - id: A valid HTML ID and guaranteed unique.
 * - title_attributes: Same as attributes, except applied to the main title
 *   tag that appears in the template.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the main title tag that appears in the template.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the main title tag that appears in the template.
 *
 * @see template_preprocess_block()
 *
 * @ingroup themeable
 */
#}

Writing code, both science and art, London, March 2019

Object oriented code

  • Classes and interfaces
  • Methods and properties
  • Translate function, with placeholders
  • @variable, %variable, :variable
  • Dependency injection 

Writing code, both science and art, London, March 2019

namespace Drupal\block\Entity;

use Drupal\Core\Cache\Cache;
...

/**
 * Defines a Block configuration entity class.
 *
 * @ConfigEntityType(
 * ...
 */
class Block extends ConfigEntityBase implements BlockInterface, EntityWithPluginCollectionInterface {

  /**
   * The ID of the block.
   *
   * @var string
   */
  protected $id;

  /**
   * The plugin collection that holds the block plugin for this entity.
   *
   * @var \Drupal\block\BlockPluginCollection
   */
  protected $pluginCollection;

  /**
   * {@inheritdoc}
   */
  public function getPlugin() {
    return $this->getPluginCollection()->get($this->plugin);
  }

  ...

}

Writing code, both science and art, London, March 2019

/**
  * Constructs a ContentEntityForm object.
  *
  * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
  *   The entity manager.
  * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
  *   The factory for the temp store object.
  */
public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory) {
  parent::__construct($entity_manager);
  $this->tempStoreFactory = $temp_store_factory;
}

/**
  * {@inheritdoc}
  */
public static function create(ContainerInterface $container) {
  return new static(
    $container->get('entity.manager'),
    $container->get('user.private_tempstore')
  );
}

/**
  * {@inheritdoc}
  */
public function save(array $form, FormStateInterface $form_state) {
  $node = $this->entity;
  $insert = $node->isNew();
  $node->save();
  $node_link = $node->link($this->t('View'));

Writing code, both science and art, London, March 2019

$this->t('Direction that text in this language is presented.');

$this->t('The %label search page has been updated.', [
  '%label' => $this->entity->label()
]);

Writing code, both science and art, London, March 2019

What can (should) you do?

  • Set up your code editor
  • Use Coder and CodeSniffer
  • Formatting comes a long way
  • Be consistent and respect style

Writing code, both science and art, London, March 2019

Using Coder Sniffer

  • PHP CodeSniffer
  • Coder, provides rulesets
  • Manual checks
  • Using code editors

Writing code, both science and art, London, March 2019

Installing necessities

Writing code, both science and art, London, March 2019

composer global require drupal/coder:8.x
composer global require dealerdirect/phpcodesniffer-composer-installer
phpcs -i

Manual steps, more options are available, but the above is the bare minimum

PHPStorm

Writing code, both science and art, London, March 2019

Other editors

Writing code, both science and art, London, March 2019

  • Visual Studio (extension)
  • Sublime Text (plugin)
  • Atom (plugin)

Be consistent!

Writing code, both science and art, London, March 2019

Simply making it work doesn't cut it.

Writing code, both science and art, London, March 2019

Documentation and resources

https://www.drupal.org/docs/develop/standards

https://www.drupal.org/docs/8/modules/code-review-module

 

Writing code, both science and art, London, March 2019

@jnzavrl | jzavrl | janzavrl.me

@ndp_studio | ndp-studio.com

Questions, comments?

Feel free to give me a nudge outside.

Writing code, both science and art, London, March 2019

Thank you

Made with Slides.com