Skip to content
This repository
Browse code

merged branch umpirsky/console-helper-table (PR #6368)

This PR was squashed before being merged into the master branch (closes #6368).

Discussion
----------

[2.3] [Console] TableHelper

When building a console application it may be useful to display tabular data.

`TableHelper` can display table header and rows, customizable alignment of columns, cell padding and colors.

Basic usage example:
```php
$table = $app->getHelperSet()->get('table');
$table
    ->setHeaders(array('ISBN', 'Title', 'Author'))
    ->setRows(array(
        array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
        array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
        array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
        array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
    ))
;
$table->render($output);
```
Output:
![table](https://f.cloud.github.com/assets/208957/14955/6fb4f500-46ca-11e2-8435-0f6b22f96e58.png)

If this PR gets merged I will submit doc PR as well.

I'm sure there is a plenty of room for improvements so any feedback is welcome.

Commits
-------

8de7813 [2.3] [Console] TableHelper
  • Loading branch information...
commit 22adfdf504e1c9cf605738ffc3c350ec68b3d1b6 2 parents a7c9863 + 8de7813
Fabien Potencier authored April 07, 2013
2  src/Symfony/Component/Console/Application.php
@@ -27,6 +27,7 @@
27 27
 use Symfony\Component\Console\Helper\FormatterHelper;
28 28
 use Symfony\Component\Console\Helper\DialogHelper;
29 29
 use Symfony\Component\Console\Helper\ProgressHelper;
  30
+use Symfony\Component\Console\Helper\TableHelper;
30 31
 use Symfony\Component\Console\Event\ConsoleCommandEvent;
31 32
 use Symfony\Component\Console\Event\ConsoleForExceptionEvent;
32 33
 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
@@ -1013,6 +1014,7 @@ protected function getDefaultHelperSet()
1013 1014
             new FormatterHelper(),
1014 1015
             new DialogHelper(),
1015 1016
             new ProgressHelper(),
  1017
+            new TableHelper(),
1016 1018
         ));
1017 1019
     }
1018 1020
 
1  src/Symfony/Component/Console/CHANGELOG.md
Source Rendered
@@ -4,6 +4,7 @@ CHANGELOG
4 4
 2.3.0
5 5
 -----
6 6
 
  7
+ * added Table Helper for tabular data rendering
7 8
  * added support for events in `Application`
8 9
  * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()`
9 10
  * added a way to set the progress bar progress via the `setCurrent` method
20  src/Symfony/Component/Console/Helper/FormatterHelper.php
@@ -71,26 +71,6 @@ public function formatBlock($messages, $style, $large = false)
71 71
     }
72 72
 
73 73
     /**
74  
-     * Returns the length of a string, using mb_strlen if it is available.
75  
-     *
76  
-     * @param string $string The string to check its length
77  
-     *
78  
-     * @return integer The length of the string
79  
-     */
80  
-    private function strlen($string)
81  
-    {
82  
-        if (!function_exists('mb_strlen')) {
83  
-            return strlen($string);
84  
-        }
85  
-
86  
-        if (false === $encoding = mb_detect_encoding($string)) {
87  
-            return strlen($string);
88  
-        }
89  
-
90  
-        return mb_strlen($string, $encoding);
91  
-    }
92  
-
93  
-    /**
94 74
      * {@inheritDoc}
95 75
      */
96 76
     public function getName()
20  src/Symfony/Component/Console/Helper/Helper.php
@@ -39,4 +39,24 @@ public function getHelperSet()
39 39
     {
40 40
         return $this->helperSet;
41 41
     }
  42
+
  43
+    /**
  44
+     * Returns the length of a string, using mb_strlen if it is available.
  45
+     *
  46
+     * @param string $string The string to check its length
  47
+     *
  48
+     * @return integer The length of the string
  49
+     */
  50
+    protected function strlen($string)
  51
+    {
  52
+        if (!function_exists('mb_strlen')) {
  53
+            return strlen($string);
  54
+        }
  55
+
  56
+        if (false === $encoding = mb_detect_encoding($string)) {
  57
+            return strlen($string);
  58
+        }
  59
+
  60
+        return mb_strlen($string, $encoding);
  61
+    }
42 62
 }
29  src/Symfony/Component/Console/Helper/ProgressHelper.php
@@ -14,7 +14,7 @@
14 14
 use Symfony\Component\Console\Output\OutputInterface;
15 15
 
16 16
 /**
17  
- * The Progress class providers helpers to display progress output.
  17
+ * The Progress class provides helpers to display progress output.
18 18
  *
19 19
  * @author Chris Jones <leeked@gmail.com>
20 20
  * @author Fabien Potencier <fabien@symfony.com>
@@ -320,7 +320,7 @@ private function initialize()
320 320
         }
321 321
 
322 322
         if ($this->max > 0) {
323  
-            $this->widths['max']     = $this->getLength($this->max);
  323
+            $this->widths['max']     = $this->strlen($this->max);
324 324
             $this->widths['current'] = $this->widths['max'];
325 325
         } else {
326 326
             $this->barCharOriginal = $this->barChar;
@@ -356,7 +356,7 @@ private function generate($finish = false)
356 356
                 }
357 357
             }
358 358
 
359  
-            $emptyBars = $this->barWidth - $completeBars - $this->getLength($this->progressChar);
  359
+            $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar);
360 360
             $bar = str_repeat($this->barChar, $completeBars);
361 361
             if ($completeBars < $this->barWidth) {
362 362
                 $bar .= $this->progressChar;
@@ -419,7 +419,7 @@ private function humaneTime($secs)
419 419
      */
420 420
     private function overwrite(OutputInterface $output, $message)
421 421
     {
422  
-        $length = $this->getLength($message);
  422
+        $length = $this->strlen($message);
423 423
 
424 424
         // append whitespace to match the last line's length
425 425
         if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) {
@@ -430,26 +430,7 @@ private function overwrite(OutputInterface $output, $message)
430 430
         $output->write("\x0D");
431 431
         $output->write($message);
432 432
 
433  
-        $this->lastMessagesLength = $this->getLength($message);
434  
-    }
435  
-
436  
-    /**
437  
-     * Wrapper arround strlen: uses multi-byte function if available
438  
-     *
439  
-     * @param  string $string
440  
-     * @return integer
441  
-     */
442  
-    private function getLength($string)
443  
-    {
444  
-        if (!function_exists('mb_strlen')) {
445  
-            return strlen($string);
446  
-        }
447  
-
448  
-        if (false === $encoding = mb_detect_encoding($string)) {
449  
-            return strlen($string);
450  
-        }
451  
-
452  
-        return mb_strlen($string, $encoding);
  433
+        $this->lastMessagesLength = $this->strlen($message);
453 434
     }
454 435
 
455 436
     /**
453  src/Symfony/Component/Console/Helper/TableHelper.php
... ...
@@ -0,0 +1,453 @@
  1
+<?php
  2
+
  3
+/*
  4
+ * This file is part of the Symfony package.
  5
+ *
  6
+ * (c) Fabien Potencier <fabien@symfony.com>
  7
+ *
  8
+ * For the full copyright and license information, please view the LICENSE
  9
+ * file that was distributed with this source code.
  10
+ */
  11
+
  12
+namespace Symfony\Component\Console\Helper;
  13
+
  14
+use Symfony\Component\Console\Output\OutputInterface;
  15
+use InvalidArgumentException;
  16
+
  17
+/**
  18
+ * Provides helpers to display table output.
  19
+ *
  20
+ * @author Саша Стаменковић <umpirsky@gmail.com>
  21
+ */
  22
+class TableHelper extends Helper
  23
+{
  24
+    const LAYOUT_DEFAULT = 0;
  25
+    const LAYOUT_BORDERLESS = 1;
  26
+
  27
+    /**
  28
+     * Table headers.
  29
+     *
  30
+     * @var array
  31
+     */
  32
+    private $headers = array();
  33
+
  34
+    /**
  35
+     * Table rows.
  36
+     *
  37
+     * @var array
  38
+     */
  39
+    private $rows = array();
  40
+
  41
+    // Rendering options
  42
+    private $paddingChar;
  43
+    private $horizontalBorderChar;
  44
+    private $verticalBorderChar;
  45
+    private $crossingChar;
  46
+    private $cellHeaderFormat;
  47
+    private $cellRowFormat;
  48
+    private $borderFormat;
  49
+    private $padType;
  50
+
  51
+    /**
  52
+     * Column widths cache.
  53
+     *
  54
+     * @var array
  55
+     */
  56
+    private $columnWidths = array();
  57
+
  58
+    /**
  59
+     * Number of columns cache.
  60
+     *
  61
+     * @var array
  62
+     */
  63
+    private $numberOfColumns;
  64
+
  65
+    /**
  66
+     * @var OutputInterface
  67
+     */
  68
+    private $output;
  69
+
  70
+    public function __construct()
  71
+    {
  72
+        $this->setLayout(self::LAYOUT_DEFAULT);
  73
+    }
  74
+
  75
+    /**
  76
+     * Sets table layout type.
  77
+     *
  78
+     * @param int $layout self::LAYOUT_*
  79
+     *
  80
+     * @return TableHelper
  81
+     */
  82
+    public function setLayout($layout)
  83
+    {
  84
+        switch ($layout) {
  85
+            case self::LAYOUT_BORDERLESS:
  86
+                $this
  87
+                    ->setPaddingChar(' ')
  88
+                    ->setHorizontalBorderChar('=')
  89
+                    ->setVerticalBorderChar(' ')
  90
+                    ->setCrossingChar(' ')
  91
+                    ->setCellHeaderFormat('<info>%s</info>')
  92
+                    ->setCellRowFormat('<comment>%s</comment>')
  93
+                    ->setBorderFormat('%s')
  94
+                    ->setPadType(STR_PAD_RIGHT)
  95
+                ;
  96
+                break;
  97
+
  98
+            case self::LAYOUT_DEFAULT:
  99
+                $this
  100
+                    ->setPaddingChar(' ')
  101
+                    ->setHorizontalBorderChar('-')
  102
+                    ->setVerticalBorderChar('|')
  103
+                    ->setCrossingChar('+')
  104
+                    ->setCellHeaderFormat('<info>%s</info>')
  105
+                    ->setCellRowFormat('<comment>%s</comment>')
  106
+                    ->setBorderFormat('%s')
  107
+                    ->setPadType(STR_PAD_RIGHT)
  108
+                ;
  109
+                break;
  110
+
  111
+            default:
  112
+                throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
  113
+                break;
  114
+        };
  115
+
  116
+        return $this;
  117
+    }
  118
+
  119
+    public function setHeaders(array $headers)
  120
+    {
  121
+        $this->headers = array_values($headers);
  122
+
  123
+        return $this;
  124
+    }
  125
+
  126
+    public function setRows(array $rows)
  127
+    {
  128
+        $this->rows = array();
  129
+
  130
+        return $this->addRows($rows);
  131
+    }
  132
+
  133
+    public function addRows(array $rows)
  134
+    {
  135
+        foreach ($rows as $row) {
  136
+            $this->addRow($row);
  137
+        }
  138
+
  139
+        return $this;
  140
+    }
  141
+
  142
+    public function addRow(array $row)
  143
+    {
  144
+        $this->rows[] = array_values($row);
  145
+
  146
+        return $this;
  147
+    }
  148
+
  149
+    public function setRow($column, array $row)
  150
+    {
  151
+        $this->rows[$column] = $row;
  152
+
  153
+        return $this;
  154
+    }
  155
+
  156
+    /**
  157
+     * Sets padding character, used for cell padding.
  158
+     *
  159
+     * @param string $paddingChar
  160
+     *
  161
+     * @return TableHelper
  162
+     */
  163
+    public function setPaddingChar($paddingChar)
  164
+    {
  165
+        $this->paddingChar = $paddingChar;
  166
+
  167
+        return $this;
  168
+    }
  169
+
  170
+    /**
  171
+     * Sets horizontal border character.
  172
+     *
  173
+     * @param string $horizontalBorderChar
  174
+     *
  175
+     * @return TableHelper
  176
+     */
  177
+    public function setHorizontalBorderChar($horizontalBorderChar)
  178
+    {
  179
+        $this->horizontalBorderChar = $horizontalBorderChar;
  180
+
  181
+        return $this;
  182
+    }
  183
+
  184
+    /**
  185
+     * Sets vertical border character.
  186
+     *
  187
+     * @param string $verticalBorderChar
  188
+     *
  189
+     * @return TableHelper
  190
+     */
  191
+    public function setVerticalBorderChar($verticalBorderChar)
  192
+    {
  193
+        $this->verticalBorderChar = $verticalBorderChar;
  194
+
  195
+        return $this;
  196
+    }
  197
+
  198
+    /**
  199
+     * Sets crossing character.
  200
+     *
  201
+     * @param string $crossingChar
  202
+     *
  203
+     * @return TableHelper
  204
+     */
  205
+    public function setCrossingChar($crossingChar)
  206
+    {
  207
+        $this->crossingChar = $crossingChar;
  208
+
  209
+        return $this;
  210
+    }
  211
+
  212
+    /**
  213
+     * Sets header cell format.
  214
+     *
  215
+     * @param string $cellHeaderFormat
  216
+     *
  217
+     * @return TableHelper
  218
+     */
  219
+    public function setCellHeaderFormat($cellHeaderFormat)
  220
+    {
  221
+        $this->cellHeaderFormat = $cellHeaderFormat;
  222
+
  223
+        return $this;
  224
+    }
  225
+
  226
+    /**
  227
+     * Sets row cell format.
  228
+     *
  229
+     * @param string $cellRowFormat
  230
+     *
  231
+     * @return TableHelper
  232
+     */
  233
+    public function setCellRowFormat($cellRowFormat)
  234
+    {
  235
+        $this->cellRowFormat = $cellRowFormat;
  236
+
  237
+        return $this;
  238
+    }
  239
+
  240
+    /**
  241
+     * Sets table border format.
  242
+     *
  243
+     * @param string $borderFormat
  244
+     *
  245
+     * @return TableHelper
  246
+     */
  247
+    public function setBorderFormat($borderFormat)
  248
+    {
  249
+        $this->borderFormat = $borderFormat;
  250
+
  251
+        return $this;
  252
+    }
  253
+
  254
+    /**
  255
+     * Sets cell padding type.
  256
+     *
  257
+     * @param integer $padType STR_PAD_*
  258
+     *
  259
+     * @return TableHelper
  260
+     */
  261
+    public function setPadType($padType)
  262
+    {
  263
+        $this->padType = $padType;
  264
+
  265
+        return $this;
  266
+    }
  267
+
  268
+    /**
  269
+     * Renders table to output.
  270
+     *
  271
+     * Example:
  272
+     * +---------------+-----------------------+------------------+
  273
+     * | ISBN          | Title                 | Author           |
  274
+     * +---------------+-----------------------+------------------+
  275
+     * | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
  276
+     * | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
  277
+     * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  278
+     * +---------------+-----------------------+------------------+
  279
+     *
  280
+     * @param OutputInterface $output
  281
+     */
  282
+    public function render(OutputInterface $output)
  283
+    {
  284
+        $this->output = $output;
  285
+
  286
+        $this->renderRowSeparator();
  287
+        $this->renderRow($this->headers, $this->cellHeaderFormat);
  288
+        if (!empty($this->headers)) {
  289
+            $this->renderRowSeparator();
  290
+        }
  291
+        foreach ($this->rows as $row) {
  292
+            $this->renderRow($row, $this->cellRowFormat);
  293
+        }
  294
+        if (!empty($this->rows)) {
  295
+            $this->renderRowSeparator();
  296
+        }
  297
+
  298
+        $this->cleanup();
  299
+    }
  300
+
  301
+    /**
  302
+     * Renders horizontal header separator.
  303
+     *
  304
+     * Example: +-----+-----------+-------+
  305
+     */
  306
+    private function renderRowSeparator()
  307
+    {
  308
+        if (0 === $count = $this->getNumberOfColumns()) {
  309
+            return;
  310
+        }
  311
+
  312
+        $markup = $this->crossingChar;
  313
+        for ($column = 0; $column < $count; $column++) {
  314
+            $markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column))
  315
+                    .$this->crossingChar
  316
+            ;
  317
+        }
  318
+
  319
+        $this->output->writeln(sprintf($this->borderFormat, $markup));
  320
+    }
  321
+
  322
+    /**
  323
+     * Renders vertical column separator.
  324
+     */
  325
+    private function renderColumnSeparator()
  326
+    {
  327
+        $this->output->write(sprintf($this->borderFormat, $this->verticalBorderChar));
  328
+    }
  329
+
  330
+    /**
  331
+     * Renders table row.
  332
+     *
  333
+     * Example: | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
  334
+     *
  335
+     * @param array  $row
  336
+     * @param string $cellFormat
  337
+     */
  338
+    private function renderRow(array $row, $cellFormat)
  339
+    {
  340
+        if (empty($row)) {
  341
+            return;
  342
+        }
  343
+
  344
+        $this->renderColumnSeparator();
  345
+        for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
  346
+            $this->renderCell($row, $column, $cellFormat);
  347
+            $this->renderColumnSeparator();
  348
+        }
  349
+        $this->output->writeln('');
  350
+    }
  351
+
  352
+    /**
  353
+     * Renders table cell with padding.
  354
+     *
  355
+     * @param array   $row
  356
+     * @param integer $column
  357
+     * @param string  $cellFormat
  358
+     */
  359
+    private function renderCell(array $row, $column, $cellFormat)
  360
+    {
  361
+        $cell = isset($row[$column]) ? $row[$column] : '';
  362
+
  363
+        $this->output->write(sprintf(
  364
+            $cellFormat,
  365
+            str_pad(
  366
+                $this->paddingChar.$cell.$this->paddingChar,
  367
+                $this->getColumnWidth($column),
  368
+                $this->paddingChar,
  369
+                $this->padType
  370
+            )
  371
+        ));
  372
+    }
  373
+
  374
+    /**
  375
+     * Gets number of columns for this table.
  376
+     *
  377
+     * @return int
  378
+     */
  379
+    private function getNumberOfColumns()
  380
+    {
  381
+        if (null !== $this->numberOfColumns) {
  382
+            return $this->numberOfColumns;
  383
+        }
  384
+
  385
+        $columns = array(0);
  386
+        $columns[] = count($this->headers);
  387
+        foreach ($this->rows as $row) {
  388
+            $columns[] = count($row);
  389
+        }
  390
+
  391
+        return $this->numberOfColumns = max($columns);
  392
+    }
  393
+
  394
+    /**
  395
+     * Gets column width.
  396
+     *
  397
+     * @param integer $column
  398
+     *
  399
+     * @return int
  400
+     */
  401
+    private function getColumnWidth($column)
  402
+    {
  403
+        if (isset($this->columnWidths[$column])) {
  404
+            return $this->columnWidths[$column];
  405
+        }
  406
+
  407
+        $lengths = array(0);
  408
+        $lengths[] = $this->getCellWidth($this->headers, $column);
  409
+        foreach ($this->rows as $row) {
  410
+            $lengths[] = $this->getCellWidth($row, $column);
  411
+        }
  412
+
  413
+        return $this->columnWidths[$column] = max($lengths) + 2;
  414
+    }
  415
+
  416
+    /**
  417
+     * Gets cell width.
  418
+     *
  419
+     * @param array   $row
  420
+     * @param integer $column
  421
+     *
  422
+     * @return int
  423
+     */
  424
+    private function getCellWidth(array $row, $column)
  425
+    {
  426
+        if ($column < 0) {
  427
+            return 0;
  428
+        }
  429
+
  430
+        if (isset($row[$column])) {
  431
+            return $this->strlen($row[$column]);
  432
+        }
  433
+
  434
+        return $this->getCellWidth($row, $column - 1);
  435
+    }
  436
+
  437
+    /**
  438
+     * Called after rendering to cleanup cache data.
  439
+     */
  440
+    private function cleanup()
  441
+    {
  442
+        $this->columnWidths = array();
  443
+        $this->numberOfColumns = null;
  444
+    }
  445
+
  446
+    /**
  447
+     * {@inheritDoc}
  448
+     */
  449
+    public function getName()
  450
+    {
  451
+        return 'table';
  452
+    }
  453
+}
188  src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php
... ...
@@ -0,0 +1,188 @@
  1
+<?php
  2
+
  3
+/*
  4
+ * This file is part of the Symfony package.
  5
+ *
  6
+ * (c) Fabien Potencier <fabien@symfony.com>
  7
+ *
  8
+ * For the full copyright and license information, please view the LICENSE
  9
+ * file that was distributed with this source code.
  10
+ */
  11
+
  12
+namespace Symfony\Component\Console\Tests\Helper;
  13
+
  14
+use Symfony\Component\Console\Helper\TableHelper;
  15
+use Symfony\Component\Console\Output\StreamOutput;
  16
+
  17
+class TableHelperTest extends \PHPUnit_Framework_TestCase
  18
+{
  19
+    protected $stream;
  20
+
  21
+    protected function setUp()
  22
+    {
  23
+        $this->stream = fopen('php://memory', 'r+');
  24
+    }
  25
+
  26
+    protected function tearDown()
  27
+    {
  28
+        fclose($this->stream);
  29
+        $this->stream = null;
  30
+    }
  31
+
  32
+    /**
  33
+     * @dataProvider testRenderProvider
  34
+     */
  35
+    public function testRender($headers, $rows, $layout, $expected)
  36
+    {
  37
+        $table = new TableHelper();
  38
+        $table
  39
+            ->setHeaders($headers)
  40
+            ->setRows($rows)
  41
+            ->setLayout($layout)
  42
+        ;
  43
+        $table->render($output = $this->getOutputStream());
  44
+
  45
+        $this->assertEquals($expected, $this->getOutputContent($output));
  46
+    }
  47
+
  48
+    /**
  49
+     * @dataProvider testRenderProvider
  50
+     */
  51
+    public function testRenderAddRows($headers, $rows, $layout, $expected)
  52
+    {
  53
+        $table = new TableHelper();
  54
+        $table
  55
+            ->setHeaders($headers)
  56
+            ->addRows($rows)
  57
+            ->setLayout($layout)
  58
+        ;
  59
+        $table->render($output = $this->getOutputStream());
  60
+
  61
+        $this->assertEquals($expected, $this->getOutputContent($output));
  62
+    }
  63
+
  64
+    /**
  65
+     * @dataProvider testRenderProvider
  66
+     */
  67
+    public function testRenderAddRowsOneByOne($headers, $rows, $layout, $expected)
  68
+    {
  69
+        $table = new TableHelper();
  70
+        $table
  71
+            ->setHeaders($headers)
  72
+            ->setLayout($layout)
  73
+        ;
  74
+        foreach ($rows as $row) {
  75
+            $table->addRow($row);
  76
+        }
  77
+        $table->render($output = $this->getOutputStream());
  78
+
  79
+        $this->assertEquals($expected, $this->getOutputContent($output));
  80
+    }
  81
+
  82
+    public function testRenderProvider()
  83
+    {
  84
+        return array(
  85
+            array(
  86
+                array('ISBN', 'Title', 'Author'),
  87
+                array(
  88
+                    array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
  89
+                    array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
  90
+                    array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
  91
+                    array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
  92
+                ),
  93
+                TableHelper::LAYOUT_DEFAULT,
  94
+<<<TABLE
  95
++---------------+--------------------------+------------------+
  96
+| ISBN          | Title                    | Author           |
  97
++---------------+--------------------------+------------------+
  98
+| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
  99
+| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens  |
  100
+| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
  101
+| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
  102
++---------------+--------------------------+------------------+
  103
+
  104
+TABLE
  105
+            ),
  106
+            array(
  107
+                array('ISBN', 'Title', 'Author'),
  108
+                array(
  109
+                    array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
  110
+                    array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
  111
+                    array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
  112
+                    array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
  113
+                ),
  114
+                TableHelper::LAYOUT_BORDERLESS,
  115
+                " =============== ========================== ================== \n  ISBN            Title                      Author            \n =============== ========================== ================== \n  99921-58-10-7   Divine Comedy              Dante Alighieri   \n  9971-5-0210-0   A Tale of Two Cities       Charles Dickens   \n  960-425-059-0   The Lord of the Rings      J. R. R. Tolkien  \n  80-902734-1-6   And Then There Were None   Agatha Christie   \n =============== ========================== ================== \n"
  116
+            ),
  117
+            array(
  118
+                array('ISBN', 'Title'),
  119
+                array(
  120
+                    array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
  121
+                    array('9971-5-0210-0'),
  122
+                    array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
  123
+                    array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
  124
+                ),
  125
+                TableHelper::LAYOUT_DEFAULT,
  126
+<<<TABLE
  127
++---------------+--------------------------+------------------+
  128
+| ISBN          | Title                    |                  |
  129
++---------------+--------------------------+------------------+
  130
+| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
  131
+| 9971-5-0210-0 |                          |                  |
  132
+| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
  133
+| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
  134
++---------------+--------------------------+------------------+
  135
+
  136
+TABLE
  137
+            ),
  138
+            array(
  139
+                array(),
  140
+                array(
  141
+                    array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
  142
+                    array('9971-5-0210-0'),
  143
+                    array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
  144
+                    array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
  145
+                ),
  146
+                TableHelper::LAYOUT_DEFAULT,
  147
+<<<TABLE
  148
++---------------+--------------------------+------------------+
  149
+| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
  150
+| 9971-5-0210-0 |                          |                  |
  151
+| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
  152
+| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
  153
++---------------+--------------------------+------------------+
  154
+
  155
+TABLE
  156
+            ),
  157
+            array(
  158
+                array('ISBN', 'Title'),
  159
+                array(),
  160
+                TableHelper::LAYOUT_DEFAULT,
  161
+<<<TABLE
  162
++------+-------+
  163
+| ISBN | Title |
  164
++------+-------+
  165
+
  166
+TABLE
  167
+            ),
  168
+            array(
  169
+                array(),
  170
+                array(),
  171
+                TableHelper::LAYOUT_DEFAULT,
  172
+                '',
  173
+            ),
  174
+        );
  175
+    }
  176
+
  177
+    protected function getOutputStream()
  178
+    {
  179
+        return new StreamOutput($this->stream);
  180
+    }
  181
+
  182
+    protected function getOutputContent(StreamOutput $output)
  183
+    {
  184
+        rewind($output->getStream());
  185
+
  186
+        return stream_get_contents($output->getStream());
  187
+    }
  188
+}

Git Notes

github-comments

---------------------------------------------------------------------------

by michelsalib at 2012-12-16T00:35:49Z

Looks very useful :+1:

---------------------------------------------------------------------------

by stof at 2012-12-16T20:07:15Z

I don't like this implementation: your helper is stateful, meaning that using it twice in a command can led to weird side-effects as it will still contain some stuff from the previous table you rendered (for instance reusign the custom spearator for the second table)

---------------------------------------------------------------------------

by umpirsky at 2012-12-16T21:39:57Z

@stof Yes, I am aware of that, and I think users should be as well. Other helpers like `ProgressHelper' are no exception. The idea behind this helpers is to be stateful.

For example, if you want to render tables with green borders, you set green border format and they will be green until you change border format. I don't see this as a weird side-effect.

Other then that, I don't see a use case when this is not expected behavior, table style is usually standardized per application. And resetting all values to default is as easy as creating a new `TableHelper` instance.

---------------------------------------------------------------------------

by piotrpasich at 2012-12-21T09:12:03Z

+1 from my side. Nice feature

---------------------------------------------------------------------------

by henrikbjorn at 2012-12-21T09:39:49Z

:+1: could yield some nice debug commands :)

---------------------------------------------------------------------------

by Baachi at 2012-12-21T09:43:41Z

:+1: Really nice!

---------------------------------------------------------------------------

by tyler-king at 2012-12-28T18:29:01Z

+1 this is really useful to have.

---------------------------------------------------------------------------

by Sukei at 2012-12-29T01:36:43Z

+1 really nice feature!

---------------------------------------------------------------------------

by WouterJ at 2013-01-01T23:43:37Z

Isn't this table much to complicated for a great display in terminals? I think it is better to have something like:

    ISBN           Title                     Author
    =============  ========================  ================

    99921-58-10-7  Divine Comedy             Dante Alighieri
    9971-5-0210-0  A Tale of Two Cities      Charles Dickens
    960-425-059-0  The Lord of the Rings     J. R. R. Tolkien
    80-902734-1-6  And Then There Were None  Agatha Christie

---------------------------------------------------------------------------

by umpirsky at 2013-01-01T23:53:29Z

@WouterJ Current implementation is what I find most convenient, and is a default now, you can easily change this with something like:

```php
$table
    ->setEdgeChar(' ')
    ->setHorizontalBorderChar('=')
    ->setVerticalBorderChar(' ')
;
```

If community thinks some other default layout would be better, we can change it ofc.

---------------------------------------------------------------------------

by stloyd at 2013-01-07T13:56:59Z

@fabpot Skipping that small CS _issue_, I this it's good to be merged =)

---------------------------------------------------------------------------

by fabpot at 2013-03-23T16:55:27Z

Looks good to me. @umpirsky Can you take my comments into account, add a note in the CHANGELOG, and submit a PR for the documentation?

---------------------------------------------------------------------------

by umpirsky at 2013-03-23T19:56:29Z

@fabpot Thanks for useful comments, they are [fixed](https://github.com/umpirsky/symfony/commit/5e04c56f295a27c2fa17df73835d60aee5352c19).

Where in changelog this feature belongs, I see it's planned for 2.3?

I will try to submit documentation before the end of next week.

---------------------------------------------------------------------------

by stof at 2013-03-25T23:40:15Z

@umpirsky In the ``2.3.0`` section of the Console changelog.

---------------------------------------------------------------------------

by fabpot at 2013-03-26T08:38:50Z

@umpirsky Thanks for the update. I've reread the comments and I think @WouterJ suggestion is good. So, can you change the default look to what he suggests? Also, what about defining some named configuration so that you can easily use pre-defined templates?

---------------------------------------------------------------------------

by umpirsky at 2013-03-26T08:50:00Z

@fabpot Named configuration is great idea, I will try to fix something nice. Do you really like this borderless table? :)

For example, table layout I used is used by mysql client. It have sense for commands like `doctrine:query:sql` or `doctrine:query:dql`. On the other hand @WouterJ layout can be handy for commands like `container:debug` or `router:debug`.

It would be nice to get more feedback from other people about default table layout.

---------------------------------------------------------------------------

by pilot at 2013-03-26T09:22:38Z

Mysql like table layouts is more convinient, but as @umpirsky say it's depend of commands for which try to use it, so maybe have a sense add a some params which configure table view from console?

Like a `git log --pretty` add a `--style=mysql-table` or `--style=borderless`

---------------------------------------------------------------------------

by fabpot at 2013-03-26T10:03:42Z

I think we won't have an agreement on which default table layout we should use; that's why named configuration is probably needed.

---------------------------------------------------------------------------

by umpirsky at 2013-03-26T10:10:15Z

@fabpot But some configuration must be the default one.

---------------------------------------------------------------------------

by bendavies at 2013-03-26T10:15:45Z

if you are going to follow convention of most CLI tools, you start basic, then enhance from there, `--pretty`, `--verbose` etc...

0 notes on commit 22adfdf

Please sign in to comment.
Something went wrong with that request. Please try again.