skip to Main Content

I have a third-party package method that adds rows to a custom on-screen keyboard like this:

$keyboard = InlineKeyboardMarkup::make();
$keyboard->addRow(
    InlineKeyboardButton::make('Button 1', callback_data: '1'),
    InlineKeyboardButton::make('Button 2', callback_data: '2')
);

I have an array of letters for which I’m trying to create buttons on the on-screen keyboard, but also don’t want a single letter per row, so this is what I tried:

$letters; // ['a', 'b', 'd' ...]

$rowItems = 5;
$currentItem = 0;
$buttons = [];

for ($i = 0; $i < count($letters); $i++) {
  if ($currentItem === $rowItems || $i === (count($letters) - 1)) {
    $keyboard->addRow((object)$buttons);
    $buttons = [];
    $currentItem = 0;
  } else {
    $buttons[] = InlineKeyboardButton::make($letters[$i], callback_data: $letters[$i]);
    $currentItem++;
  }
}

This fails because it says it can’t find an expected property in the InlineKeyboardButton object. How can I make the contents of addRow() the same as in the first example above, but dynamically based on varying inputs?

2

Answers


  1. You have two problems:

    1. $keyboard->addRow((object)$buttons);
      • Given your example code, you don’t want an object, you want to unpack the array.
    2. Your if/else condition means that you can either add a button or a row. You need to add the current row and also add a button to a fresh row. Your existing code would have skipped every 6th [N+1’th] letter.

    You can also skip checking the "are we at the end yet?" condition on every loop and replace it with a "was there anything left over?" check following the loop.

    Also, unless you are absolutely certain that you need a for() loop will at the least work the same with less boilerplate, if not avoiding problems like non-sequential or non-numeric arrays.

    Also, checking $x == $some_bound in a bounded loop works only when $x increments exactly as expected. Checking $x >= $some_bound ensures that there is not accidentally an infinite loop if $x does something unexpected.

    Consolidated into code:

    foreach($letters as $letter) {
      if ($currentItem >= $rowItems) {
        $keyboard->addRow(...$buttons);
        $buttons = [];
        $currentItem = 0;
      }
      $buttons[] = InlineKeyboardButton::make($letter, callback_data: $letter);
      $currentItem++;
    }
    
    if( !empty($buttons) ) {
        $keyboard->addRow(...$buttons);
    }
    
    Login or Signup to reply.
  2. I’d suggest you make use of PHP’s associative array capability. Since you need 2 parameters, this should work very well.

    I managed to locate the telegram-bot class you are using, so I’m reasonably confident this code will work for you.

    Your configuration array would be in this form:

    $keyboardConfig = ['a' => 'a_data', 'b' => 'b_data'];
    

    You can then use array_map to process the array and build your array of buttons. The lambda(anonymous function) will create an array of keyboard button objects that you pass to the addRow method of the keyboard class, providing a solution to your question as posed.

    $keyboardConfig = ['a' => 'a_data', 'b' => 'b_data'];
    
    $buttons = [];
    $buttons[] = array_map(
        function($k, $v) {
            return InlineKeyboardButton::make($k)->callbackData($v);
        }, 
        array_keys($keyboardConfig), 
        array_values($keyboardConfig)
    );
    
    if (count($buttons) > 0) {
       $keyboard = InlineKeyboardMarkup::make();
       $keyboard->addRow($buttons);
    } else {
       // No buttons were created
       // Handle that....
    }
    

    This is similar to sammitch’s answer, only using array_map and a lambda.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search