Skip to content
This repository has been archived by the owner on Jul 16, 2021. It is now read-only.

Make it possible to append rows to a rendered table in a console command. #2064

Open
freekmurze opened this issue Feb 10, 2020 · 5 comments
Open

Comments

@freekmurze
Copy link

freekmurze commented Feb 10, 2020

Symfony provides a handy Table console helper that allows rows to be appended to an already rendered table.

Unfortunately, it's currently impossible to use this method in Laravel.

When newing up Symfony\Component\Console\Helper\Table it requires a ConsoleSectionOutput to be passed it.

A fully constructed ConsoleSectionOutput is available when calling section() on Symfony\Component\Console\Output\ConsoleOutputInterface. An object that implements this interface is available on Illuminate\Console\Command as $output. Unfortunately, this property is marked as protected.

In my project, I've just made this property public so I can just reach in and get the section.

// in a command

$section = $this->output->output->section();

$this->table = new Table($section);

$this->table->setHeaders(['Column', 'Another column']);
$this->table->render();

$this->table->appendRow(['Value', 'Another Value']);

I'm not advocating making this property public, but rather for an easy way to be able to use appendRow.

@freekmurze freekmurze changed the title Make it possible to append rows in a table in a console command. Make it possible to append rows to a rendered table in a console command. Feb 10, 2020
@crynobone
Copy link
Member

crynobone commented Feb 11, 2020

<?php

namespace Illuminate\Console;

use Illuminate\Support\Traits\ForwardsCalls;

class OutputStyle 
{
    use ForwardsCalls;

    // ...

    public function __call($method, $parameters) {
        return $this->forwardCallTo($this->output, $method, $parameters);
    }
}

This would allow you to use as long as it being executed inside Illuminate\Console\Command:

$section = $this->output->section();

$this->table = new Table($section);

$this->table->setHeaders(['Column', 'Another column']);
$this->table->render();

$this->table->appendRow(['Value', 'Another Value']);

Unfortunately Illuminate\Console\OutputStyle also have a section method so it can't forward the call automatically.

@adam-prickett
Copy link

<?php

namespace Illuminate\Console;

class OutputStyle extends SymfonyStyle
{
    // ...
+    /**
+     * Get the underlying Symfony output implementation.
+     *
+     * @return \Symfony\Component\Console\Output\OutputInterface
+     */
+    public function getOutput()
+    {
+        return $this->output;
+    }
}

This gives us access to the Symfony\Component\Console\Output\ConsoleOutput instance.

<?php

namespace Illuminate\Console\Concerns;

trait InteractsWithIO
{
    // ...

    /**
     * Format input to textual table.
     *
     * @param  array  $headers
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $rows
     * @param  string  $tableStyle
     * @param  array  $columnStyles
-     * @return void
+     * @return \Symfony\Component\Console\Helper\Table
     */
    public function table($headers, $rows, $tableStyle = 'default', array $columnStyles = [])
    {
-        $table = new Table($this->output);
+        $table = new Table($this->output->getOutput()->section());

        if ($rows instanceof Arrayable) {
            $rows = $rows->toArray();
        }

        $table->setHeaders((array) $headers)->setRows($rows)->setStyle($tableStyle);

        foreach ($columnStyles as $columnIndex => $columnStyle) {
            $table->setColumnStyle($columnIndex, $columnStyle);
        }

        $table->render();
+
+        return $table;
    }

    // ...
}

Now, then creating a table, the Table instance is returned, allowing us to call appendRow()

public function handle()
{
    $table = $this->table(
        ['Column', 'Another column'],
        []
    );

    $table->appendRow(['Value', 'Another Value']);
    $table->appendRow(['Value', 'Another Value']);
}
+--------+----------------+
| Column | Another column |
+--------+----------------+
| Value  | Another Value  |
| Value  | Another Value  |
+--------+----------------+

@freekmurze
Copy link
Author

@adam-prickett You should PR that to the framework. I you won't, I'll do it 😄

@adam-prickett
Copy link

@freekmurze On the way!

@renepardon
Copy link

This ticket is still open and I think for a reason.
grafik

I want to display multiple progress bars and update them individually based on related actions. If I set this aforementioned property to public, it works perfectly. So a getter Method would be really nice instead. Did someone already create a pull request in the Symfony project?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants