<!doctype html>
<html lang="en">

  <head>
    <meta charset="utf-8">

    <title>Drupal VM, Meet Symfony Console</title>

    <meta name="description" content="Drupal VM, Meet Symfony Console">
    <meta name="author" content="Oliver Davies">

    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <link rel="stylesheet" href="css/reveal.css">
    <link rel="stylesheet" href="css/theme/simple.css" id="theme">
    <link rel="stylesheet" href="lib/css/tomorrow-night-bright.css">
    <link rel="stylesheet" href="assets/css/custom.css">

    <!-- Printing and PDF exports -->
    <script>
      var link = document.createElement( 'link' );
      link.rel = 'stylesheet';
      link.type = 'text/css';
      link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
      document.getElementsByTagName( 'head' )[0].appendChild( link );
    </script>

    <!--[if lt IE 9]>
    <script src="lib/js/html5shiv.js"></script>
    <![endif]-->
  </head>

  <body>
    <div class="reveal">
      <div class="slides">

      <section>
          <h1 class="big">Drupal VM, Meet Symfony Console</h1>

          <p>
            <img class="no-border" src="assets/images/drupalcamp-bristol.png" >
          </p>
        </section>

        <section>
          <h2>Oliver Davies (opdavies)</h2>

          <div class="col--6-5">
            <ul class="bullets medium">
              <li>Senior Drupal Developer for Appnovation</li>
              <li>Symfony hobbyist</li>
              <li>Drupal VM user, Drupal VM Generator maintainer</li>
              <li>Drupal Bristol organiser, PHPSW co-organiser, DrupalCamp committee member</li>
            </ul>
          </div>

          <div class="col--6-1">
            <img src="assets/images/me_thumb.jpg" class="no-border">
            <img src="assets/images/appno.jpg" class="no-border">
            <!-- <img src="assets/images/drupal-bristol.jpg" class="no-border"> -->
          </div>
        </section>

        <section>
          <h2>Prerequisites</h2>

          <ul class="bullets">
            <li>Object-orientated PHP</li>
            <li>Composer</li>
            <li>Autoloading, PSR-4</li>
          </ul>
        </section>

        <section>
          <h2>About Drupal VM</h2>

          <ul class="bullets">
            <li>Virtual machine for Drupal development</li>
            <li>Developed and maintained by Jeff Geerling</li>
            <li>Vagrant, Ansible</li>
            <li>Configured via YAML files</li>
            <li>Customisable</li>
          </ul>
        </section>

        <section>
          <h2>Using Drupal VM (< 3.0)</h2>

          <ul class="bullets">
            <li>Download the project</li>
            <li>Copy example.config.yml to config.yml</li>
            <li>Edit values</li>
            <li>Start the VM</li>
          </ul>
        </section>

        <section>
          <h2>Using Drupal VM (>= 3.0)</h2>

          <ul class="bullets">
            <li>Download the project</li>
            <li>default.config.yml contains default values</li>
            <li>Make config.yml if needed and override values</li>
            <li>Start the VM</li>
          </ul>
        </section>

        <section>
          <h2>About the Drupal VM Generator</h2>

            <ul class="bullets">
              <li>Symfony application</li>
              <li>Twig</li>
              <li>Generates minimal, use-case specific configuration files</li>
              <li>http://bit.ly/announcing-drupal-vm-generator</li>
            </ul>
        </section>

        <section>
          <h2>Using Drupal VM (>= 3.0)</h2>

          <ul class="bullets">
            <li>Download the project</li>
            <li>default.config.yml contains default values</li>
            <li><strong>Make config.yml if needed and override values</strong></li>
            <li>Start the VM</li>
          </ul>

          <aside class="notes">
            <ul class="bullets">
              <li>Time consuming - 297 lines</li>
              <li>Too much cruft</li>
            </ul>
          </aside>
        </section>

        <section>
          <h2>Drupal VM Generator Example 1</h2>
          <img src="assets/images/drupal-vm-generator-example.gif" alt="">
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Drupal VM Generator Example 2</h2>
          <pre><code class="php text-medium">
drupalvm config:generate \
  --machine-name="drupalbristol" \
  --hostname="drupalbristol.l" \
  --ip-address="192.168.88.88" \
  --cpus="1" --memory="512" \
  --webserver="nginx" --drupal-version="8" \
  --database-name="drupal" --database-user="drupal" \
  --database-password="drupal" --build-makefile=false \
  ...
          </code></pre>
        </section>

        <section>
          <h2>CLI Examples</h2>

          <ul class="bullets">
            <li>Drush</li>
            <li>Symfony/Drupal Console</li>
            <li>Terminus (Pantheon)/Platform.sh</li>
            <li>Artisan (Laravel)</li>
            <li>Composer</li>
            <li>Sculpin</li>
          </ul>
        </section>

        <section>
          <h2>CLI Examples</h2>

          <ul class="bullets">
            <li><s>Drush</s></li>
            <li>Symfony/Drupal Console</li>
            <li>Terminus (Pantheon)/Platform.sh</li>
            <li>Artisan (Laravel)</li>
            <li>Composer</li>
            <li>Sculpin</li>
          </ul>

          <aside class="notes">
            <ul class="bullets">
              <li>All of these except Drush are based on Symfony Console</li>
            </ul>
          </aside>
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h1 class="white big">Symfony Console</h1>
        </section>

        <section>
          <h3 class="title">The Console component eases the creation of beautiful and testable command line interfaces.</h3>

          <p>The Console component allows you to create command-line commands. Your console commands can be used for any recurring task, such as cronjobs, imports, or other batch jobs.</p>

          <br>
          <div class="text-small">
            <p>
              <a href="http://symfony.com/doc/current/components/console/introduction.html">http://symfony.com/doc/current/components/console/introduction.html</a>
            </p>
          </div>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Installation</h2>

          <pre><code class="bash text-big" data-trim>
$ composer require symfony/console

$ composer install
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Installation (cont)</h2>

          <pre><code class="json text-big" data-trim>
# composer.json

"require": {
    "symfony/console": "^3.1"
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Installation (cont)</h2>
          <pre><code class="php text-big">
# app.php

require __DIR__ . '/vendor/autoload.php';

// Do stuff.
          </code></pre>
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Building a Console Application</h2>
        </section>

        <section>
          <h2>Steps</h2>

          <ul class="bullets">
            <li>Download the Console component</li>
            <li>Add an "entry point"</li>
            <li>Configure and run an <code>Application</code> class</li>
            <li>Add new Commands</li>
          </ul>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">bin/drupalcamp</h2>

          <pre><code class="php text-big">
#!/usr/bin/env php

require __DIR__ . '/drupalcamp.php';
          </code></pre>

          <aside class="notes">
            <ul class="bullets">
              <li>No .php file extension</li>
              <li>Formatting issues</li>
              <li>Convenience</li>
            </ul>
          </aside>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">bin/drupalcamp.php</h2>

          <pre><code class="php text-medium">
require __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Console\Application;

$application = new Application();
$application->run();
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <img src="assets/images/console-application-1.png" alt="" style="max-width: 100%">
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">bin/drupalcamp.php (cont)</h2>

          <pre><code class="php text-medium">
require __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Console\Application;

$application = new Application();
$application->run('DrupalCamps', '1.0');
          </code></pre>

          <aside class="notes">
            <ul>
              <li>No .php extension</li>
            </ul>
          </aside>
        </section>

        <section data-background="#000" data-background-transition="none">
          <img src="assets/images/console-application-2.png" alt="">
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Adding Commands</h2>
        </section>

        <section>
          <h2>Adding Commands</h2>
          <ul class="bullets">
            <li>Add command classes in <code>src/</code></li>
            <li>Autoload via Composer</li>
            <li>Add to <code>Application</code></li>
          </ul>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Autoloading via Composer</h2>

          <pre><code class="php text-big" data-trim>
# composer.json

"autoload": {
  "psr-4": {
    "": "src/"
  }
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">Autoloading via Composer</h2>

          <pre><code class="php text-big" data-trim>
# composer.json

"autoload": {
  "psr-4": {
    "DrupalCamps\\": "src/"
  }
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php</h2>

          <pre><code class="php text-medium" data-trim>
use Symfony\Component\Console\Command\Command;

class GoCommand extends Command
{
    public function configure()
    {
        $this->setName('go')
            ->setDescription('Go to a DrupalCamp.');
    }
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php (cont)</h2>

          <pre><code class="php text-normal">
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

...

public function execute(InputInterface $input, OutputInterface $output)
{
    // Execute the command.
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">bin/drupalcamp.php</h2>

          <pre><code class="php text-big">
$application = new Application();

$application->add(new GoCommand());

$application->run();
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">bin/drupalcamp.php</h2>

          <pre><code class="php text-big">
$application = new Application();

$application->addCommands(
    [
        new GoCommand(),
    ]
);

$application->run();
          </code></pre>
        </section>

        <section data-background='#000' data-background-transition="none">
          <img src="assets/images/go-command-1.png" alt="">
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Arguments and Options</h2>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php</h2>

          <pre><code class="php text-normal" data-trim>
use Symfony\Component\Console\Input\InputArgument;

...

public function configure()
{
    $this->setName('go')
        ->setDescription('Go to a DrupalCamp')
        ->addArgument('name', InputArgument::OPTIONAL, 'Which DrupalCamp?')
        ->addOption('past', null, InputOption::VALUE_NONE);
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php (cont)</h2>

          <pre><code class="php text-normal" data-trim>
public function execute(InputInterface $input, OutputInterface $output)
{
    $text = $input->getArgument('name');
    $past = $input->getOption('past');

    ...
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <img src="assets/images/command-arguments.png" alt="">
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Input and Output</h2>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">InputInterface</h2>
          <ul class="bullets">
            <pre><code class="php text-big" data-trim>
$input->getArgument('foo');

$input->getOption('bar');
          </code></pre>
          <aside class="notes">
            Gets values inputted by the user.
          </aside>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">OutputInterface</h2>
          <pre><code class="php text-big" data-trim>
$output->write($text);

$output->writeln($text);
          </code></pre>
        </section>

         <section data-background="#000" data-background-transition="none">
          <h2 class="white">OutputInterface</h2>
          <pre><code class="php text-big" data-trim>
$output->write("<info>$text</info>");

$output->write("<error>$text</error>");

$output->write("<debug>$text</debug>");
          </code></pre>
        </section>

        <section>
          <h2>SymfonyStyle</h2>

          <div class="col--6-3">
            <ul class="bullets">
              <li>title</li>
              <li>section</li>
              <li>text</li>
              <li>comment</li>
              <li>note</li>
              <li>caution</li>
              <li>table</li>
            </ul>
          </div>

          <div class="col--6-3">
            <ul class="bullets">
              <li>ask</li>
              <li>askHidden</li>
              <li>confirm</li>
              <li>choice</li>
              <li>success</li>
              <li>error</li>
              <li>warning</li>
            </ul>
          </div>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">SymfonyStyle</h2>

          <pre><code class="php text-normal">
public function execute(InputInterface $input, OutputInterface $output)
{
    $io = new SymfonyStyle($input, $output);

    $io->table(
        ['Title', 'Speaker'],
        [
            ['Drupal VM, Meet Symfony Console', 'Oliver Davies'],
            ['Drupal 8 and the Symfony EventDispatcher', 'Eric Smith'],
            ['Building with APIs', 'Nigel Dunn'],
        ]
    );
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <img src="assets/images/talks-command.png" alt="">
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Interaction</h2>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php</h2>
          <pre><code class="php text-normal">
public function interact(InputInterface $input, OutputInterface $output)
{
    $io = new SymfonyStyle($input, $output);

    if (!$input->getArgument('name')) {
        $input->setArgument('name', $io->ask('Which DrupalCamp?'));
    }
}
          </code></pre>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/GoCommand.php</h2>
          <pre><code class="php text-normal">
public function execute(InputInterface $input, OutputInterface $output)
{
    $io = new SymfonyStyle($input, $output);

    $io->success($input->getArgument('name'));
}
          </code></pre>
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Dependency Injection</h2>
        </section>

        <section>
          <h2>Dependency Injection</h2>

          <ul class="bullets">
            <li>Instantiate dependencies in single place or DI container</li>
            <li>Add as arguments when adding commands</li>
            <li>Add as arguments to the constructor and assign to properties</li>
          </ul>

          <h3 class="note">
            Make sure to call the __construct() method within the parent class.
          </h3>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/Console/Application.php</h2>

          <pre><code class="php text-medium" data-trim>
use GuzzleHttp\Client;

...

$client = new Client();

$application->add(new NewCommand($client));
          </code></pre>

          <aside class="notes">
            <ul class="bullets">
              <li>Example from the Drupal VM Generator</li>
              <li>Downloads an archive of Drupal VM from GitHub</li>
              <li></li>
            </ul>
          </aside>
        </section>

        <section data-background="#000" data-background-transition="none">
          <h2 class="white">src/Command/NewCommand.php</h2>

          <pre><code class="php text-normal" data-trim>
use GuzzleHttp\ClientInterface;
use Symfony\Component\Console\Command;

final class NewCommand extends Command
{
    private $client;

    public function __construct(ClientInterface $client)
    {
        parent::__construct();

        $this->client = $client;
    }
}
          </code></pre>
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Distributing Your Application</h2>
        </section>

        <section>
          <h2>.phar files</h2>

          <ul class="bullets">
            <li>PHP archive file</li>
            <li>Packages your application into one file</li>
            <li><code>box build</code></li>
          </ul>
        </section>

        <section data-background='#000' data-background-transition="none">
          <h2 class="white">Generating phar files</h2>
          <pre><code class="json text-small">
# composer.json

"scripts": {
    "build": [
        "box build",
        "shasum drupalvm.phar |
         awk '{print $1}' > drupalvm.phar.version"
    ]
}
          </code></pre>

          <pre><code class="bash text-small">
$ composer run-script build
          </code></pre>
        </section>
        <section>
          <h2>Roadmap</h2>

          <ul class="bullets medium">
            <li>Keep up to date with Drupal VM stable releases</li>
            <li>New commands - updating existing files, adding vhosts</li>
            <li>Improve user defaults and settings</li>
          </ul>
        </section>

        <section>
          <h2>Resources</h2>

          <ul class="bullets medium">
            <li>https://github.com/opdavies/drupal-vm-generator</li>
            <li>https://www.drupalvmgenerator.com</li>
            <!-- <li>http://docs.drupalvmgenerator.com</li> -->
            <li>http://bit.ly/announcing-drupal-vm-generator</li>
            <li>http://symfony.com/doc/current/components</li>
            <li>http://symfony.com/doc/current/cookbook/console</li>
          </ul>
        </section>

        <section data-background="#0076C2" data-background-transition="none">
          <h2 class="white">Questions?</h2>
        </section>

        <section>
          <h2>Feedback</h2>

          <ul class="bullets">
            <li>@opdavies</li>
            <li>https://www.oliverdavies.uk</li>
          </ul>
        </section>

      </div>

    </div>

    <script src="lib/js/head.min.js"></script>
    <script src="js/reveal.js"></script>

    <script>

      // More info https://github.com/hakimel/reveal.js#configuration
      Reveal.initialize({
        controls: false,
        progress: true,
        history: true,
        center: true,

        transition: 'none', // none/fade/slide/convex/concave/zoom

        // More info https://github.com/hakimel/reveal.js#dependencies
        dependencies: [
          { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
          { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
          { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
          { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
          { src: 'plugin/zoom-js/zoom.js', async: true },
          { src: 'plugin/notes/notes.js', async: true }
        ]
      });

    </script>

  </body>
</html>