Introducing the Drupal 8 Console Scaffolding Module Generator

jam's DrupalCamp 2014

Jesus Manuel Olivas



Web Developer Symfony & Drupal


http://jmolivas.com

https://github.com/jmolivas

https://github.com/hechoendrupal

@jmolivas | @drupodcast | @drupal8links

About Drupal 8 Console


Every modern framework nowadays provides a scaffolding tool code generator for speeding up the process of starting a new project and avoid the repetitive tasks.

The purpose of this project is to leverage the Symfony Console Component to provide a CLI tool to automate the creation of Drupal 8 modules.

Where do I find the project?



Do you have a Drupal user ?

https://drupal.org/project/console


Do you have a Github account ?

https://github.com/hechoendrupal/DrupalAppConsole

Who started this ?

Jesús Manuel Olivas

@jmolivas

http://jmolivas.com

David Flores ‏

@dmouse

http://dmouse.net

But you can blame him

What is out of the box?

  • Generates module and info files.
  • Generates PSR-4 compliant directory structure for a module.
  • Register routes on YML files and map to controller and form PHP Classes.
  • Create classes adding namespaces, uses and also the extend and implements keywords when required.
  • Support adding services using Dependency Injection on class generation.

Who will benefit of using it?


Module Maintainers & Developers

Create & Migrate contributed modules to Drupal 8.


Drupal Trainers & Consultors

Train developers on Drupal 8.


Drupal Shops

Reduce module development time for Drupal 8.

Anatomy of a Drupal 8 module


Directory Structure

acme.info.yml


name: acme
type: module
description: Acme Module
core: 8.x
package: Other

acme.module


/**
 * Implements hook_theme().
 */

  function acme_theme() {
    $theme['hello_page'] = [
      'variables' => ['name' => NULL],
      'template' => 'hello',
    ];

    return $theme;
  }

acme.routing.yml


acme.hello:
  path: '/acme/hello/{name}'
  defaults:
    _content: '\Drupal\acme\Controller\DefaultController::hello'
    _title: 'acme Title'
  requirements:
    _permission: 'access content'

acme.defaultform_form:
  path: '/acme/settings/DefaultForm'
  defaults:
    _form: '\Drupal\acme\Form\DefaultForm'
    _title: 'acme Form'
  requirements:
    _permission: 'access administration pages'

src/Controller/DefaultController.php

namespace Drupal\acme\Controller;

use Drupal\Core\Controller\ControllerBase;

class DefaultController extends ControllerBase 
{
  public function hello($name) 
  {
    return [
      '#theme' => 'hello_page',
      '#name' => $name,
      '#attached' => [ 
        'css' => [ drupal_get_path('module', 'acme') . '/assets/css/acme.css' ]
      ]
    ];
  }
}

templates/hello.html.twig


{# templates/hello.html.twig #}

Hello {{ name }}!

Hello Druplicon !

Demo


Live demo always a bad idea

Requirements


Virtual Machine

https://drupal.org/project/vm

Drupal Console

https://drupal.org/project/console

TTools library

https://github.com/ttools/ttools

Clone VM & add host


# Clone repo drupal8.dev

$ git clone --branch master
  http://git.drupal.org/project/vm.git drupal8.dev
$ cd drupal8.dev
$ vagrant up


# Add VM to hosts file

192.168.9.10 drupal8.dev

Clone and Install Drupal8


# Clone Drupal 8 repo

$ vagrant ssh
$ cd /vagrant/www
$ git clone --branch 8.x
  http://git.drupal.org/project/drupal.git drupal8.dev


# Install Drupal 8

$ drush si standard
  --db-url=mysql://drupal:drupal@localhost/drupal
  --site-name=drupal8.dev --account-name=admin
  --account-pass=admin
  --account-mail=[user-email]

NOTE: You may also install Drupal 8 using the GUI accessing http://drupal8.dev

Install Drupal 8 Console


$ cd path/to/drupal8.dev

$ vagrant ssh

$ cd /vagrant/www/drupal8.dev

$ COMPOSER_BIN_DIR=bin composer require
  --dev drupal/console:dev-master

$ bin/console --help

Add TTools library


$ composer require "ttools/ttools":"2.0.*@dev"

{
  "require": {
		"ttools/ttools": "2.0.*@dev"
  }
}

$ composer update

Generators


Generate Module


$ bin/console generate:module

Generate Controller


$ bin/console generate:controller

Generate Form


$ bin/console generate:form

Generate Block


$ bin/console generate:plugin:block

Implement the TTools Library


src/Service/TTools.php

<?php

namespace Drupal\ttools\Service;

use Drupal\Core\Config\ConfigFactory;

class TTools
{

  protected $config;

  protected $ttoolsApp;

  public function __construct(ConfigFactory $config) {
    $this->config = $config;
  }

  protected function getApp() {
    if (!$this->ttoolsApp) {
      $settingsForm = $this->config->get("ttools.settingsform_config");

      $config = [
        "consumer_key"        => $settingsForm->get("consumer_key"),
        "consumer_secret"     => $settingsForm->get("consumer_secret"),
        "access_token"        => $settingsForm->get("access_token") ?: null,
        "access_token_secret" => $settingsForm->get("access_token_secret") ?: null,
      ];

      $this->ttoolsApp = new \TTools\App($config);
    } 
    return $this->ttoolsApp;  
  }

  public function getUserTimeline(){
    $settingsForm = $this->config->get("ttools.settingsform_config");

    $screen_name = $settingsForm->get("screen_name");
    $limit = $settingsForm->get("limit") ? $settingsForm->get("limit") : 10;
    $stream = $this->getApp()->getUserTimeline(null, $screen_name, $limit);

    return $stream;
  }

}

ttools.services.yml


services:
  ttools.app:
    class: Drupal\ttools\Service\TTools
    arguments: ['@config.factory']
    tags:
      - { name: ttools_app }

acme.module


function ttools_theme()
{

  $theme['timeline_page'] = [
    'variables' => ['stream' => NULL],
    'template' => 'timeline',
  ];

  return $theme;
}

templates/timeline.html.twig


{% for tweet in stream %} {% if tweet.retweeted %} {{ include('modules/ttools/templates/tweet.html.twig', {user: tweet.retweeted_status.user, text: tweet.retweeted_status.text}) }} {% else %} {{ include('modules/ttools/templates/tweet.html.twig', {user: tweet.user, text: tweet.text}) }} {% endif %} {% endfor %}

templates/tweet.html.twig


{{ user.name }} @{{ user.screen_name }}
{{ text }}

assets/css/ttools.css


.tweet {
   clear:both;
   position: relative;
   line-height: 1.25em;
   border-bottom: 1px solid #dedede;
}

.user-profile_image,
.user-name-screen_name-text {
  padding: 5px;
  float:left;
  display:inline;
}

.user-profile_image {
  width: auto;
}

.user-name-screen_name-text {
  padding-top: 10px;
  width: 85%;
}

.sidebar .user-name-screen_name-text {
  width: 60%;
}

.user-profile_image a {
  border: none;
}

src/Controller/TimelineController.php


  public function stream() 
  {
    $stream = $this->ttools_app->getUserTimeline();

    return [
      '#theme' => 'timeline_page',
      '#stream' => $stream,
      '#attached' => [ 
        'css' => [
          drupal_get_path('module', 'ttools') . '/assets/css/ttools.css'
        ]
      ]
    ];
  }

src/Plugin/Block/TimelineBlock.php


  public function build()
  {
    $stream = $this->ttools_app->getUserTimeline();

    return [
      '#theme' => 'timeline_page',
      '#stream' => $stream,
      '#attached' => [ 
        'css' => [
          drupal_get_path('module', 'ttools') . '/assets/css/ttools.css'
        ]
      ]
    ];
  }

Jesus Manuel Olivas



Web Developer Symfony & Drupal


http://jmolivas.com

https://github.com/jmolivas

https://github.com/hechoendrupal

@jmolivas | @drupodcast | @drupal8links