Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel choice command numeric key

I'm trying to use the "choice" functionality that Laravel/Symfony provide as part of the console and having issues when it comes to numeric indexes.

I'm trying to simulate the behaviour of a HTML select element in the sense that you show string values but actually get back an associated ID and not the string.

Example - Unfortunately $choice is always the name but I want the ID

<?php

namespace App\Console\Commands;

use App\User;
use Illuminate\Console\Command;

class DoSomethingCommand extends Command
{
    protected $signature = 'company:dosomething';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $choice = $this->choice("Choose person", [
            1    =>    'Dave',
            2    =>    'John',
            3    =>    'Roy'
        ]);
    }
}

Workaround - If I prefix the person ID then it works, but was hoping there is another way or is this just a limitation of the library?

<?php

namespace App\Console\Commands;

use App\User;
use Illuminate\Console\Command;

class DoSomethingCommand extends Command
{
    protected $signature = 'company:dosomething';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $choice = $this->choice("Choose person", [
            "partner-1"    =>    'Dave',
            "partner-2"    =>    'John',
            "partner-3"    =>    'Roy'
        ]);
    }
}
like image 850
Carlton Avatar asked Oct 03 '16 09:10

Carlton


3 Answers

I have had the same question. I'm listing entities as choices, with IDs as keys and labels as values. I thought this would be extremely common case, so was surprised to find not much information about this limitation.

The issue is that the console will decide if to use the key as a value or not based on if the $choices array is an associative array. It determines this by checking if there is at least one string key in the choices array -- so throwing in one bogus choice is one strategy.

$choices = [
  1 => 'Dave',
  2 => 'John',
  3 => 'Roy',
  '_' => 'bogus'
];

Note: You can't cast the keys to string (i.e. use "1" instead of 1) because PHP will always cast the string representation of an int to a true int when used as an array key.


The work around I have adopted is to extend the ChoiceQuestion class and add a property to it, $useKeyAsValue, to force if a key to be used as a value, and then override the ChoiceQuestion::isAssoc() method to honour this property.

class ChoiceQuestion extends \Symfony\Component\Console\Question\ChoiceQuestion
{
    /**
     * @var bool|null
     */
    private $useKeyAsValue;

    public function __construct($question, array $choices, $useKeyAsValue = null, $default = null)
    {
        $this->useKeyAsValue = $useKeyAsValue;
        parent::__construct($question, $choices, $default);
    }

    protected function isAssoc($array)
    {
        return $this->useKeyAsValue !== null ? (bool)$this->useKeyAsValue : parent::isAssoc($array);
    }
}

This solution is a little risky. It assumes that Question::isAssoc() will only ever be used to determine how the array of choices will be handled.

like image 74
Courtney Miles Avatar answered Nov 16 '22 23:11

Courtney Miles


I had the same issue. Seems there is no options for this in the library. I solved it by concatenating the index or id with the value in the array. e.g.

$choices = [
    1 => 'Dave-1',
    2 => 'John-2',
    3 => 'Roy-3'
];
$choice = $this->choice('Choose',$choices);

And then getting the part after '-' like

$id = substr( strrchr($choice, '-'), 1);;
like image 5
user3929745 Avatar answered Nov 16 '22 22:11

user3929745


This may or may not the best option, but if you are doing something really simple then:

$options = [
    1 => 'Dave',
    2 => 'John',
    3 => 'Roy',
];

$choice = array_search(
    $this->choice('Choose person', $options),
    $options
);
like image 4
Renon Stewart Avatar answered Nov 16 '22 22:11

Renon Stewart