Цифровая клавиша команды выбора Laravel

#laravel #symfony #console #command

#laravel #symfony #консоль #команда

Вопрос:

Я пытаюсь использовать функциональность «выбора», которую Laravel / Symfony предоставляет как часть консоли, и у меня возникают проблемы, когда дело доходит до числовых индексов.

Я пытаюсь имитировать поведение элемента выбора HTML в том смысле, что вы показываете строковые значения, но на самом деле возвращаете связанный идентификатор, а не строку.

Пример — К сожалению, $choice всегда является именем, но я хочу идентификатор

 <?php

namespace AppConsoleCommands;

use AppUser;
use IlluminateConsoleCommand;

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'
        ]);
    }
}
  

Обходной путь — если я добавляю префикс к идентификатору пользователя, это работает, но надеялся, что есть другой способ или это просто ограничение библиотеки?

 <?php

namespace AppConsoleCommands;

use AppUser;
use IlluminateConsoleCommand;

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'
        ]);
    }
}
  

Ответ №1:

У меня был тот же вопрос. Я перечисляю объекты в качестве вариантов, с идентификаторами в качестве ключей и метками в качестве значений. Я думал, что это будет чрезвычайно распространенный случай, поэтому был удивлен, обнаружив не так много информации об этом ограничении.

Проблема в том, что консоль решит, использовать ли ключ в качестве значения или нет, исходя из того, является ли $choices массив ассоциативным массивом. Он определяет это, проверяя, есть ли хотя бы один строковый ключ в массиве choices — так что использование одного фиктивного выбора — это одна стратегия.

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

Примечание: Вы не можете преобразовать ключи в string (т. Е. Использовать "1" вместо 1 ), потому что PHP всегда преобразует строковое представление int в true int при использовании в качестве ключа массива.


Работа, которую я принял, заключается в расширении ChoiceQuestion класса и добавлении к нему свойства, $useKeyAsValue , чтобы принудительно использовать ключ в качестве значения, а затем переопределить ChoiceQuestion::isAssoc() метод для соблюдения этого свойства.

 class ChoiceQuestion extends SymfonyComponentConsoleQuestionChoiceQuestion
{
    /**
     * @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);
    }
}
  

Это решение немного рискованно. Предполагается, что Question::isAssoc() это будет использоваться только для определения того, как будет обрабатываться массив вариантов.

Комментарии:

1. Это привело меня в правильном направлении. Реализация isAssoc просто неправильная. Существуют разные правильные реализации, о которых я могу думать проще всего: !is_array(json_decode(json_encode($array))) (проблемы с производительностью? задавая вопрос пользователю .. не делайте этого в пакетном процессе, хотя)

Ответ №2:

У меня была такая же проблема. Кажется, в библиотеке нет опций для этого. Я решил это, объединив индекс или идентификатор со значением в массиве. например

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

И затем получаем часть после ‘-‘, например

 $id = substr( strrchr($choice, '-'), 1);;
  

Комментарии:

1. Это была и моя первая идея, но вместо этого я выбрал идею @MagePalExtensions. Он чувствует себя немного чище.

Ответ №3:

Это может быть или не быть лучшим вариантом, но если вы делаете что-то действительно простое, то:

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

$choice = array_search(
    $this->choice('Choose person', $options),
    $options
);
  

Ответ №4:

другие ответы верны. Проблема в том, что нет управления на основе аргументов, будет ли функция -> ask() возвращать индекс массива или значение.

Но один простой способ — использовать функцию chr() для преобразования между буквами и цифрами… итак, что-то вроде

 $choices[chr($i   97)] = "this is actually option number $i";
$choice_mapper[chr($i   97] = $what_you_really_want[$i];

\later

$choice_letter = $this->choice('Choose',$choices);
$what_i_really_wanted = $choice_mapper[$choice_letter];
  

HTH,

-FT