├── .gitignore
├── src
├── Button.php
├── helpers.php
├── InlineKeyboard
│ ├── InlineKeyboardMarkup.php
│ └── InlineKeyboardButton.php
├── ReplyKeyboardRemove.php
├── ForceReply.php
├── ReplyKeyboard
│ ├── KeyboardButtonPollType.php
│ ├── ReplyKeyboardMarkup.php
│ └── KeyboardButton.php
├── FluentEntity.php
└── KeyboardMarkup.php
├── docs
└── images
│ ├── inlinekeyboard-stack.png
│ ├── inlinekeyboard-multiple-rows.png
│ └── inlinekeyboards-multiline-buttons.png
├── phpunit.xml
├── tests
├── Unit
│ ├── ForceReplyTest.php
│ ├── ReplyKeyboardRemoveTest.php
│ ├── KeyboardButtonPollTypeTest.php
│ ├── KeyboardButtonTest.php
│ ├── InlineKeyboardButtonTest.php
│ ├── InlineKeyboardMarkupTest.php
│ └── ReplyKeyboardMarkupTest.php
└── Pest.php
├── .github
└── workflows
│ └── tests.yaml
├── composer.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 | .phpunit.result.cache
4 | .phpunit.cache
--------------------------------------------------------------------------------
/src/Button.php:
--------------------------------------------------------------------------------
1 | true,
13 | ];
14 |
15 | protected array $defaults = [
16 | 'selective' => true,
17 | ];
18 |
19 | }
--------------------------------------------------------------------------------
/src/ForceReply.php:
--------------------------------------------------------------------------------
1 | true,
14 | ];
15 |
16 | protected array $defaults = [
17 | 'selective' => true,
18 | ];
19 |
20 | }
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests
10 |
11 |
12 |
13 |
14 | ./src
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/ReplyKeyboard/KeyboardButtonPollType.php:
--------------------------------------------------------------------------------
1 | 'quiz'
24 | ]);
25 | }
26 |
27 | public static function regular(): static
28 | {
29 | return new static([
30 | 'type' => 'regular'
31 | ]);
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/tests/Unit/ForceReplyTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([
9 | 'force_reply' => true,
10 | ]);
11 | });
12 |
13 | it('can set known fields', function () {
14 | $keyboard = ForceReply::make()
15 | ->selective();
16 |
17 | expect($keyboard)->toMatchEntity([
18 | 'force_reply' => true,
19 | 'selective' => true,
20 | ]);
21 | });
22 |
23 | it('can set unknown fields', function () {
24 | $keyboard = ForceReply::make()
25 | ->unknownFields('unknown');
26 |
27 | expect($keyboard)->toMatchEntity([
28 | 'force_reply' => true,
29 | 'unknown_fields' => 'unknown'
30 | ]);
31 | });
--------------------------------------------------------------------------------
/src/ReplyKeyboard/ReplyKeyboardMarkup.php:
--------------------------------------------------------------------------------
1 | true,
21 | 'one_time_keyboard' => true,
22 | 'selective' => true,
23 | ];
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | tests:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Validate composer.json and composer.lock
18 | run: composer validate --strict
19 |
20 | - name: Cache Composer packages
21 | id: composer-cache
22 | uses: actions/cache@v2
23 | with:
24 | path: vendor
25 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
26 | restore-keys: |
27 | ${{ runner.os }}-php-
28 |
29 | - name: Install dependencies
30 | run: composer install --prefer-dist --no-progress
31 |
32 | - name: Run test suite
33 | run: composer run-script test
34 |
--------------------------------------------------------------------------------
/tests/Unit/ReplyKeyboardRemoveTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([
9 | 'remove_keyboard' => true
10 | ]);
11 | });
12 |
13 | it('can set known fields', function () {
14 | $keyboard = ReplyKeyboardRemove::make()
15 | ->selective();
16 |
17 | expect($keyboard)->toMatchEntity([
18 | 'remove_keyboard' => true,
19 | 'selective' => true,
20 | ]);
21 | });
22 |
23 | it('can set unknown fields', function () {
24 | $keyboard = ReplyKeyboardRemove::make()
25 | ->unknownFields('unknown');
26 |
27 | expect($keyboard)->toMatchEntity([
28 | 'remove_keyboard' => true,
29 | 'unknown_fields' => 'unknown'
30 | ]);
31 | });
--------------------------------------------------------------------------------
/tests/Unit/KeyboardButtonPollTypeTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([]);
9 | });
10 |
11 | it('creates quiz type', function () {
12 | $button = KeyboardButtonPollType::quiz();
13 |
14 | expect($button)->toMatchEntity([
15 | 'type' => 'quiz'
16 | ]);
17 | });
18 |
19 | it('creates regular type', function () {
20 | $button = KeyboardButtonPollType::regular();
21 |
22 | expect($button)->toMatchEntity([
23 | 'type' => 'regular'
24 | ]);
25 | });
26 |
27 | it('creates unknown type', function () {
28 | $button = new KeyboardButtonPollType([
29 | 'type' => 'unknown',
30 | ]);
31 |
32 | expect($button)->toMatchEntity([
33 | 'type' => 'unknown',
34 | ]);
35 | });
36 |
--------------------------------------------------------------------------------
/src/FluentEntity.php:
--------------------------------------------------------------------------------
1 | data = $data + $this->data;
15 | }
16 |
17 | public static function make(): static
18 | {
19 | return new static;
20 | }
21 |
22 | public function __call($name, $arguments): self
23 | {
24 | $key = snake_case($name);
25 |
26 | $this->data[$key] = $arguments[0] ?? $this->getDefault($key);
27 |
28 | return $this;
29 | }
30 |
31 | public function jsonSerialize(): object
32 | {
33 | return (object) $this->data;
34 | }
35 |
36 | private function getDefault($key): mixed
37 | {
38 | return $this->defaults[$key] ?? null;
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-telegram-bot/fluent-keyboard",
3 | "description": "Fluent Keyboard builder for ReplyKeyboardMarkup and InlineKeyboardMarkup.",
4 | "keywords": ["telegram", "bot", "api", "fluent", "keyboard", "builder"],
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Tii",
10 | "email": "mail@tii.one"
11 | }
12 | ],
13 | "minimum-stability": "stable",
14 | "autoload": {
15 | "files": [
16 | "./src/helpers.php"
17 | ],
18 | "psr-4": {
19 | "PhpTelegramBot\\FluentKeyboard\\": "./src"
20 | }
21 | },
22 | "require": {
23 | "php": "^8.0"
24 | },
25 | "require-dev": {
26 | "pestphp/pest": "^1.21",
27 | "ext-json": "*"
28 | },
29 | "scripts": {
30 | "test": "vendor/bin/pest"
31 | },
32 | "config": {
33 | "allow-plugins": {
34 | "pestphp/pest-plugin": true
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/Unit/KeyboardButtonTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([]);
9 | });
10 |
11 | it('can set known fields', function () {
12 | $button = KeyboardButton::make()
13 | ->text('Text')
14 | ->requestContact()
15 | ->requestLocation()
16 | ->requestPoll();
17 |
18 | expect($button)->toMatchEntity([
19 | 'text' => 'Text',
20 | 'request_contact' => true,
21 | 'request_location' => true,
22 | 'request_poll' => []
23 | ]);
24 | });
25 |
26 | it('can set text via make', function () {
27 | $button = KeyboardButton::make('Text');
28 |
29 | expect($button)->toMatchEntity([
30 | 'text' => 'Text',
31 | ]);
32 | });
33 |
34 | it('can set unknown fields', function () {
35 | $button = KeyboardButton::make()
36 | ->unknownField('Test');
37 |
38 | expect($button)->toMatchEntity([
39 | 'unknown_field' => 'Test',
40 | ]);
41 | });
--------------------------------------------------------------------------------
/src/ReplyKeyboard/KeyboardButton.php:
--------------------------------------------------------------------------------
1 | true,
18 | 'request_location' => true,
19 | 'request_poll' => [],
20 | ];
21 |
22 | public static function make(string $text = null): static
23 | {
24 | $data = [];
25 |
26 | if ($text !== null) {
27 | $data['text'] = $text;
28 | }
29 |
30 | return new static($data);
31 | }
32 |
33 | public function webApp(array|string $web_app): self
34 | {
35 | if (is_string($web_app)) {
36 | $web_app = [
37 | 'url' => $web_app,
38 | ];
39 | }
40 |
41 | $this->data['web_app'] = $web_app;
42 |
43 | return $this;
44 | }
45 | }
--------------------------------------------------------------------------------
/src/InlineKeyboard/InlineKeyboardButton.php:
--------------------------------------------------------------------------------
1 | true,
21 | ];
22 |
23 | public static function make(string $text = null): static
24 | {
25 | $data = [];
26 |
27 | if ($text !== null) {
28 | $data['text'] = $text;
29 | }
30 |
31 | return new static($data);
32 | }
33 |
34 | public function webApp(array|string $web_app): self
35 | {
36 | if (is_string($web_app)) {
37 | $web_app = [
38 | 'url' => $web_app,
39 | ];
40 | }
41 |
42 | $this->data['web_app'] = $web_app;
43 |
44 | return $this;
45 | }
46 |
47 | public function loginUrl(array|string $login_url): self
48 | {
49 | if (is_string($login_url)) {
50 | $login_url = [
51 | 'url' => $login_url
52 | ];
53 | }
54 |
55 | $this->data['login_url'] = $login_url;
56 |
57 | return $this;
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/tests/Pest.php:
--------------------------------------------------------------------------------
1 | in('Feature');
15 |
16 | /*
17 | |--------------------------------------------------------------------------
18 | | Expectations
19 | |--------------------------------------------------------------------------
20 | |
21 | | When you're writing tests, you often need to check that values meet certain conditions. The
22 | | "expect()" function gives you access to a set of "expectations" methods that you can use
23 | | to assert different things. Of course, you may extend the Expectation API at any time.
24 | |
25 | */
26 |
27 | expect()->extend('toMatchEntity', function ($array) {
28 | return expect(json_encode($this->value))->json()->toMatchArray($array);
29 | });
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Functions
34 | |--------------------------------------------------------------------------
35 | |
36 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your
37 | | project that you don't want to repeat in every file. Here you can also expose helpers as
38 | | global functions to help you to reduce the number of lines of code in your test files.
39 | |
40 | */
41 |
42 | //function something()
43 | //{
44 | // // ..
45 | //}
46 |
--------------------------------------------------------------------------------
/tests/Unit/InlineKeyboardButtonTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([
9 |
10 | ]);
11 | });
12 |
13 | it('can set known fields', function () {
14 | $button = InlineKeyboardButton::make()
15 | ->text('Text')
16 | ->url('http://example.com')
17 | ->loginUrl([])
18 | ->callbackData('Callback Data')
19 | ->switchInlineQuery('Switch Inline Query')
20 | ->switchInlineQueryCurrentChat('Switch Inline Query Current Chat')
21 | ->callbackGame('Callback Game')
22 | ->pay();
23 |
24 | expect($button)->toMatchEntity([
25 | 'text' => 'Text',
26 | 'url' => 'http://example.com',
27 | 'login_url' => [],
28 | 'callback_data' => 'Callback Data',
29 | 'switch_inline_query' => 'Switch Inline Query',
30 | 'switch_inline_query_current_chat' => 'Switch Inline Query Current Chat',
31 | 'callback_game' => 'Callback Game',
32 | 'pay' => true,
33 | ]);
34 | });
35 |
36 | it('can set text via make', function () {
37 | $button = InlineKeyboardButton::make('Test');
38 |
39 | expect($button)->toMatchEntity([
40 | 'text' => 'Test',
41 | ]);
42 | });
43 |
44 | it('can set unknown fields', function () {
45 | $button = InlineKeyboardButton::make()
46 | ->unknownField('unknown');
47 |
48 | expect($button)->toMatchEntity([
49 | 'unknown_field' => 'unknown',
50 | ]);
51 | });
--------------------------------------------------------------------------------
/src/KeyboardMarkup.php:
--------------------------------------------------------------------------------
1 | data[static::$keyboardFieldName] = [[]];
23 | }
24 |
25 | /**
26 | * Adds a new row to the keyboard.
27 | *
28 | * @param Button[] $buttons
29 | * @return $this
30 | */
31 | public function row(array $buttons = []): self
32 | {
33 | $keyboard = &$this->data[static::$keyboardFieldName];
34 |
35 | // Last row is not empty, add new row
36 | if (! empty($keyboard[$this->currentRowIndex])) {
37 | $keyboard[] = [];
38 | $this->currentRowIndex++;
39 | }
40 |
41 | // Buttons have been passed, add them
42 | if (! empty($buttons)) {
43 | $keyboard[$this->currentRowIndex] = $buttons;
44 | $this->currentRowIndex++;
45 | }
46 |
47 | return $this;
48 | }
49 |
50 | /**
51 | * Adds buttons to the keyboard, one per row.
52 | *
53 | * @param Button[] $buttons
54 | * @return $this
55 | */
56 | public function stack(array $buttons): self
57 | {
58 | foreach ($buttons as $button) {
59 | $this->row([$button]);
60 | }
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * Adds a button to the last row.
67 | *
68 | * @param Button $button
69 | * @return $this
70 | */
71 | public function button(Button $button): self
72 | {
73 | $keyboard = &$this->data[static::$keyboardFieldName];
74 |
75 | $keyboard[$this->currentRowIndex][] = $button;
76 |
77 | return $this;
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/tests/Unit/InlineKeyboardMarkupTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([
10 | 'inline_keyboard' => [[]]
11 | ]);
12 | });
13 |
14 | it('can set unknown fields', function () {
15 | $keyboard = InlineKeyboardMarkup::make()
16 | ->unknownField('unknown');
17 |
18 | expect($keyboard)->toMatchEntity([
19 | 'unknown_field' => 'unknown',
20 | ]);
21 | });
22 |
23 | it('can have multiple buttons', function () {
24 | $keyboard = InlineKeyboardMarkup::make()
25 | ->button(InlineKeyboardButton::make()->text('Button A'))
26 | ->button(InlineKeyboardButton::make()->text('Button B'))
27 | ->row()
28 | ->button(InlineKeyboardButton::make()->text('Button C'))
29 | ->button(InlineKeyboardButton::make()->text('Button D'));
30 |
31 | expect($keyboard)->toMatchEntity([
32 | 'inline_keyboard' => [
33 | [
34 | ['text' => 'Button A'],
35 | ['text' => 'Button B'],
36 | ], [
37 | ['text' => 'Button C'],
38 | ['text' => 'Button D'],
39 | ]
40 | ]
41 | ]);
42 | });
43 |
44 | it('can have multiple rows', function () {
45 | $keyboard = InlineKeyboardMarkup::make()
46 | ->row([
47 | InlineKeyboardButton::make()->text('Button A'),
48 | InlineKeyboardButton::make()->text('Button B'),
49 | ])->row([
50 | InlineKeyboardButton::make()->text('Button C'),
51 | InlineKeyboardButton::make()->text('Button D'),
52 | ]);
53 |
54 | expect($keyboard)->toMatchEntity([
55 | 'inline_keyboard' => [
56 | [
57 | ['text' => 'Button A'],
58 | ['text' => 'Button B'],
59 | ], [
60 | ['text' => 'Button C'],
61 | ['text' => 'Button D'],
62 | ]
63 | ]
64 | ]);
65 | });
66 |
67 | it('can have multiple stacks', function () {
68 | $keyboard = InlineKeyboardMarkup::make()
69 | ->stack([
70 | InlineKeyboardButton::make()->text('Button A'),
71 | InlineKeyboardButton::make()->text('Button B'),
72 | ])
73 | ->stack([
74 | InlineKeyboardButton::make()->text('Button C'),
75 | InlineKeyboardButton::make()->text('Button D'),
76 | ]);
77 |
78 | expect($keyboard)->toMatchEntity([
79 | 'inline_keyboard' => [
80 | [
81 | ['text' => 'Button A'],
82 | ],
83 | [
84 | ['text' => 'Button B'],
85 | ],
86 | [
87 | ['text' => 'Button C'],
88 | ],
89 | [
90 | ['text' => 'Button D'],
91 | ],
92 | ]
93 | ]);
94 | });
95 |
96 |
97 | it('can mix rows, stacks and buttons', function () {
98 | $keyboard = InlineKeyboardMarkup::make()
99 | ->button(InlineKeyboardButton::make()->text('Button A'))
100 | ->button(InlineKeyboardButton::make()->text('Button B'))
101 | ->row([
102 | InlineKeyboardButton::make()->text('Button C'),
103 | InlineKeyboardButton::make()->text('Button D')
104 | ])
105 | ->button(InlineKeyboardButton::make()->text('Button E'))
106 | ->button(InlineKeyboardButton::make()->text('Button F'))
107 | ->stack([
108 | InlineKeyboardButton::make()->text('Button G'),
109 | InlineKeyboardButton::make()->text('Button H')
110 | ])
111 | ->button(InlineKeyboardButton::make()->text('Button I'))
112 | ->button(InlineKeyboardButton::make()->text('Button J'));
113 |
114 | expect($keyboard)->toMatchEntity([
115 | 'inline_keyboard' => [
116 | [
117 | ['text' => 'Button A'],
118 | ['text' => 'Button B'],
119 | ],
120 | [
121 | ['text' => 'Button C'],
122 | ['text' => 'Button D'],
123 | ],
124 | [
125 | ['text' => 'Button E'],
126 | ['text' => 'Button F'],
127 | ],
128 | [
129 | ['text' => 'Button G'],
130 | ], [
131 | ['text' => 'Button H'],
132 | ],
133 | [
134 | ['text' => 'Button I'],
135 | ['text' => 'Button J'],
136 | ],
137 | ]
138 | ]);
139 | });
--------------------------------------------------------------------------------
/tests/Unit/ReplyKeyboardMarkupTest.php:
--------------------------------------------------------------------------------
1 | toMatchEntity([
10 | 'keyboard' => [[]],
11 | ]);
12 | });
13 |
14 | it('can set known fields', function () {
15 | $keyboard = ReplyKeyboardMarkup::make()
16 | ->inputFieldPlaceholder('Placeholder')
17 | ->oneTimeKeyboard()
18 | ->resizeKeyboard()
19 | ->selective();
20 |
21 | expect($keyboard)->toMatchEntity([
22 | 'keyboard' => [[]],
23 | 'input_field_placeholder' => 'Placeholder',
24 | 'one_time_keyboard' => true,
25 | 'resize_keyboard' => true,
26 | 'selective' => true,
27 | ]);
28 | });
29 |
30 | it('can set unknown fields', function () {
31 | $keyboard = ReplyKeyboardMarkup::make()
32 | ->unknownField('Test');
33 |
34 | expect($keyboard)->toMatchEntity([
35 | 'keyboard' => [[]],
36 | 'unknown_field' => 'Test'
37 | ]);
38 | });
39 |
40 | it('can have multiple buttons', function () {
41 | $keyboard = ReplyKeyboardMarkup::make()
42 | ->button(KeyboardButton::make()->text('Button A'))
43 | ->button(KeyboardButton::make()->text('Button B'))
44 | ->row()
45 | ->button(KeyboardButton::make()->text('Button C'))
46 | ->button(KeyboardButton::make()->text('Button D'));
47 |
48 | expect($keyboard)->toMatchEntity([
49 | 'keyboard' => [
50 | [
51 | ['text' => 'Button A'],
52 | ['text' => 'Button B'],
53 | ], [
54 | ['text' => 'Button C'],
55 | ['text' => 'Button D'],
56 | ]
57 | ]
58 | ]);
59 | });
60 |
61 | it('can have multiple rows', function () {
62 | $keyboard = ReplyKeyboardMarkup::make()
63 | ->row([
64 | KeyboardButton::make()->text('Button A'),
65 | KeyboardButton::make()->text('Button B')
66 | ])->row([
67 | KeyboardButton::make()->text('Button C'),
68 | KeyboardButton::make()->text('Button D')
69 | ]);
70 |
71 | expect($keyboard)->toMatchEntity([
72 | 'keyboard' => [
73 | [
74 | ['text' => 'Button A'],
75 | ['text' => 'Button B'],
76 | ], [
77 | ['text' => 'Button C'],
78 | ['text' => 'Button D'],
79 | ],
80 | ]
81 | ]);
82 | });
83 |
84 | it('can have multiple stacks', function () {
85 | $keyboard = ReplyKeyboardMarkup::make()
86 | ->stack([
87 | KeyboardButton::make()->text('Button A'),
88 | KeyboardButton::make()->text('Button B'),
89 | ])
90 | ->stack([
91 | KeyboardButton::make()->text('Button C'),
92 | KeyboardButton::make()->text('Button D'),
93 | ]);
94 |
95 | expect($keyboard)->toMatchEntity([
96 | 'keyboard' => [
97 | [
98 | ['text' => 'Button A'],
99 | ],
100 | [
101 | ['text' => 'Button B'],
102 | ],
103 | [
104 | ['text' => 'Button C'],
105 | ],
106 | [
107 | ['text' => 'Button D'],
108 | ],
109 | ]
110 | ]);
111 | });
112 |
113 | it('can mix rows, stacks and buttons', function () {
114 | $keyboard = ReplyKeyboardMarkup::make()
115 | ->button(KeyboardButton::make()->text('Button A'))
116 | ->button(KeyboardButton::make()->text('Button B'))
117 | ->row([
118 | KeyboardButton::make()->text('Button C'),
119 | KeyboardButton::make()->text('Button D')
120 | ])
121 | ->button(KeyboardButton::make()->text('Button E'))
122 | ->button(KeyboardButton::make()->text('Button F'))
123 | ->stack([
124 | KeyboardButton::make()->text('Button G'),
125 | KeyboardButton::make()->text('Button H')
126 | ])
127 | ->button(KeyboardButton::make()->text('Button I'))
128 | ->button(KeyboardButton::make()->text('Button J'));
129 |
130 | expect($keyboard)->toMatchEntity([
131 | 'keyboard' => [
132 | [
133 | ['text' => 'Button A'],
134 | ['text' => 'Button B'],
135 | ],
136 | [
137 | ['text' => 'Button C'],
138 | ['text' => 'Button D'],
139 | ],
140 | [
141 | ['text' => 'Button E'],
142 | ['text' => 'Button F'],
143 | ],
144 | [
145 | ['text' => 'Button G'],
146 | ], [
147 | ['text' => 'Button H'],
148 | ],
149 | [
150 | ['text' => 'Button I'],
151 | ['text' => 'Button J'],
152 | ],
153 | ]
154 | ]);
155 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [![Version][version]](https://packagist.org/packages/php-telegram-bot/fluent-keyboard)
4 | ![PHP Version][php-version]
5 | [![Bot API][bot-api-shield]](https://core.telegram.org/bots/api#january-31-2022)
6 | [![Tests][tests-shield]](https://github.com/php-telegram-bot/fluent-keyboard/actions/workflows/tests.yaml)
7 |
8 |
9 |
10 |
11 | Table of Contents
12 |
13 | - Installation
14 | -
15 | Usage
16 |
17 | - Defining a Keyboard
18 | - Defining Buttons
19 | -
20 | Bind Buttons to a Keyboard
21 |
22 | - By Row
23 | - By Button
24 | - As Stack
25 |
26 |
27 | - ForceReply and ReplyKeyboardRemove
28 | - KeyboardButtonPollType
29 |
30 |
31 |
32 |
33 |
34 | ## Installation
35 |
36 | Install the package using composer:
37 |
38 | ```shell
39 | composer require php-telegram-bot/fluent-keyboard
40 | ```
41 |
42 | (back to top)
43 |
44 | ## Usage
45 |
46 | If you need to create a keyboard you can use the classes provided by this package as a drop-in replacement.
47 |
48 | This is best explained with an example:
49 |
50 | ```php
51 | Request::sendMessage([
52 | 'chat_id' => 12345,
53 | 'text' => 'Keyboard Example',
54 | 'reply_markup' => ReplyKeyboardMarkup::make()
55 | ->oneTimeKeyboard()
56 | ->button(KeyboardButton::make('Cancel'))
57 | ->button(KeyboardButton::make('OK')),
58 | ]);
59 | ```
60 |
61 | A ReplyKeyboardMarkup is created by calling the static `make()` method on `ReplyKeyboardMarkup`. After that every field,
62 | like `one_time_keyboard`, can be chained by calling it in camelCase. Buttons can be added by calling
63 | the `button()` method. We have a detailed look on that later.
64 |
65 | The classes and fields are named after the corresponding types and fields of
66 | the [Telegram Bot API](https://core.telegram.org/bots/api).
67 |
68 | (back to top)
69 |
70 | ### Defining a Keyboard
71 |
72 | You can create a keyboard by calling the static `make()` method on its class.
73 |
74 | After that you can chain methods to set additional fields that are available in the Bot API. This is done by calling the
75 | field name in camelCase. So instead of `input_field_placeholder`, you need to call `inputFieldPlaceholder()`.
76 |
77 | ```php
78 | ReplyKeyboardMarkup::make()
79 | ->inputFieldPlaceholder('Placeholder');
80 | ```
81 |
82 | (back to top)
83 |
84 | ### Defining Buttons
85 |
86 | The Buttons are created in the same way:
87 |
88 | ```php
89 | KeyboardButton::make()
90 | ->text('Send my Contact')
91 | ->requestContact();
92 | ```
93 |
94 | As a shortcut, you can pass the mandatory `text` field as an argument to the static method `make()` like this:
95 |
96 | ```php
97 | KeyboardButton::make('Send my Location')
98 | ->requestLocation();
99 | ```
100 |
101 | This is done the same way for `InlineKeyboardButton`:
102 |
103 | ```php
104 | InlineKeyboardButton::make('Login')
105 | ->loginUrl(['url' => 'https://example.com']);
106 | ```
107 |
108 | To find out which fields are available have a look at the [Bot API documentation](https://core.telegram.org/bots/api).
109 |
110 | (back to top)
111 |
112 | ### Bind Buttons to a Keyboard
113 |
114 | The keyboard does not work without any buttons, so you need to pass the buttons to the keyboard. There are a few ways to
115 | do this.
116 |
117 | #### By Row
118 |
119 | ```php
120 | ReplyKeyboardMarkup::make()
121 | ->row([
122 | KeyboardButton::make('Cancel'),
123 | KeyboardButton::make('OK')
124 | ]);
125 | ```
126 |
127 | If you need more than one row, call `row()` multiple times:
128 |
129 | ```php
130 | InlineKeyboardMarkup::make()
131 | ->row([
132 | InlineKeyboardButton::make('1')->callbackData('page-1'),
133 | InlineKeyboardButton::make('2')->callbackData('page-2'),
134 | InlineKeyboardButton::make('3')->callbackData('page-3')
135 | ])
136 | ->row([
137 | InlineKeyboardButton::make('prev')->callbackData('page-prev'),
138 | InlineKeyboardButton::make('next')->callbackData('page-next')
139 | ]);
140 | ```
141 |
142 | 
143 |
144 | #### By Button
145 |
146 | ```php
147 | ReplyKeyboardMarkup::make()
148 | ->button(KeyboardButton::make('First Button'))
149 | ->button(KeyboardButton::make('Second Button'));
150 | ```
151 |
152 | If you need more than one row, just call the row method without arguments, and continue calling `button()`:
153 |
154 | ```php
155 | InlineKeyboardMarkup::make()
156 | ->button(InlineKeyboardButton::make('A')->callbackData('answer-a'))
157 | ->button(InlineKeyboardButton::make('B')->callbackData('answer-b'))
158 | ->row()
159 | ->button(InlineKeyboardButton::make('C')->callbackData('answer-c'))
160 | ->button(InlineKeyboardButton::make('D')->callbackData('answer-d'));
161 | ```
162 | 
163 |
164 | It's up to you if you define your buttons inline like in these examples or if you'd like to generate a whole row beforehand and
165 | pass the variable to the `row()` method.
166 |
167 | #### As Stack
168 |
169 | If you want to add a bunch of buttons that have each a row for themselves you can use the `stack()` method.
170 |
171 | ```php
172 | InlineKeyboardMarkup::make()
173 | ->stack([
174 | InlineKeyboardButton::make('Login')->loginUrl('https://example.com/login'),
175 | InlineKeyboardButton::make('Visit Homepage')->url('https://example.com')
176 | ]);
177 | ```
178 |
179 | 
180 |
181 | **You can mix and match the `row()`, `stack()` and `button()` methods as it fits your needs.**
182 |
183 | (back to top)
184 |
185 | ### ForceReply and ReplyKeyboardRemove
186 |
187 | ForceReply and ReplyKeyboardRemove can be used the same way as a normal keyboard, but they do not receive any buttons:
188 |
189 | ```php
190 | $this->replyToUser('Thank you', [
191 | 'reply_markup' => ReplyKeyboardRemove::make()->selective(),
192 | ]);
193 | ```
194 |
195 | ```php
196 | $data['reply_markup'] = ForceReply::make()->inputFieldPlaceholder('Please type something...');
197 | ```
198 |
199 | (back to top)
200 |
201 | ### KeyboardButtonPollType
202 |
203 | The `request_poll` field is a little special. You can specify which poll type the user can create by passing
204 | a `KeyboardButtonPollType` object.
205 |
206 | ```php
207 | KeyboardButton::make()->requestPoll(KeyboardButtonPollType::regular())
208 | ```
209 |
210 | The `KeyboardButtonPollType` class has static methods for each possible type. But if there are new types in the future
211 | you don't have to wait until we release an update. You can either pass the array structure directly to
212 | the `requestPoll()` method or you pass the array structure to the constructor of `KeyboardButtonPollType`.
213 |
214 | ```php
215 | $pollButton = new KeyboardButtonPollType([
216 | 'type' => 'quiz'
217 | ]);
218 | ```
219 |
220 | [tests-shield]: https://img.shields.io/github/workflow/status/php-telegram-bot/fluent-keyboard/Tests?label=Tests&style=for-the-badge
221 |
222 | [bot-api-shield]: https://img.shields.io/badge/Bot%20API-5.7%20(Jan%202022)-%232a9ed6?style=for-the-badge
223 |
224 | [php-version]: https://img.shields.io/packagist/php-v/php-telegram-bot/fluent-keyboard?style=for-the-badge
225 |
226 | [version]: https://img.shields.io/packagist/v/php-telegram-bot/fluent-keyboard?color=orange&label=Version&style=for-the-badge
227 |
--------------------------------------------------------------------------------