From ba716fdbfbdd3e1e6b4cceae4a5dfb3dbe952c75 Mon Sep 17 00:00:00 2001 From: CinPer Date: Mon, 17 Aug 2020 11:55:28 +0000 Subject: [PATCH 1/6] Change repo links from prinx github to rejoice organisation github --- README.md | 2 +- current/installation.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8eac3c4..80dd03d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You can browse the documentation on the [Rejoice docs site](https://prinx.github ## Rejoice framework -Retrieve the Rejoice framework [here](https://github.com/prinx/rejoice). +Retrieve the Rejoice framework [here](https://github.com/rejoice-framework/rejoice). ## Contributing diff --git a/current/installation.md b/current/installation.md index a85eb1c..9221acb 100644 --- a/current/installation.md +++ b/current/installation.md @@ -18,7 +18,7 @@ To install Rejoice and craft your application, you need `composer` installed on Then, create a Rejoice project by running in the console: ```php -composer create-project --prefer-dist prinx/rejoice my-ussd-app +composer create-project --prefer-dist rejoice/rejoice my-ussd-app ``` This will create a project in the `my-ussd-app` directory. From 99ac94563caa0ab18ce2518a9d67fe571c4fcb33 Mon Sep 17 00:00:00 2001 From: CinPer Date: Mon, 17 Aug 2020 12:39:51 +0000 Subject: [PATCH 2/6] Fix baseUrl in _config.yml --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 10d82ff..199d445 100644 --- a/_config.yml +++ b/_config.yml @@ -2,7 +2,7 @@ title: Rejoice documentation email: princedorcis@gmail.com description: >- # this means to ignore newlines until "baseurl:" The Rejoice USSD Framework documentation -baseurl: "/rejoice-docs" # the subpath of your site, e.g. /blog +baseurl: "/docs" # the subpath of your site, e.g. /blog url: "" # the base hostname & protocol for your site, e.g. http://example.com twitter_username: DorcisPrince github_username: prinx From a4ce85daf954bbe0daa566f53ace7931037ba276 Mon Sep 17 00:00:00 2001 From: CinPer Date: Mon, 17 Aug 2020 12:41:50 +0000 Subject: [PATCH 3/6] Point rejoice docs site links to rejoice organisation repos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80dd03d..f24a608 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Rejoice building your USSD applications # Rejoice Docs This is the official documentation of the rejoice framework. -You can browse the documentation on the [Rejoice docs site](https://prinx.github.io/rejoice-docs). +You can browse the documentation on the [Rejoice docs site](https://rejoice-framework.github.io/docs). ## Rejoice framework From b1b161702adfabb1a10c7c295160db3c1db31f6a Mon Sep 17 00:00:00 2001 From: CinPer Date: Wed, 23 Sep 2020 15:31:27 +0000 Subject: [PATCH 4/6] Upgrade kramdown --- Gemfile | 3 +++ Gemfile.lock | 6 +++--- current/database.md | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bdcd550..8f1127c 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,9 @@ source "https://rubygems.org" # gem "jekyll", "~> 4.1.1" # This is the default theme for new Jekyll sites. You may change this to anything you like. # gem "minima", "~> 2.5" + + +gem "kramdown", ">= 2.3.0" gem "just-the-docs" # If you want to use GitHub Pages, remove the "gem "jekyll"" above and # uncomment the line below. To upgrade, run `bundle update github-pages`. diff --git a/Gemfile.lock b/Gemfile.lock index eb2998b..db5f358 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,7 +67,7 @@ GEM jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) jemoji (= 0.11.1) - kramdown (= 1.17.0) + kramdown (>= 2.3.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) @@ -93,7 +93,7 @@ GEM i18n (~> 0.7) jekyll-sass-converter (~> 1.0) jekyll-watch (~> 2.0) - kramdown (~> 1.14) + kramdown (~> 2.3) liquid (~> 4.0) mercenary (~> 0.3.3) pathutil (~> 0.9) @@ -195,7 +195,7 @@ GEM jekyll (>= 3.8.5) jekyll-seo-tag (~> 2.0) rake (>= 12.3.1, < 13.1.0) - kramdown (1.17.0) + kramdown (>= 2.3.0) liquid (4.0.3) listen (3.2.1) rb-fsevent (~> 0.10, >= 0.10.3) diff --git a/current/database.md b/current/database.md index ed86301..1b32173 100644 --- a/current/database.md +++ b/current/database.md @@ -78,7 +78,7 @@ public function retrieveUser() $statement = $this->db()->prepare("SELECT * FROM users WHERE id = :user_id"); $statement->execute(['user_id' => $id]); - $result = $statement->fetch(PDO::FETCH_ASSOC); + $result = $statement->fetch(\PDO::FETCH_ASSOC); $statement->closeCursor(); return $result; From 818a19ca604f9e0a6f78939136c6ff9caf3648ae Mon Sep 17 00:00:00 2001 From: CinPer Date: Mon, 26 Oct 2020 02:18:16 +0000 Subject: [PATCH 5/6] v0.0.7 --- README.md | 2 + current/commands.md | 61 ++++- current/configurations.md | 2 +- current/console.md | 56 ++++- current/contributions.md | 26 +- current/database.md | 360 ++++++++++++++++++++++------ current/deployment.md | 2 + current/first-application.md | 27 ++- current/installation.md | 25 +- current/logging.md | 4 +- current/menu/actions.md | 129 ++++++---- current/menu/index.md | 2 +- current/menu/menu-class.md | 252 +++++++++++-------- current/menu/menu-resource.md | 12 +- current/menu/pagination.md | 237 ++++++++++-------- current/menu/validation.md | 39 +-- current/request.md | 13 +- current/samples/e-commerce.markdown | 20 +- current/samples/index.md | 2 +- current/samples/multi-flow.markdown | 3 +- current/samples/one-flow.markdown | 3 +- current/session.md | 32 ++- current/sms.md | 5 +- current/user-response.md | 22 +- index.md | 13 +- 25 files changed, 924 insertions(+), 425 deletions(-) diff --git a/README.md b/README.md index f24a608..fdaa8e3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Rejoice building your USSD applications # Rejoice Docs + This is the official documentation of the rejoice framework. You can browse the documentation on the [Rejoice docs site](https://rejoice-framework.github.io/docs). @@ -19,4 +20,5 @@ Retrieve the Rejoice framework [here](https://github.com/rejoice-framework/rejoi Your contribution to Rejoice is very much appreciated. ## License + [MIT License](LICENSE) diff --git a/current/commands.md b/current/commands.md index 4d11c54..3cfbd17 100644 --- a/current/commands.md +++ b/current/commands.md @@ -31,14 +31,17 @@ nav_order: 140 - [Going further](#going-further) ## Creating a command + ```php -php smile command:new MyCommand +php smile make:command MyCommand ``` Will create a new command in the `app/Console/Commands`{:.php} folder. ## Configuring the command + It helps you set the name of the command, the help message associated, the arguments the command takes, the options the command takes. + ```php public function configure() { @@ -70,20 +73,25 @@ public function configure() ); } ``` + The `addArgument` method takes three parameters: -* The name of the argument, -* A constant specifying if the argument is required or not: - * `Argument::OPTIONAL`, - * `Argument::REQUIRED` -* and a description of the variable that will be display aside the argument in the help message. + +- The name of the argument, +- A constant specifying if the argument is required or not: + - `Argument::OPTIONAL`, + - `Argument::REQUIRED` +- and a description of the variable that will be display aside the argument in the help message. You need to import the class: + ```php -use Prinx\Rejoice\Console\Argument; +use Rejoice\Console\Argument; ``` + You can add as many arguments as you want. ### Adding options + ```php public function configure() { @@ -104,21 +112,26 @@ public function configure() ); } ``` + The `addOption` method takes four parameters. + - The name of the option - A one-letter shortcut for the option - A constant specifying if the option is required or not. The possible values are: - `Option::OPTIONAL` - - `Option::REQUIRED` + - `Option::REQUIRED` - And the default value of the option. You need to import the class: + ```php -use Prinx\Rejoice\Console\Option; +use Rejoice\Console\Option; ``` + You can add as many options as you want. ## The fire method + ```php public function fire() { @@ -140,18 +153,23 @@ $this->write('Cool'); // end with a new line $this->writeln('Cool'); ``` + The variable in writeln can be an array of string. Each string will be displayed on a new line. + ```php $this->writeln(['Hi Prince!', 'This is amazing.']); ``` ## Display in color + ```php $this->writeWithColor('Cool', 'red', 'cyan'); ``` + The first color is the foreground color. The second, the background color. Both are optional. The default is white for the foreground, black for the background. ### Supported colors + - white - black - red @@ -160,8 +178,9 @@ The first color is the foreground color. The second, the background color. Both - blue - magenta - cyan - + ### Get a colorized string + If you don't want to write directly but rather want to get the colorized string in a variable, you can use the `colorize` method. ```php @@ -169,7 +188,9 @@ $greetings = $this->colorize('Cool', 'red', 'cyan'); ``` ## Colors helper methods + ### Display a green text: + ```php $this->info('Cool'); $this->success('Cool'); @@ -179,6 +200,7 @@ $this->writeWithColor('Cool', 'green', 'black'); ``` ### Error text + ```php $this->error('Uhhh'); @@ -187,6 +209,7 @@ $this->writeWithColor('Uhhh', 'white', 'red'); ``` ### Question + ```php $this->question('Why?'); @@ -195,7 +218,9 @@ $this->writeWithColor('Why?', 'black', 'cyan'); ``` ## Tables + ## Create a table + ```php $table = $this->table() ->body([ @@ -205,7 +230,9 @@ $table = $this->table() ]) ->show(); // Display the table ``` + Or define the rows one by one: + ```php $table = $this->table() ->addRow([ 1, 'Edna', 22 ]) @@ -232,6 +259,7 @@ $table = $this->table() ### Separation line By default the rows are not separated by a line. To show a separtion line use the `tableLine` method. + ```php $table = $this->table() ->head(['ID', 'Name', 'Age']) @@ -245,12 +273,15 @@ $table = $this->table() ``` ### Table header and footer titles + ```php $table->headTitle('Users') $table->footTitle('Page 1/2') $table->show(); ``` + ### Control the border style + ```php // Default $table->border('default') @@ -269,24 +300,30 @@ $table->border('all-double') $table->show(); ``` + ## Ask question + ```php $filename = $this->ask('What is the file name?'); ``` + The `ask` method returns the response given by the user. ## Ask for confirmation + ```php if ($this->confirm('Do you want to overwrite this file?', 'no');) { // } ``` + Takes as first argument, the question and as second argument the default response. The default response must be one of: `y`, `yes`, `n`, `no`. ## The input object The input of the user is represented by an object that Smile uses under the hood to retrieve the arguments and options provided by the user. For advanced manipulation of the command, you may need to use this object. You can retrieve it by: + ```php $input = $this->getInput(); ``` @@ -294,9 +331,11 @@ $input = $this->getInput(); ## The output object The output object controls what the command will display and how it will be displayed. It is the object use under the hood when calling a method like `writeln`. For advanced manipulation of the command, you may need to use this object. You can retrieve it by: + ```php $output = $this->getOutput(); ``` ## Going further -You customize more your command by visiting the awesome Symfony Console documentation. Smile is fully based on Symfony Console. \ No newline at end of file + +You customize more your command by visiting the awesome Symfony Console documentation. Smile is fully based on Symfony Console. diff --git a/current/configurations.md b/current/configurations.md index 77ec336..e53d556 100644 --- a/current/configurations.md +++ b/current/configurations.md @@ -23,4 +23,4 @@ $this->config('menu.default_end_message'); ``` For multidimentional arrays, use the same format, separating the indexes by a dot. -{: .note .note-info } \ No newline at end of file +{: .note .note-info } diff --git a/current/console.md b/current/console.md index b96338e..42b30dc 100644 --- a/current/console.md +++ b/current/console.md @@ -7,52 +7,82 @@ nav_order: 130

Console commands

- [Console commands](#console-commands) +- [Devtool](#devtool) - [Running the simulator](#running-the-simulator) - - [Console simulator](#console-simulator) - [Web simulator](#web-simulator) + - [Console simulator](#console-simulator) - [Create a new menu](#create-a-new-menu) - [Directly in the Menus folder](#directly-in-the-menus-folder) - [In a Menu sub-folder](#in-a-menu-sub-folder) - [Other console commands](#other-console-commands) - [Help on a command](#help-on-a-command) - ## Console commands + Rejoice ships with useful console features to allow you test quickly your application and other more. The console application in Rejoice is name `smile`. +## Devtool + +Rejoice provides a very handful devtool to interact with your application. You can access the devtool by running in a console (opened in the project root): + +```php +php smile devtool +``` + +You can run any valid PHP code inside the devtool and also use the classes of your application. + ## Running the simulator + You have access to two simulators, one in console, the other as a web interface. You can provide the configuration of the simulator in the .env file: -### Console simulator +### Web simulator Run the console simulator by running the command + ```php -php smile simulator:console +php smile serve ``` -You can use the shortcut `php smile sim:con` -> Note: You can use shortcuts for any simulator command, provided it does not conflict with any other command. -### Web simulator +or -Run the console simulator by running the command ```php php smile simulator:web ``` +### Console simulator + +Run the console simulator by running the command + +```php +php smile serve -c console +``` + +or + +```php +php smile simulator:console +``` + +You can use the shortcut `php smile sim:con` +> Note: You can use shortcuts for any simulator command, provided it does not conflict with any other command. + ## Create a new menu + ### Directly in the Menus folder + ```php -php smile menu:new MyMenu +php smile make:menu MyMenu ``` Will create the class `MyMenu` in a newly created `app/Menus/MyMenu.php`. ### In a Menu sub-folder + ```php -php smile menu:new FirstFlow/MyMenu +php smile make:menu FirstFlow/MyMenu ``` Will create the class `MyMenu` in a newly created `app/Menus/FirstFlow/MyMenu.php`. @@ -61,13 +91,15 @@ If the sub-folder does not exist, it will be created. ## Other console commands You can retrieve any other console command by running: + ```php -php smile +php smile list ``` ## Help on a command To read the help of a command, run: + ```php php smile help name_of_the_command -``` \ No newline at end of file +``` diff --git a/current/contributions.md b/current/contributions.md index 0d24c17..0ee881e 100644 --- a/current/contributions.md +++ b/current/contributions.md @@ -18,18 +18,20 @@ If you have discover a security vulnerability inside Rejoice, please DO NOT open ## Support and general questions If you need help or have a general question about rejoice, you can post it on: -* [Stack Overflow], -* [Discord](https://discord.gg/92JVJUf). + +- [Stack Overflow], +- [Discord](https://discord.gg/92JVJUf). The issue tracker is only for bugs and feature requests. ## Code of conduct + The Rejoice code of conduct is derived from the Laravel framework code of conduct. By participating, you are expected to uphold this code. Any violations of the code of conduct may be reported to Prince Dorcis (princedorcis@gmail.com): -* Participants will be tolerant of opposing views. -* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. -* When interpreting the words and actions of others, participants should always assume good intentions. -* Behavior that can be reasonably considered harassment will not be tolerated. +- Participants will be tolerant of opposing views. +- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. +- When interpreting the words and actions of others, participants should always assume good intentions. +- Behavior that can be reasonably considered harassment will not be tolerated. ## Fork & create a branch @@ -95,10 +97,10 @@ git push --force-with-lease 732-fix-default-menu-not-used A PR can only be merged into master by a maintainer if: -* It is passing CI. -* It has been approved by a maintainer. -* It has no requested changes. -* It is up to date with current master. +- It is passing CI. +- It has been approved by a maintainer. +- It has no requested changes. +- It is up to date with current master. Any maintainer is allowed to merge a PR if all of these conditions are met. @@ -107,8 +109,8 @@ met. Maintainers need to do the following to push out a release: -* Switch to the master branch and make sure it's up to date. -* Review and merge the PR. The generated changelog in the PR should include all user visible changes you intend to ship. +- Switch to the master branch and make sure it's up to date. +- Review and merge the PR. The generated changelog in the PR should include all user visible changes you intend to ship. [Stack Overflow]: http://stackoverflow.com/questions/tagged/rejoice [new issue]: https://github.com/rejoice/rejoice/issues/new diff --git a/current/database.md b/current/database.md index 1b32173..de15250 100644 --- a/current/database.md +++ b/current/database.md @@ -6,163 +6,389 @@ nav_order: 70

Connecting to the database

-## Configuring databases -Application database connections in Rejoice are configured in the `config/database` folder. +## Connections + +Application database connections in Rejoice are configured in the `config/database.php` file. You can define as many database connections as you want by giving each of them a name. There is a default database configured by default. -This is an example of configuration: +You will define the environment variables in the `.env` file in the root directory. + +
+We do not recommend specifying the database credentials directly in the config files. +
+ +## Creating Models + +Rejoice ships with the Eloquent ORM provided by the wonderful `illuminate/database` package which allows to create Models reflecting the tables in our database. + +A model can be created using the commands: + ```php -// config/database.php +php smile make:model Country +``` -return [ +This command will create a class named `Country` in a newly created `app/Models/Country.php` file. - 'default' => [ - 'user' => env('APP_DEFAULT_DB_USER', ''), - 'password' => env('APP_DEFAULT_DB_PASS', ''), - 'host' => env('APP_DEFAULT_DB_HOST', ''), - 'port' => env('APP_DEFAULT_DB_PORT', ''), - 'dbname' => env('APP_DEFAULT_DB_NAME', ''), - ], +This model is then automatically mapped to the `countries` table in the database. If the countries table has another name (for example `all_countries`), you can simply specify that name in the class: - 'log_db' => [ - 'user' => env('LOG_DB_USER', ''), - 'password' => env('LOG_DB_PASS', ''), - 'host' => env('LOG_DB_HOST', ''), - 'port' => env('LOG_DB_PORT', ''), - 'dbname' => env('LOG_DB_NAME', ''), - ] +```php + -We do not recommend specifying the database credentials directly in the config files. - +```php +public function before() +{ + $name = $this->previousResponses('ChooseCountry'); + $countryCode = Country::where('name', '=', $name)->value('code'); +} +``` + +You can use all the wonderful features that come with the Eloquent ORM, like relationships, collections, mutators, etc. Learn more about Eloquent [here](laravel.com/docs/8.x/eloquent#eloquent-model-conventions). + +## Query Builder + +Together with the Eloquent ORM, you have access to the query builder simply by calling the `Rejoice/Database/DB` class. + +You can then use it like: + +```php +public function before() +{ + $name = $this->previousResponses('ChooseCountry'); + $countryCode = DB::table('all_countries')->where('name', '=', $name)->value('code'); +} +``` + +Learn more about Query Builder [here](https://laravel.com/docs/8.x/queries). -## Using a database connection -Using a database connection in a menu class is as easy as calling the `db` method. +## Using directly the PDO connection + +Getting the PDO connection is as easy as calling the `db` method. The db method takes one parameter which is the name of the connection you want to use. You can also call the `db` method without parameter. The `default` connection will be automatically used. The `db` method returns a `PDO` connection to the database. So you will use it exactly how a PDO connection is used, and using a plain query. These are some examples: -### Inserting to the database +## Inserting to the database + +### Using Eloquent + +Assuming we have created a `User` Model: + +```php +userPreviousResponses('enter_username'); + $firstName = $this->previousResponses('EnterFirstName'); + $lastName = $this->previousResponses('EnterLastName'); - $statement = $this->db()->prepare("INSERT INTO users (name) VALUES (?)"); - $statement->execute([$name]); - $statement->closeCursor(); + $user = new User; + $user->first_name = $firstName; + $user->last_name = $lastName; + $user->save(); +} +``` - $logStatement = $this->db('log_db')->prepare("INSERT INTO logs (log_text) VALUES (?)"); - $logStatement->execute(["User {$name} has been created"]); - $logStatement->closeCursor(); +We can do the same via mass-assignement by defining the attributes that will be mass-assignable in the `fillable` property of the model: + +```php +previousResponses('EnterFirstName'); + $lastName = $this->previousResponses('EnterLastName'); + + $user = User::create([ + 'first_name' => $firstName, + 'last_name' => $lastName, + ]); +} +``` -### Retrieving from the database +### Using Query Builder ```php -public function retrieveUser() +public function before() { - $id = $this->userPreviousResponses('choose_user'); + $firstName = $this->previousResponses('EnterFirstName'); + $lastName = $this->previousResponses('EnterLastName'); + + DB::table('users')->insert([ + 'first_name' => $firstName, + 'last_name' => $lastName, + ]); +} +``` - $statement = $this->db()->prepare("SELECT * FROM users WHERE id = :user_id"); - $statement->execute(['user_id' => $id]); +### Using PDO connection - $result = $statement->fetch(\PDO::FETCH_ASSOC); +```php +public function before() +{ + $firstName = $this->previousResponses('EnterFirstName'); + $lastName = $this->previousResponses('EnterLastName'); + + $statement = $this->db()->prepare("INSERT INTO users (first_name, last_name) VALUES (:first_name, :last_name)"); + $statement->execute([ + 'first_name' => $firstName, + 'last_name' => $lastName, + ]); $statement->closeCursor(); +} +``` + +## Retrieving from the database + +### Using Eloquent + +```php +public function message() +{ + $id = $this->previousResponses('choose_user'); + + $user = User::find($id); + + $message = $user ? "Hello {$user->first_name}" : "User not found"; - return $result; + return $message; } +``` + +### Using Query Builder +```php public function message() { - $user = $this->retrieveUser(); - $message = $user ? "Hello {$user['name']}" : "User not found"; - + $id = $this->previousResponses('choose_user'); + + $user = DB::table('users')->where('id', '=', $id)->get(); + + $message = $user ? "Hello {$user->first_name}" : "User not found"; + return $message; } ``` -Here we used another similar PDO syntax to prepare the query. Notice how the `user_id` is named in the query and referenced in the `execute` method. This is just to show this syntax. Having only one variable (`user_id`), using the question mark syntax would have been easier. We will prefer the `named` variable syntax when we have more variables. -### Updating the database +### Using PDO connection + +```php +public function message() +{ + $id = $this->previousResponses('choose_user'); + + $statement = $this->db()->prepare("SELECT * FROM users WHERE id = ?"); + $statement->execute([$id]); + + $user = $statement->fetch(\PDO::FETCH_ASSOC); + + $statement->closeCursor(); + + $message = $user ? "Hello {$user['first_name']}" : "User not found"; + + return $message; +} +``` + +## Updating the database + +### Using Eloquent + +```php +public function before() +{ + $id = $this->previousResponses('choose_user'); + + User::where('id', $id)->update(['active' => 0]); +} +``` + +### Using Query Builder + +```php +public function before() +{ + $id = $this->previousResponses('choose_user'); + + DB::table('users')->where('id', $id)->update(['active' => 0]); +} +``` + +### Using PDO connection ```php public function before() { - $name = $this->userPreviousResponses('edit_username'); + $id = $this->previousResponses('choose_user'); - $statement = $this->db()->prepare("UPDATE users SET name = ?"); - $statement->execute([$name]); + $statement = $this->db()->prepare("UPDATE users SET active = 0 WHERE id = ?"); + $statement->execute([$id]); $statement->closeCursor(); } ``` -### Deleting from the database + +## Deleting from the database + +### Using Eloquent ```php public function before() { - $name = $this->userPreviousResponses('edit_username'); + $id = $this->previousResponses('choose_user'); - $statement = $this->db()->prepare("DELETE * FROM users WHERE name = ?"); - $statement->execute([$name]); + User::where('id', $id)->delete(); +} +``` + +### Using Query Builder + +```php +public function before() +{ + $id = $this->previousResponses('choose_user'); + + DB::table('users')->where('id', $id)->delete(); +} +``` + +### Using PDO connection + +```php +public function before() +{ + $id = $this->previousResponses('choose_user'); + + $statement = $this->db()->prepare("DELETE * FROM users WHERE id = ?"); + $statement->execute([$id]); $statement->closeCursor(); } ``` -### Transactions +## Transactions + +### Using the `transaction` method + +The `DB` class provides a handful `transaction` method to manage transactions. + +```php +public function before() +{ + DB::transaction(function () { + // First query + // Second query + // ... + }); +} +``` + +Committing or rolling back are automatically handled by the `transaction` method. + +The `transaction` method accepts a second argument that is the number of times the transaction will be retried if it failed. ```php public function before() { - $name = $this->userPreviousResponses('enter_username'); + $retry = 3; + + DB::transaction(function () { + // First query + // Second query + // ... + }, $retry); +} +``` + +An exception will be thrown if all the attempts are exhausted. + +### Manually handling committing and rolling back +If for any reason you want to manually handle committing and rolling back, you can easily do it: + +```php +public function before() +{ $this->db()->beginTransaction(); try { - $statement = $this->db()->prepare("INSERT INTO users (name) VALUES (?)"); - $statement->execute([$name]); - $statement->closeCursor(); + // First query + // Second query + // ... $this->db()->commit(); - $this->respond("Your information has been successfully saved."); + + $this->respond("Operation successful."); } catch (\Throwable $th) { - $this->db()->rollBack(); + $this->db()->rollBack(); + $this->respond("An error happened."); } } ``` +
+Instead of `$this->db()->beginTransaction()`, you can also use `DB::beginTransaction()`. Same for `$this->db()->commit()` and `$this->db()->rollBack()`. +
+ We are using the `respond` method to send the response to the user. This implies this screen will be the last screen. If you want to do the same thing on a screen that is not the last screen, you can do: + ```php protected $nameSavedSuccessfuly = false; public function before() { - $name = $this->userPreviousResponses('enter_username'); - $this->db()->beginTransaction(); try { - $statement = $this->db()->prepare("INSERT INTO users (name) VALUES (?)"); - $statement->execute([$name]); - $statement->closeCursor(); + // First query + // Second query + // ... $this->db()->commit(); $this->nameSavedSuccessfuly = true; } catch (\Throwable $th) { - $this->db()->rollBack(); + $this->db()->rollBack(); } } diff --git a/current/deployment.md b/current/deployment.md index 697c32e..6f409ff 100644 --- a/current/deployment.md +++ b/current/deployment.md @@ -14,6 +14,7 @@ nav_order: 30 The USSD protocol is highly linked to telecommunication environment of each country. Each request passes through a mobile operator before reaching the application. Hence, you (or your organization) must be in partnership with at least one (ideally all) the mobile operator of your country to be able to process a USSD request. ## USSD request parameters + If you are already in a partnership with a mobile operator, they have surely given you some requests paramters you should wait for from them and the ones you should send to them on each request. This can differ from country to country. The default USSD parameters have been tailored to Ghana. But Rejoice lets you quickly change the parameters if they are different from from what your mobile operators provide. This can be changed in the .env file: ```ini @@ -51,4 +52,5 @@ The user's phone number. The network MNC of the user's mobile operator. ## You don't do USSD like that? + If the way you do USSD in your country is still completely different from what Rejoice provides, kindly create a pull request or just send an email to princedorcis@gmail.com explaining how you do it at your side. We are ready to integrate it to Rejoice :) \ No newline at end of file diff --git a/current/first-application.md b/current/first-application.md index 76e86f7..60463cd 100644 --- a/current/first-application.md +++ b/current/first-application.md @@ -17,12 +17,14 @@ nav_order: 20 - [Going further](#going-further) ## USSD menus + A USSD application consists of Menus also known as Screens, that follow each other and process the user's interaction in a USSD session. Building a USSD application will be actually implementing this interactions with the user. Rejoice allows you to build the USSD menus and implement how the follow each other in a simple a beautiful way. It allows you to have full control on the user's interaction, the user's answer to each menu. Let's see how to create our USSD menus. All your menus will be in the app/Menus folder (or sub-folders). ## The default menus + When you open the `app/Menus` folder, You will notice that there is already a `Menu.php` and a `Welcome.php` files in it. Those two are the minimum for every application. The `Menu.php` contains the `Menu` class which is the base menu class that all your menus will extend. @@ -46,6 +48,7 @@ The `actions` methods defines the different available actions on the menu. For e The action method returns an array of actions called the `action bag`. Create the action method like this: + ```php public function actions() { @@ -57,9 +60,11 @@ public function actions() ]; } ``` + As you can see an action bag is just a plain PHP array associating the expected user inputs to the next menus to call. Here the action bag contains only one action associating the user input `1` to the menu `enter_username`. The `display` property defines the explanatory text the user will see associated to the input. Our welcome menu looks like: + ```php namespace App\Menus; @@ -107,14 +112,14 @@ In the actions on the welcome menu, we defined that, if the user chooses `1` we Open the console in the project root folder. Run the command: ```php -php smile menu:new EnterUsername +php smile make:menu EnterUsername ``` This command will create a simple menu class in a newly created file `app/Menus/EnterUsername.php`. You can add the flag `-x` to the command to create the menu without the comments around the method. -Run `php smile help menu:new` to see all the available flags that can help you modify the menu you are creating. +Run `php smile help make:menu` to see all the available flags that can help you modify the menu you are creating. {: .note .note-info } You will notice that the name of the menu class is in `PascalCase` when the name in the action bag is in `snake_case`. This is intentional and just to show that Rejoice can automatically guess the name of the class from it. In a real application you may use the true name of the menu directly 'EnterUsername'. Note that `enter-username`, or even `enter username` in the action bag will also produce the same result. @@ -231,20 +236,20 @@ Let's create that menu. Create a new menu called `RegisterUser`: ```php -php smile menu:new RegisterUser -x +php smile make:menu RegisterUser -x ``` Replace the `message` method by: ```php -public function message($userPreviousResponses) +public function message($previousResponses) { - $name = $userPreviousResponses->get('enter_username'); + $name = $previousResponses->get('enter_username'); return "Thank you {$name}. You have successfully registered."; } ``` -The `$userPreviousResponses` argument is an object containing all the previous responses given by the user. You access a specific response by using its `get` method with the name of the menu as argument. Just make sure the name of the menu is the same used in the `next_menu` of the action bag or in the `defaultNextMenu` method. +The `$previousResponses` argument is an object containing all the previous responses given by the user. You access a specific response by using its `get` method with the name of the menu as argument. Just make sure the name of the menu is the same used in the `next_menu` of the action bag or in the `defaultNextMenu` method. There are other slightly different ways of retrieving the user's response. Learn more about it [here](user-response). @@ -262,7 +267,7 @@ class RegisterUser extends Menu { public function message() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); // Save the user name to the database here @@ -282,7 +287,7 @@ class RegisterUser extends Menu { public function before() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); // Save the user name to the database here } @@ -291,7 +296,7 @@ class RegisterUser extends Menu { // The message will then just display the feedback to the user. - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); return "Thank you {$name}. You have successfully registered."; } @@ -307,7 +312,7 @@ class RegisterUser extends Menu public function before() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); try { // Save the user name to the database here @@ -333,7 +338,7 @@ class RegisterUser extends Menu { public function before() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); try { // Save the user name to the database here diff --git a/current/installation.md b/current/installation.md index 9221acb..b5ed187 100644 --- a/current/installation.md +++ b/current/installation.md @@ -13,13 +13,15 @@ nav_order: 10 - [Testing the application](#testing-the-application) ## Installation + To install Rejoice and craft your application, you need `composer` installed on your machine. If you don't have composer, you can get it [here](https://getcomposer.org). It will help us manage the dependencies of our application. Then, create a Rejoice project by running in the console: ```php -composer create-project --prefer-dist rejoice/rejoice my-ussd-app +composer create-project rejoice/rejoice my-ussd-app ``` + This will create a project in the `my-ussd-app` directory. That's all! @@ -27,6 +29,7 @@ That's all! You can go into the new created folder. ## The app folder + In the new project folder, you can see an `app` folder. The `app` folder is where our code (functions, classes) will reside. ## The public folder @@ -34,6 +37,7 @@ In the new project folder, you can see an `app` folder. The `app` folder is wher The `public` directory is where our application will run from. It is where the index of our application is. You then need to configure your web server's root directory to point to this `public` directory. ## The config folder + The configuration files of the application are in the `config/` folder. Some of the configurations, especially the ones related to credentials can be configured from the `.env` file at the root of the project and referenced in the configuration file using the `env()` function, so that the are not committed in your version control system. @@ -41,9 +45,11 @@ Some of the configurations, especially the ones related to credentials can be co The default configurations are enough for a first application but feel free to go through the files, especially `app.php` and `menu.php`. ## The simulator + The USSD application is meant to be used on a phone with the USSD protocol that is handle by a mobile operator. Rejoice provides two simulators that will help us run and test our application before deploying it. Let's configure the simulator. We need to set: + - the application URL, - the test phone number, - the network [MNC](glossary#mnc) of the phone number (optional), @@ -70,6 +76,7 @@ You do not need to enclose string with double-quotes unless it contains special {: .note .note-warning } ## Testing the application + Rejoice provides a very simple way to test your application. You can test the application either in the console or in your browser. Rejoice comes with a very handful console command called **`smile`**. It will help us run the simulators. @@ -77,7 +84,7 @@ Rejoice comes with a very handful console command called **`smile`**. It will he Open a console at the root of the project and run: ```php -php smile simulator:console +php smile serve -c console ``` This should show in the console: @@ -87,7 +94,7 @@ This should show in the console: MENU SCREEN - It works! + Welcome to Rejoice :) @@ -95,16 +102,17 @@ This should show in the console: You can also test the application via the web simulator by running the command: ```php -php smile simulator:web +php smile serve ``` -This will start a development server at `http://localhost:8001`. + +This will start a development server at `http://localhost:8000`. Open your browser at that address and click on the dial button of the test phone on the page. You should get the test phone screen:
- + @@ -112,11 +120,8 @@ You should get the test phone screen:
It works!Welcome to Rejoice :)
-You can use the shortcuts `php smile sim:con` and `php smile sim:web` to run the simulators. -{: .note .note-info } - Now that we have seen how our applications will look like, let's create our first one. \ No newline at end of file + diff --git a/current/logging.md b/current/logging.md index 9b76638..e28f679 100644 --- a/current/logging.md +++ b/current/logging.md @@ -9,14 +9,17 @@ nav_order: 95 - [Logging](#logging) ## Logging + Anywhere in the menu class, you can log by using the `log` method and specifying the log level as second parameter. By default, the log level is `info`. ```php $this->log('Balance sent') ``` + The log will be stored in the `storage/logs/{date}/{name_of_the_menu_that_is_logging}.log` file. The available log levels are: + - debug - info - notice @@ -27,4 +30,3 @@ The available log levels are: - emergency Rejoice uses the `Notify` package as default log package. It is meant to log very simply. Consider using a more performant log package like `Monolog` if you want extra features. - diff --git a/current/menu/actions.md b/current/menu/actions.md index 861cc30..de05c1b 100644 --- a/current/menu/actions.md +++ b/current/menu/actions.md @@ -6,28 +6,29 @@ nav_order: 43 ---

Actions

-- [The actoin bag](#the-actoin-bag) - - [The actions](#the-actions) - - [The action trigger](#the-action-trigger) - - [Actions properties](#actions-properties) - - [The display property](#the-display-property) - - [The next_menu property](#the-next_menu-property) - - [The save_as property](#the-save_as-property) - - [Default next menu](#default-next-menu) - - [Predefined next menus - Magic menus](#predefined-next-menus---magic-menus) - - [Inserting predefined actions](#inserting-predefined-actions) - - [Inserting Back action](#inserting-back-action) - - [Inserting Welcome action](#inserting-welcome-action) - - [Inserting Paginate forward action](#inserting-paginate-forward-action) - - [Inserting Paginate back action](#inserting-paginate-back-action) - - [Later menu](#later-menu) - - [Calling a remote USSD application](#calling-a-remote-ussd-application) +- [Introduction](#introduction) +- [The action bag](#the-action-bag) +- [The actions](#the-actions) +- [The action trigger](#the-action-trigger) +- [Actions properties](#actions-properties) + - [The display property](#the-display-property) + - [The `next_menu` property](#the-next_menu-property) + - [The `save_as` property](#the-save_as-property) +- [Default next menu](#default-next-menu) +- [Predefined next menus - Magic menus](#predefined-next-menus---magic-menus) +- [Inserting predefined actions](#inserting-predefined-actions) + - [Inserting Back action](#inserting-back-action) + - [Inserting Welcome action](#inserting-welcome-action) + - [Inserting Paginate forward action](#inserting-paginate-forward-action) + - [Inserting Paginate back action](#inserting-paginate-back-action) +- [Later menu](#later-menu) +- [Calling a remote USSD application](#calling-a-remote-ussd-application) ## Introduction Actions allowed the user to navigate through your application and allow us to get information from the user. -# The actoin bag +## The action bag All the actions on a menu are contained in an array called actions bag. The action bag is either returned by the `actions` method of the menu class or is the value of the `actions` index in the menu resource of the specific menu. @@ -57,6 +58,7 @@ return [ ``` In a menu class: + ```php public function actions() { @@ -73,6 +75,7 @@ public function actions() return $actions; } ``` + An integer also will work as action trigger and may be useful for automatically reodering the actions whenever you make a change. But note that the first action displayed will be `0`. {: .note note-info } @@ -81,11 +84,13 @@ An integer also will work as action trigger and may be useful for automatically An action has several properties that allow us to have full control on the navigation in our application and full control over the interaction with the user, especially when requesting information from the user. These are the properties that an action can take: + - `display` - `next_menu` - `save_as` ### The display property + The display property of the action is an very short text indicating what is the action about. It is the text that will be displayed alongside the action trigger on the user's phone. ```php @@ -121,7 +126,7 @@ This will render as: -### The next_menu property +### The `next_menu` property The next menu property of an action is the property that allows us to implement navigation through our menus. It configuration is simple as providing the name of the menu we want to go to. @@ -183,7 +188,7 @@ class ShowProfile extends Menu These assume that classes `ShowProfile` and `DeleteProfile` exist in the `app/Menus/UserProfile` folder. -### The save_as property +### The `save_as` property The `save_as` property allows us to modify the response of the user before it is saved in the session. @@ -228,17 +233,17 @@ class ProcessPayment extends Menu { public function actions() { - $paymentOption = $userPreviousResponses->get('payment_option'); + $paymentOption = $previousResponses->get('payment_option'); switch ($paymentOption) { case 'pay_via_mobile_money': - // Return a custom action + // Return a custom action break; case 'pay_via_cheque': - // Return a custom action + // Return a custom action break; case 'pay_via_paypal': - // Return a custom action + // Return a custom action break; } } @@ -260,14 +265,15 @@ You can learn about user response validation [here](validation). On such menus, we can no more define a next menu in the actions bag. So we need a way to allow the user to send any value even though it is not specified in the actions bag. It is possible by defining the property `default_next_menu` directly in the menu (and not in the actions) or by using the `defaultNextMenu` in the menu class. -You configure the `default_next_menu` the exact way the `next_menu` property of an action is configured. It means the `default_next_menu` will just contain the name of the next menu we want to go to. In a menu class, the `defaultNextMenu` will return the name or the next menu. +You configure the `default_next_menu` the exact way the `next_menu` property of an action is configured. It means the `default_next_menu` will just contain the name of the next menu we want to go to. In a menu class, the `defaultNextMenu` will return the name or the next menu. ## Predefined next menus - Magic menus Rejoice provides and automatically handles some predefined menu for you to allow you to concentrate on the actual application menus. Those menus are called **magic menus**. Their names starts with double undescore. -These are all the magic menus that Rejoice ships with. -- `__back` for going back. +These are all the magic menus that Rejoice ships: + +- `__back` for going back - `__welcome` for returning to the main menu - `__end` to return a last menu message (a.k.a end the session) - `__same` for displaying the same menu again @@ -275,16 +281,17 @@ These are all the magic menus that Rejoice ships with. - `__paginate_back` for implementing a back pagination (this is used by the [Paginator](#implementing-pagination)) Those are the magic menus you can use directly in your application to navigate. There are other magic menus that are reserved to the framework: + - `__split_next` When a menu overflows, Rejoice automatically detects it and splits it under the hood for you. `__split_next` is the name that will be used to reference any next screen of the splitted menu. - `__split_back` will be used to reference any back screen of a splitted menu. - `__continue_last_session` The name of the *continue from last session* menu.
It is highly recommended not to name any of your own menu with double underscore at the beginning!
- ## Inserting predefined actions The available predefined actions are: + - backAction - mainMenuAction - paginateForwardAction @@ -299,22 +306,23 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) - { - $actions = [ - // Define some actions here (or not) - - // Back action - '0' => [ - 'display' => 'Back', - 'next_menu' => '__back', - ] - ]; + public function actions($previousResponses) + { + $actions = [ + // Define some actions here (or not) - return $actions; - } + // Back action + '0' => [ + 'display' => 'Back', + 'next_menu' => '__back', + ] + ]; + + return $actions; + } } ``` + The trigger is `0` and the message attached is `Back`. or @@ -324,7 +332,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ // @@ -343,7 +351,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ // @@ -355,12 +363,13 @@ class ProcessBalanceRequest extends Menu ``` If only the back action will be displayed, you can directly return the back action. + ```php namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { return $this->backAction(); } @@ -369,17 +378,19 @@ class ProcessBalanceRequest extends Menu The backAction method can take two arguments, the trigger and the display message associated. By default the trigger is `0` and the display message is `Back`. + ```php namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { return $this->backAction('99', 'Previous menu'); } } ``` + The method will return the exact array that we harcoded in the first example. You can modify the trigger by passing your custom trigger to the method as first parameter. You can custom the message attached by passing the sustom message as second parameter. @@ -392,7 +403,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ //... @@ -407,6 +418,7 @@ class ProcessBalanceRequest extends Menu } } ``` + The trigger is `00` and the message attached is `Main menu`. Or @@ -416,7 +428,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ // @@ -428,17 +440,19 @@ class ProcessBalanceRequest extends Menu ``` If only the main menu action will be displayed, you can directly return it. + ```php namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { return $this->mainMenuAction(); } } ``` + The `mainMenuAction` method can take two arguments, the trigger and the display message associated. By default the trigger is `00` and the display message is `Main menu`. @@ -451,7 +465,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ //... @@ -466,6 +480,7 @@ class ProcessBalanceRequest extends Menu } } ``` + Since we are going back, we are using the same trigger and message attached as the normal go-back action. The trigger is `0` and the message attached is `Back`. or @@ -475,7 +490,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ // @@ -495,11 +510,11 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ //... - + '0' => [ 'display' => 'Back', 'next_menu' => '__paginate_back', @@ -510,6 +525,7 @@ class ProcessBalanceRequest extends Menu } } ``` + Since we are going back, we are using the same trigger and message attached as the normal go-back action. The trigger is `0` and the message attached is `Back`. or @@ -519,7 +535,7 @@ namespace App\Menus; class ProcessBalanceRequest extends Menu { - public function actions($userPreviousResponses) + public function actions($previousResponses) { $actions = [ // @@ -533,6 +549,7 @@ class ProcessBalanceRequest extends Menu ## Later menu Later menu is a powerful concept that helps you to modify the normal flow of the menu. Actually, every menu defined is attached to another menu through the `next_menu` parameter. It restrict you to use a menu for a specific purpose out of it flow. The later menu bypass that limitation and allows you to call a menu outside of its flow. View from other side, it allows us to create a menu that is not dependent on a particular flow but is rather *alone* and used by multiple flows. This is a scenario to help understand the concept and a use case of a later menu. + ```php // resources/menus/menus.php @@ -557,6 +574,7 @@ return [ ``` In a menu class: + ```php // app/Menus/chooseOptionScreen.php @@ -607,6 +625,7 @@ return [ ``` In a menu class: + ```php // app/Menus/EnterUsername.php @@ -618,7 +637,7 @@ class EnterUsername extends Menu { return 'Kindly enter your name'; } - + public function defaultNextMenu() { return [ @@ -630,6 +649,7 @@ class EnterUsername extends Menu ``` ## Calling a remote USSD application + You can call a remote USSD simply by setting the URL of the USSD application as *next_menu* to an action. ```php @@ -649,7 +669,9 @@ return [ ]; ``` + In a menu class: + ```php // app/Menus/ChooseOptionScreen.php @@ -673,5 +695,6 @@ class ChooseOptionScreen extends Menu } } ``` +
It's important to know that as soon as the USSD has switched to a remote USSD, all subsequent requests will be routed to that remote USSD and this current USSD will be just a relay. No menu of the current USSD can be called again. Everything happens as if the current USSD application has completely handed over to the remote USSD.
\ No newline at end of file diff --git a/current/menu/index.md b/current/menu/index.md index 77a216b..1da8581 100644 --- a/current/menu/index.md +++ b/current/menu/index.md @@ -7,4 +7,4 @@ nav_order: 40 # Menus -Everything about creating a menu. \ No newline at end of file +Everything about creating a menu. diff --git a/current/menu/menu-class.md b/current/menu/menu-class.md index ebc9305..b4dec4d 100644 --- a/current/menu/menu-class.md +++ b/current/menu/menu-class.md @@ -7,7 +7,7 @@ nav_order: 42

Menu class

-- [Creating the menu class](#creating-the-menu-class) +- [The menu methods](#the-menu-methods) - [The `message` method](#the-message-method) - [The `actions` method](#the-actions-method) - [The `validate` method](#the-validate-method) @@ -17,169 +17,223 @@ nav_order: 42 - [The `onBack` hook](#the-onback-hook) - [The `onMoveToNextMenu` hook](#the-onmovetonextmenu-hook) - [Order of the methods](#order-of-the-methods) + We've created our first application and that was pretty cool and rather simple. I'm sure you will need more in your applications. -LNowthat we have all the basics, the rest will not be a big deal. +Now that we have all the basics, the rest will not be a big deal. + +The first thing that worth knowing is Menu classes. +They are object representaion of a menu. The same menu we created in the `menus.php` file above. We will now create it using rather an object. Actually, the menu class does not replace the menu defined in the `menus.php` file. Both can coexist. But the parameters defined in the menu class will for most of them overwrite the ones defined in the menus.php file (except the message parameter, which will rather be combined). + +To create a menu, use the command: -The first thing that worth knowing is Menu Entities. -A menu entity is an object representaion of a menu. The same menu we created in the `menus.php` file above. We will now create it using rather an object. Actually, the menu entity does not replace the menu defined in the `menus.php` file. Both can coexist. But the parameters defined in the menu entity will for most of them overwrite the ones defined in the menus.php file. +```php +php smile make:menu InspiringQuote +``` -A menu entity inherit from a base menu entity class. +Here, `InspiringQuote` is the name of the menu. -The basic scheme looks like this: +This command will generate this menu class in the `Menus` folder: ```php +withBack($actions); } +} +``` - /** - * Allow you to modify the user's response before saving it in the session. - * - * @param string $response - * @return mixed - */ - public function saveAs($response) - { - return $response; - } +You can generate the menu with other menu methods apart from the default `message` and `actions` methods. Use `php smile help make:menu` to know more about it. - /** - * `before` hook. Runs before any other method in the current menu. - * You will usually retrieve data to display in database here from database here - * @param \Prinx\Rejoice\UserResponse $userPreviousResponses - * @return void - */ - public function before($userPreviousResponses) - { +### The menu methods + +#### The `message` method + +By convention, the menu screen is divided in two parts, the message and the actions: + +
+ + + + + + + +
This is the message
Select an option

1. Action 1
2. Action 2
3. Action 3
0. Action 4
+
+ +The `message` method will generate whatever will be displayed in the message part of the menu. It is the equivalent of the `message` parameter of a menu in the `menus.php` resource file. + +This method must return a string or an array of placeholders mapped to their values. + +If a string is returned, the string will be combined to any message of the same menu from the menu resource file. +If an array is returned, Rejoice will assume you their are placeholders in the message from the menu resource file and will attempt to change this placeholders with their respective values. You can create a placeholder like this: + +```php +// resources/menus/menus.php + +return [ + 'welcome' => [ // - } + ], - /** - * `after` hook. Runs after the user has sent their response. - * This method runs anytime we move from the current menu to another menu. - * It means even when we move from this menu to go back, it will run. - * Sometimes it is useful, Sometimes that is not the behavior we want. - * We rather want a method to run a method only when we are moving to a - * real next_menu, not a previous menu or the welcome menu. In that - * particular case, we will use the `onMoveToNextMenu` method. - * - * @param string $response The response given by the user on this menu screen - * @param \Prinx\Rejoice\UserResponse $userPreviousResponses All the previous responses given by the user, attached to their menu name. - * @return void - */ - public function after($response, $userPreviousResponses) - { + 'GetName' => [ // - } + ], + + 'DisplayUserId' => [ + 'message' => 'You are the user number :user_number:', + ], +]; +``` - /** - * Similar to the after method but will run only when moving from this menu - * to a real next_menu. We call a real next_menu, a menu that has been - * defined by you. It then excludes menus like `__back`, `__welcome`, - * `__same`, `__paginate_forward`, `__paginate_back`, which are - * automagically created and managed by the framework. - * - * @param string $response - * @param \Prinx\Rejoice\UserResponse $userPreviousResponses - * @return void - */ - public function onMoveToNextMenu($response, $userPreviousResponses) - { - // - } +Then in the `DisplayUserNumber` menu class: - /** - * Method to run when moving back. - * - * @param \Prinx\Rejoice\UserResponse $userPreviousResponses - * @return void - */ - public function onBack($userPreviousResponses) +```php +previousResponses('GetName'); + + $userNumber = User::where('name', '=', $name)->value('id'); + + return [ + 'user_number' => $userNumber, + ]; } -} + // +} ``` -All these methods are optional. Use the one you want when you need it. -### Creating the menu class +#### The `actions` method -#### The `message` method +The actions method displays the actions part of the menu screen. -#### The `actions` method +The method must return an action bag. An action bag is a normal PHP array. It looks like: + +```php + [ + 'display' => 'Send ID to your email.', + 'next_menu' => 'SendIdToEmail' + ], + ]; + + return $actions; + } +} +``` + +The action bag contains each predefined response of the user mapped to a short message to display near the predefined response and the next menu to call when the user selects that option. Learn more about menu actions [here](actions). #### The `validate` method +The validate method allow to easily apply validation rules to the response of the user. Rejoice ships with some amazing validation rules ready to use. Yet, you can easily implement your custom validation to the response. Just return `true` if the validation passes and `false`, if not. You can learn more [here](validation). + #### The `saveAs` method +The `saveAs` method allows to change the response of the user before saving it. By default the input of the user is saved as the response. By you can easily save another value, based on the response of the user, by using this method. + +```php +// Example +public function saveAs($response) +{ + $goodLength = 15; + + if (strlen($response) < $goodLength) { + return str_pad($response, $goodLength, '_'); + } + + return $response; +} +``` + +This method will be used only if the response of the user is not predefined. For example, when you are requesting for the user's name. When the response of the user is predefined (in the actions), you will rather use the `save_as` parameter of the actions. + #### The `before` hook +The before method allows to run any logic that must run before the menu is displayed. Sometimes, this method will be used to directly send the message to the user. In those cases, the message and actions are no more needed. This situation happens most of the time on the last screen of the application. + #### The `after` hook +The after method allows to run any logic that must run after the user has sent back a response. + #### The `onBack` hook +The onBack method run when the user is going back. + #### The `onMoveToNextMenu` hook +The onMoveToNextMenu method allows to run any logic that must run when the user is actually moving to the following menu. This method has the same purpose as the after hook with the only difference that it will not run if the user is moving to a menu back in the history. + #### Order of the methods -While writing your menu entity logic, it is important to know the order in which the menu entity methods are called. -This is the order in which the menu entity methods are called. + +In order to know very well how to use this methods, it is very important to know the order in which they are called. +This is the order in which the menu class methods are called. Methods called before the menu is rendered to the user: + - `before` - `message` - `actions` After the menu is rendered to the user **and the user has sent a response back**: + - `validate` - `saveAs` - `after` - `onMoveToNextMenu` - -Knowing the order of the menu entity methods helps you to know how to handle your own-created menu entity properties. -
You need to consider this: -Any property of the menu entity is accessible in any of the menu entity methods. - -Any property created or updated in a `before` method (before, message or actions) will not be reflect in an `after` method (validate, saveAs, after, onMoveToNextMenu, etc.) +A property created or updated in a `before` method (before, message or actions) will not be available with the same valuein the `after` methods (validate, saveAs, after, onMoveToNextMenu.) -Any property created or updated in a particular method will not be updated get the update in a method that has already run, according to the order of the methods (for example, an update of a property in the after method will not reflect in the validate method, because at the time the after method is running, the validate method has already run). +Any property created or updated in an after method will not be available with the same value in a method that has already run (before, message, actions).
diff --git a/current/menu/menu-resource.md b/current/menu/menu-resource.md index 2f22e2e..afeac5c 100644 --- a/current/menu/menu-resource.md +++ b/current/menu/menu-resource.md @@ -40,7 +40,7 @@ return [ 'menu_name' => [ 'message' => '', - + 'actions' => [ '1' => [ 'display' => '', @@ -71,7 +71,7 @@ return [ 'welcome' => [ 'message' => 'Welcome to the registration panel. Select an option', - + 'actions' => [ '1' => [ 'display' => 'Register', @@ -82,7 +82,7 @@ return [ 'enter_username' => [ 'message' => 'Kindly enter your name', - + 'actions' => [ '0' => [ 'display' => 'Back', @@ -101,6 +101,7 @@ return [ ``` That's the whole application. But you have surely noticed that certain we could not replicate all the implementation from the menu classes: + - The `welcome` menu was fully implemented. - On, the `enter_username` Everything was implemented, except from the `saveAs`. We will rely on the Menu class for that. (The menu resource and the class are compatible and can coexist.) - On the last menu, the `register_user` menu, we will also rely on the menu class to save the user in the database and also send the right response to the user (whether saving to the database was successful or not.) So the message is empty and actually we can delete it. @@ -108,6 +109,7 @@ That's the whole application. But you have surely noticed that certain we could We no more need the `welcome` menu class. It can be deleted as it is all handled in the menu resource. We still need the `EnterUsername` class and it will be: + ```php namespace App\Menus; @@ -129,7 +131,7 @@ class RegisterUser extends Menu { public function before() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); try { // Save the user name to the database here @@ -144,4 +146,4 @@ class RegisterUser extends Menu As you can see, for a very simple application that just display information, using the menu resource is ok and very useful. But as soon as the application handles some back-end logic, we will need to rely on classes. -You can create a **menu resource first** application, where every menu is created first in the menu resource and is supported by a menu class only when there is a need. Or you can decide to go **the only-classes way**. Then you completely ignore the menu resource and do everything with classes. \ No newline at end of file +You can create a **menu resource first** application, where every menu is created first in the menu resource and is supported by a menu class only when there is a need. Or you can decide to go **the only-classes way**. Then you completely ignore the menu resource and do everything with classes. diff --git a/current/menu/pagination.md b/current/menu/pagination.md index 654f197..e2acaf8 100644 --- a/current/menu/pagination.md +++ b/current/menu/pagination.md @@ -7,140 +7,187 @@ nav_order: 100

Pagination

- [Implementing pagination](#implementing-pagination) - - [The `Paginator` trait](#the-paginator-trait) - - [The paginator required methods](#the-paginator-required-methods) - - [`PaginationFetch`](#paginationfetch) - - [`PaginationCountAll`](#paginationcountall) - - [`itemAction`](#itemaction) - + - [Eloquent Pagination (Recommended)](#eloquent-pagination-recommended) ## Implementing pagination -### The `Paginator` trait -To implement pagination, you will probably need to use the paginator trait. It allows you to quickly deploy a paginable menu. -Note: The `Rejoice` framework handles two types of pagination. -The first one that we call `soft-pagination` is when all the data is retrieved and passed to the framework. It works fine when the data to paginate is small. -The second type of pagination called the `hard-pagination` is the one that requires the use of the first one is when a menu overflows +Rejoice allows you to quickly create a paginable menu. The recommend way to implement a paginable menu is by using the Eloquent ORM. But you can also create the same menu using directly SQL queries. + +This is a typical scenario: + +- We want the user to select his country. +- We assume the user has selected his continent in the previous menu (named `SelectContinent`). -### The paginator required methods -#### `PaginationFetch` -Retrieve the data from the database. Must return the retrieved data. +This is how we will implement such a menu with the help of the eloquent paginator trait. -#### `PaginationCountAll` -Must Return the total of rows fetched from the database. +### Eloquent Pagination (Recommended) + +Create the menu class by using: -#### `itemAction` -When using the paginator trait, instead of the `actions` methods, you will use the `itemAction` method to specify how to insert the actions in your menu. This method will be used under the hood by the actions method to insert the actions automatically for you. -**Example** ```php +php smile make:menu SelectCountry -p +``` + +This command will generate a paginable menu in the `Menus` directory: + +```php + namespace App\Menus; -use Prinx\Rejoice\Menu\Paginator; +use App\Menus\Menu; +use Rejoice\Menu\EloquentPaginator as Paginator; -class BetsHistory extends Menu +class SelectCountry extends Menu { use Paginator; + protected $perPage = 4; + /** - * Number of items to display per page + * Menu message. + * + * @return string|array */ - protected $maxItemsPerPage = 4; - public function message() { - if (!$this->paginationTotal()) { - return "You don't have any bet."; - } else { - return $this->isPaginationFirstPage() ? 'Your betting history' : ''; + if (!$this->itemsExist()) { + return 'No item found'; } + + return 'Select an option'; } - /** - * Defines how the items will be displayed to the user. - * - * This method will automatically be called for each rows of the array - * returned by `paginationFetch`. And its return value will be added to the - * actions - * - * The option is what will be displayed to the user as option to select. - * It's automatically handled by the Paginator - * - * @param array $row - * @param string $option - * @return array - */ - public function itemAction($row, $option) + /** + * Items to paginate. + * + * Do not use '->get()' or '->first()' on the query. + * Rejoice will perform it at the appropriate time. + * + * @return Rejoice\Database\QueryBuilder + */ + public function paginate() { - return [ - $option => [ - 'message' => 'Bet ' . $row['id'], - 'next_menu' => 'bet::bet_details', - 'save_as' => $row['id'], - ], - ]; + // return Model::where([]); } /** - * Fetches the items from the database - * + * Will be called for each item. + * + * @param array|Rejoice\Support\Collection $row + * @param string $trigger * @return array */ - public function paginationFetch() + public function itemAction($row, $trigger) { - if (isset($this->bets)) { - return $this->bets; - } + return [ + $trigger => [ + 'display' => $row['name'], + 'next_menu' => 'menu_name', + 'save_as' => $row['id'], + ], + ]; + } +} +``` - $req = $this->db()->prepare("SELECT * FROM bets - WHERE id > :offset - AND initiator_id = :initiator_id - ORDER BY id - LIMIT :limit"); +The `paginate` method will be called to retrieve the items from the database. It must return the query to select the items from the database or a logic returning the an array of the current items. - $offset = $this->lastRetrievedId(); - $userId = $this->user('id'); - $limit = $this->maxItemsPerPage(); +The `itemAction` defines the action to perform for each item. - $req->bindParam('offset', $offset, \PDO::PARAM_INT); - $req->bindParam('initiator_id', $userId); - $req->bindParam('limit', $limit, \PDO::PARAM_INT); - $req->execute(); +We just now need to modify the paginate method and the `itemAction`. - $this->bets = $req->fetchAll(\PDO::FETCH_ASSOC); +Assuming we have a `Country` model, let's modify our `paginate` method: - $req->closeCursor(); +```php +public function paginate() +{ + $continent = $this->previousResponses('SelectContinent'); - return $this->bets; - } + return Country::where('continent_id', '=', $continent); +} +``` - /** - * Returns the total number of the data to be displayed - * - * @return int - */ - public function paginationCountAll() +Assuming a country has a name and a code and we rather want to save the country's code instead of the ID and the next menu is `InputName`, let's modify the `itemAction` function like this: + +```php +public function itemAction($country, $trigger) +{ + return [ + $trigger => [ + 'display' => $country['name'], + 'next_menu' => 'InputName', + 'save_as' => $country['code'], + ], + ]; +} +``` + +That's all! We have our paginable menu: + +```php + +namespace App\Menus; + +use App\Menus\Menu; +use App\Models\Country; +use Rejoice\Menu\EloquentPaginator as Paginator; + +class SelectCountry extends Menu +{ + use Paginator; + + protected $perPage = 4; + + public function message() { - if ($total = $this->paginationGet('total')) { - return $total; + if (!$this->itemsExist()) { + return 'No item found'; } - $req = $this->db()->prepare("SELECT COUNT(*) FROM bets WHERE initiator_id = ?"); - $req->execute([ - $this->user('id') - ]); + return 'Select an option'; + } - $paginationTotal = intval($req->fetchColumn()); - $req->closeCursor(); + public function paginate() + { + $continent = $this->previousResponses('SelectContinent'); - $this->paginationSave('total', $paginationTotal); + return Country::where('continent_id', '=', $continent); + } - return $paginationTotal; + public function itemAction($country, $trigger) + { + return [ + $trigger => [ + 'display' => $country['name'], + 'next_menu' => 'InputName', + 'save_as' => $country['code'], + ], + ]; + } +} +``` + +We can also customize the `message` method to display different message based on the page on which we are by using one or both of the methods `isPaginationFirstPage` or `isPaginationLastPage`: + +```php +public function message() +{ + if (!$this->itemsExist()) { + return 'No item found'; + } elseif ($this->isPaginationFirstPage()) { + return 'Select an option'; + } elseif ($this->isPaginationFirstPage()) { + return 'Last page'; } + + return ''; } ``` -The paginator makes available some methods for our use: -- `maxItemsPerPage` The maximum number of items that can be showed on the pagination screen; It's configured as protected property of the menu entity; -- `currentItemsCount` The actual number of items showed on the current screen; + +The paginator trait makes available some methods for our use: + +- `perPage` The maximum number of items that can be showed on the pagination screen; this method will return the `perPage` property of the menu if it's configured, else it will return a default value. The default value is `4`. +- `currentItemsCount` The actual number of items showed on the current screen (the number of items can be less the `perPage`, on the last page.); - `isPaginationFirstPage` Check if the current screen is the first screen of the pagination; - `isPaginationLastPage` Check if the current screen is the last screen of the pagination; - `lastRetrievedId` Get the id of the last fetched row - the next query to the database to fetch, will begin at that index; @@ -148,16 +195,18 @@ The paginator makes available some methods for our use: - `paginationGet` Get a pagination data; - `paginationHas` Check if a pagination data exists; -
When using the pagination, always prefer +
When using the pagination, always prefer: ```php -$this->userPreviousResponses('the_name_of_the_previous_menu'); +$this->previousResponses('the_name_of_the_previous_menu'); ``` + to retrieve the response of the previous menu, over ```php -$this->userResponse(); +$this->userResponse(); //or $this->userSavedResponse(); ``` +
diff --git a/current/menu/validation.md b/current/menu/validation.md index 86e983f..228e818 100644 --- a/current/menu/validation.md +++ b/current/menu/validation.md @@ -8,7 +8,7 @@ nav_order: 44

Validation

- [Validation](#validation) - - [In the menu entity](#in-the-menu-entity) + - [In the menu class](#in-the-menu-class) - [Using your custom validation logic](#using-your-custom-validation-logic) - [Using the user response validator instance](#using-the-user-response-validator-instance) - [In the menu flow](#in-the-menu-flow) @@ -32,9 +32,9 @@ nav_order: 44 ## Validation Rejoice has made it very easy to validate the user sent on each particular menu. -You can validate directly inside the menu flow in the `resources/menus/menu.php` file or validate in the menu entity. +You can validate directly inside the menu flow in the `resources/menus/menu.php` file or validate in the menu class. -### In the menu entity +### In the menu class #### Using your custom validation logic @@ -48,7 +48,7 @@ class EnterAge extends Menu if ($age < 5 || $age > 150) { return false; } - + return true; } } @@ -72,7 +72,7 @@ class EnterAge extends Menu return false; } - + return true; } } @@ -95,7 +95,7 @@ class EnterAge extends Menu } elseif ($age > 150) { $this->addError('You must be less than 150 years to continue'); } - + return empty($this->error()); } } @@ -107,7 +107,7 @@ The user response validator is actually the class that the framework uses to aut ```php // Import the class -use Prinx\Rejoice\UserResponseValidator as Validator; +use Rejoice\UserResponseValidator as Validator; class EnterAge extends Menu { @@ -118,10 +118,12 @@ class EnterAge extends Menu return Validator::validate($response, $rules); } } -``` +``` + Or with the custom error messages: + ```php -use Prinx\Rejoice\UserResponseValidator as Validator; +use Rejoice\UserResponseValidator as Validator; class EnterAge extends Menu { @@ -137,9 +139,11 @@ class EnterAge extends Menu } } ``` + Or + ```php -use Prinx\Rejoice\UserResponseValidator as Validator; +use Rejoice\UserResponseValidator as Validator; class EnterAge extends Menu { @@ -161,9 +165,11 @@ class EnterAge extends Menu } } ``` + Or + ```php -use Prinx\Rejoice\UserResponseValidator as Validator; +use Rejoice\UserResponseValidator as Validator; class EnterAge extends Menu { @@ -179,9 +185,11 @@ class EnterAge extends Menu } } ``` + Or We can be less verbose by directly returning only the rules: + ```php -// No need to import the class the validator class +// No need to import the validator class class EnterAge extends Menu { @@ -195,6 +203,7 @@ class EnterAge extends Menu } } ``` +
An empty validation rule will throw a `RuntimeException`.
@@ -205,7 +214,7 @@ An empty validation rule will throw a `RuntimeException`.
return [ 'enter_age' => [ - 'message' => 'Enter your age', + 'message' => 'Enter your age', 'validate' => 'integer|min:5|max:150' 'default_next_menu' => 'retrieve_birth_year', ] @@ -221,7 +230,7 @@ Whatever action is specified in the `actions` parameter has the priority over th Some rules accept parameters. You specify the parameter by following the rule with **colon** (`:`) and the value of the parameter. -In the case a rule requires multiple variables, you can pass the variables by seperating them with a **comma** (`,`). This means, **you cannot pass a colon or a comma as argument to a rule**. If you are required to pass a colon or a comma as argument, you must create a custom validation inside the menu entity. +In the case a rule requires multiple variables, you can pass the variables by seperating them with a **comma** (`,`). This means, **you cannot pass a colon or a comma as argument to a rule**. If you are required to pass a colon or a comma as argument, you must create a custom validation inside the menu class. You can combine several rules by separating them with a pipe (`|`). These are the default rules you can use: @@ -405,7 +414,7 @@ return [
The default format (j/n/Y) accepts dates like '1/5/2025', '07/6/2030', etc. (The date and month do not need the '0' at their begining.)
-
In case you need to perform some calculation on the date, like check if the date is too old or too far from another date, you can use the methods in the Date Utilities class to perform the validation inside the menu entity.
+
In case you need to perform some calculation on the date, like check if the date is too old or too far from another date, you can use the methods in the Date Utilities class to perform the validation inside the menu class.
### tel diff --git a/current/request.md b/current/request.md index fc68a70..bfbf237 100644 --- a/current/request.md +++ b/current/request.md @@ -18,11 +18,15 @@ nav_order: 90 ## Retrieving the request parameters + ### Any request POST parameter + ```php $this->request()->input('name_of_the_input'); ``` + For POST parameters, the request method can be used directly: + ```php $this->request('name_of_the_input'); // Equivalent to @@ -30,6 +34,7 @@ $this->request()->input('name_of_the_input'); ``` ### Any request GET parameter + ```php $this->request()->query('name_of_the_input'); ``` @@ -37,6 +42,7 @@ $this->request()->query('name_of_the_input'); While this two methods are useful, Rejoice has helper methods for some of the parameters: ### The phone number of the user + ```php $this->tel(); // or @@ -44,6 +50,7 @@ $this->msisdn(); ``` ### The network + ```php $this->network(); ``` @@ -51,6 +58,7 @@ $this->network(); This returns the [MNC](glossary#mnc) of the user, not the network name. ### The session ID + ```php $this->sessionId(); ``` @@ -58,18 +66,21 @@ $this->sessionId(); The session ID is retrieved from the mobile operator. ### The request type (a.k.a service operator code) + ```php $this->ussdRequestType(); ``` ### The user's response (a.k.a USSD string) + ```php $this->userResponse(); ``` ### The channel + Useful when building an application not only for USSD. You can do extra stuff, customize the response, etc., based on the channel. ```php $this->channel(); // Will be USSD by default. -``` \ No newline at end of file +``` diff --git a/current/samples/e-commerce.markdown b/current/samples/e-commerce.markdown index c4a6be6..e240bf9 100644 --- a/current/samples/e-commerce.markdown +++ b/current/samples/e-commerce.markdown @@ -12,7 +12,7 @@ nav_order: 153 ## Simple e-commerce application -Anywhere in a menu entity, you can access previous responses of the user by using the method `userPreviousResponses` of the menu entity. +Anywhere in a menu entity, you can access previous responses of the user by using the method `previousResponses` of the menu entity. The method returns a UserPreviousResponse object that holds all the previous responses of the user and allows you to access. The object returned has two main methods that allows you to access the responses: `get` and `has`. Both methods take as parameter the name of the menu for which you want to retrieve the response. `has` as you can guess, checks if there is a response for this particular menu. `get` gets the response for this particular menu. @@ -24,12 +24,12 @@ class EnterAge extends Menu public function validate($response) { - if ($this->userPreviousResponses()->has('has_a_permit')) { + if ($this->previousResponses()->has('has_a_permit')) { return true; } $age = intval($response); - $wantToDrive = $this->userPreviousResponses()->get('want_to_drive'); + $wantToDrive = $this->previousResponses()->get('want_to_drive'); if ($wantToDrive && $age > 16) { $this->setError('Only adults are allowed to drive.'); @@ -40,19 +40,19 @@ class EnterAge extends Menu } } ``` -Some methods of the menu entity get automatically the userPreviousResponses object. as argument. You can decide to use them as argument or stick to the method. Both will give the same result. +Some methods of the menu entity get automatically the previousResponses object. as argument. You can decide to use them as argument or stick to the method. Both will give the same result. The methods `before`, `after`, ``, Calling `get` on a non existent menu may will throw an Exception. ### Advanced manipulations of the user's responses Let's suppose you are creating an application in which users can place orders. You want to allow the users to place multiple orders during the same session on the USSD, you need a way of remembering all the previous responses of the user. The framework allows you to implement such logic very easily. -Actually anytime you come to a screen, the response provided by the user is saved in a responses array for that particular menu. You can then get all the responses provided for a same particular menu by calling the `getAll` method on the object returned by the `userPreviousResponses` method. +Actually anytime you come to a screen, the response provided by the user is saved in a responses array for that particular menu. You can then get all the responses provided for a same particular menu by calling the `getAll` method on the object returned by the `previousResponses` method. ```php class ProcessOrder extends Menu { - public function before($userPreviousResponses) + public function before($previousResponses) { $definedPlateCosts = [ 'beans' => 7, @@ -62,10 +62,10 @@ class ProcessOrder extends Menu ]; // Returns for example ['jollof', 'beans'] - $orderedMeals = $userPreviousResponses->getAll('choose_meal'); + $orderedMeals = $previousResponses->getAll('choose_meal'); // Returns for example [2, 5] - $plateNumbers = $userPreviousResponses->getAll('enter_number_plate'); + $plateNumbers = $previousResponses->getAll('enter_number_plate'); $cost = 0; foreach ($orderedMeals as $key => $meal) { @@ -84,9 +84,9 @@ You can get a specific response by passing as second parameter to the `get` meth ```php class ProcessOrder extends Menu { - public function before($userPreviousResponses) + public function before($previousResponses) { - $secondOrderedMeal = $userPreviousResponses->get('choose_meal', 1); // Yes 1. Remember we count from 0 :) + $secondOrderedMeal = $previousResponses->get('choose_meal', 1); // Yes 1. Remember we count from 0 :) $this->terminate('You second meal is ' $secondOrderedMeal) } } diff --git a/current/samples/index.md b/current/samples/index.md index ad21407..8b4218c 100644 --- a/current/samples/index.md +++ b/current/samples/index.md @@ -7,4 +7,4 @@ nav_order: 150 # Sample applications -These are some sample applications made with Rejoice. Feel free to go through them to learn about the best way of using Rejoice. \ No newline at end of file +These are some sample applications made with Rejoice. Feel free to go through them to learn about the best way of using Rejoice. diff --git a/current/samples/multi-flow.markdown b/current/samples/multi-flow.markdown index 93a066e..d575ade 100644 --- a/current/samples/multi-flow.markdown +++ b/current/samples/multi-flow.markdown @@ -8,7 +8,6 @@ nav_order: 152

Multi-flow application

- [Multi-flow application](#multi-flow-application) - ## Multi-flow application -Let's create a multi-flow application to learn the best way to structure our application. +Let's create a multi-flow application to learn the best way to structure our application. diff --git a/current/samples/one-flow.markdown b/current/samples/one-flow.markdown index e96a17a..d4cbe1d 100644 --- a/current/samples/one-flow.markdown +++ b/current/samples/one-flow.markdown @@ -6,6 +6,7 @@ nav_order: 151 ---

One-flow application

+ - [One-flow application](#one-flow-application) -## One-flow application \ No newline at end of file +## One-flow application diff --git a/current/session.md b/current/session.md index 26e4369..e10b43a 100644 --- a/current/session.md +++ b/current/session.md @@ -26,16 +26,20 @@ You can configure the session in the `config/session.php` file. But typically, n ## Session driver Rejoice supports two drivers for the sessions: file and database. They are basically where the session data will be saved. + - For file session, no extra configuration is needed. - For database session, you will need to provide the database configuratons. No matter which driver you are using, interacting with the session is exactly the same. You can configure the session in the `.env` file: + ```java SESSION_DRIVER=file ``` + Or provide the session database configurations: + ```java SESSION_DRIVER=database @@ -49,6 +53,7 @@ USSD_SESSION_DB_NAME= ## Setting a session variable You can easily save a variable in the session by using the `sessionSave` method. + ```php $this->sessionSave('user_firstname', 'prince'); ``` @@ -59,15 +64,19 @@ You can save arrays in the session. ## Getting a session variable You can easily retrieve a variable from the session by using the `sessionGet` method. + ```php $firstname = $this->sessionGet('user_firstname'); ``` + You can use its shortcut `session` method: + ```php $firstname = $this->session('user_firstname'); ``` You can pass a default value (both to `session` and `sessionGet`) that will be returned in case the parameter specify does not exist in the session. + ```php $firstname = $this->session('user_firstname', 'Guest'); @@ -92,11 +101,13 @@ if ($this->sessionHas('user_firstname')) { ## Removing a session variable You can easily remove a variable from the session by using the `sessionRemove` method. + ```php $this->sessionRemove('user_firstname'); ``` ## Handling the last screen timeout +
This section applies only to the last screen sent to the user.
By default, the Rejoice send the menu screen to the user's phone based on the configured `message` and `actions` (both in the menus.php ressource file and in the menu class). But sometimes, the last screen does not show up and the user rather gets a `connection`[`MMI`](glossary#mmi) error. @@ -113,39 +124,47 @@ This section applies only to the last menu screen sent to the user. Rejoice takes care of discarding the session for us on a last screen. But sometimes, it is uselful to discard the session ourself, may it be to send the response to the user and being able to continue a business logic, or we need to send another push request to the user's phone (another USSD-like request passing by the mobile operator), or we need to call an API that will take long to respond or any other reason. Rejoice provides some in-built method to easily do it. ### Send the response and continue the script + We need the `respond` method: + ```php class ProcessBalanceRequest extends Menu { - public function before($userPreviousResponses) + public function before($previousResponses) { $this->respond('Your request is been processed'); - + // Continue the script (insert in database, call an API, etc.) } } ``` + Instead of the `respond` method, we can use: + ```php $this->respondAndContinue('Your request is been processed'); ``` + or + ```php $this->softEnd('Your request is been processed'); ``` + The three methods (`respond`, `respondAndContinue`, `softEnd`) are aliases. Just choose the one you like. ### Send the response and terminate the script + ```php class ProcessBalanceRequest extends Menu { - public function before($userPreviousResponses) + public function before($previousResponses) { // Your business logic here // Sends the response and exit the script automatically $this->terminate('Your request is been processed'); - + // Anything here will not run } } @@ -156,8 +175,11 @@ Instead of the `terminate` method, we can use: ```php $this->respondAndExit('Your request is been processed'); ``` + or + ```php $this->hardEnd('Your request is been processed'); ``` -The three methods (`terminate`, `respondAndExit`, `hardEnd`) are aliases. Just choose the one you like. \ No newline at end of file + +The three methods (`terminate`, `respondAndExit`, `hardEnd`) are aliases. Just choose the one you like. diff --git a/current/sms.md b/current/sms.md index 4b74067..f424ccf 100644 --- a/current/sms.md +++ b/current/sms.md @@ -16,6 +16,7 @@ This is only available if your SMS endpoint expect a post request with parameter {: .note note-info } ### Simply send SMS + ```php $this->sendSms('Your request is been processed'); ``` @@ -28,6 +29,7 @@ SMS_SENDER_NAME=MYAPP ``` If not, you can directly send SMS like this: + ```php $sms = 'Your request is been processed'; $receiver = $this->tel(); @@ -40,6 +42,7 @@ $this->sendSms($sms, $receiver, $sender, $endpoint); By default, the receiver is the current user of the application, which can be retrieved via the `$this->tel()` method. If not passed, it is automatically passed by Rejoice. ### Send and exit the script + ```php $this->sendSmsAndExit('Your request is been processed'); -``` \ No newline at end of file +``` diff --git a/current/user-response.md b/current/user-response.md index a9ea219..1951ac1 100644 --- a/current/user-response.md +++ b/current/user-response.md @@ -59,20 +59,20 @@ It is important to always see a menu class in two parts: the part that runs **be ### Retrieve a user's response from any menu -You can easily have access to the response response of the user on any menu by using the `userPreviousResponses` helper method. +You can easily have access to the response response of the user on any menu by using the `previousResponses` helper method. You will pass the menu name to the method. ```php public function before() { - $name = $this->userPreviousResponses('enter_username'); + $name = $this->previousResponses('enter_username'); $this->respond("Thank you {$name}. You have successfully registered."); } ``` -Without argument, `userPreviousResponses` returns an object that allows us to retrieve the user response. +Without argument, `previousResponses` returns an object that allows us to retrieve the user response. The object has two useful methods: `get` and `has`. `get` allows to retrieve a response. @@ -81,7 +81,7 @@ The object has two useful methods: `get` and `has`. ```php public function before() { - $responses = $this->userPreviousResponses(); + $responses = $this->previousResponses(); $name = $responses->has('enter_username') ? $responses->get('enter_username') : 'dear user'; $this->respond("Thank you {$name}. You have successfully registered."); @@ -95,36 +95,40 @@ You can pass a second parameter as default value that will be returned if the re ```php public function before() { - $name = $this->userPreviousResponses('enter_username', 'dear user'); + $name = $this->previousResponses('enter_username', 'dear user'); $this->respond("Thank you {$name}. You have successfully registered."); } ``` + Or Using the `get` method: + ```php public function before() { - $name = $this->userPreviousResponses()->get('enter_username', 'dear user'); + $name = $this->previousResponses()->get('enter_username', 'dear user'); $this->respond("Thank you {$name}. You have successfully registered."); } ``` ### Multiple responses + Sometimes it is useful to have multiple answers for the same menu. Let's take the example of a simple e-commerce application where the user can choose multiple products. To have access to all the chosen products. -Having access to all the responses on a menu is as simple as calling the `getAll` method on the object returned by `userPreviousResponses`. +Having access to all the responses on a menu is as simple as calling the `getAll` method on the object returned by `previousResponses`. ```php public function before() { - $products = $this->userPreviousResponses()->getAll('choose_product'); + $products = $this->previousResponses()->getAll('choose_product'); // Process the chosen products here $this->respond("Your order is being processed."); } ``` + The `getAll` method returns an array containing the responses of the user on the `choose_product` menu. The first response is at the first index, the second at the second index, etc. -See it in activity in this [sample application](samples/e-commerce). \ No newline at end of file +See it in activity in this [sample application](samples/e-commerce). diff --git a/index.md b/index.md index 56cc1ca..b8883dd 100644 --- a/index.md +++ b/index.md @@ -3,26 +3,33 @@ title: Introduction layout: default nav_order: 1 --- + # THE REJOICE FRAMEWORK + ## Introduction + Rejoice is a PHP framework for USSD applications. It main goal is to speed up your USSD application development while having at anytime a scalable code. The Rejoice Framework allows you to create simple to complex USSD applications. -## You said USSD? +## You said USSD?' + Yes! [USSD](current/glossary#ussd) stands for **Unstructured Supplementary Service Data**. It is a network protocol allowing mobile operators to send information and communicate with phone users. A USSD application is basically a series of popups (which are called *menus* or *pages* or *screens*) that display or request information from the user via their mobile phones. ## How does it works? -Unlike a normal web application, a USSD request is created when a user dials a *USSD code* on their phone. A USSD code usually starts with a star (`*`) and ends with a hash (`#`), eg: **\*380#**. The request is forward to the mobile operator. The mobile operator then checks if the shortcode is mapped to an internal application or a third-party application. If an application (proudly developed with Rejoice) is mapped to the shortcode, they create a session and forward the request to the application. The interaction user-application happens in a session automatically created when the user firstly dialed the USSD code, which ends when either the user has completed the USSD flow or when the user decides to cancel the session themselves. + +Unlike a normal web application, a USSD request is created when a user dials a *USSD code* on their phone. A USSD code usually starts with a star (`*`) and ends with a hash (`#`), eg: **\*000#**. The request is forward to the mobile operator. The mobile operator then checks if the shortcode is mapped to an internal application or a third-party application. If an application (proudly developed with Rejoice) is mapped to the shortcode, they create a session and forward the request to the application. The interaction user-application happens in a session automatically created when the user firstly dialed the USSD code, which ends when either the user has completed the USSD flow or when the user decides to cancel the session themselves. ## What do I need to know before starting? + You don't need to know anything apart from Object-Oriented with the PHP language. The rest we will guide you step-by-step to make your best application. ## Our Philosophy + You must be shouting for joy while building your application 😀 \ No newline at end of file + From 9d3f503e4ef2f57c59a04b196ebd82140d9189bd Mon Sep 17 00:00:00 2001 From: "aj.code" Date: Tue, 24 Nov 2020 18:02:57 +0000 Subject: [PATCH 6/6] first application content updated --- Gemfile.lock | 89 ++++++++++++++++++++---------------- current/first-application.md | 4 +- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index db5f358..f1cb173 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.2) + activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -16,38 +16,39 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.6) - dnsruby (1.61.4) + concurrent-ruby (1.1.7) + dnsruby (1.61.5) simpleidn (~> 0.1) - em-websocket (0.5.1) + em-websocket (0.5.2) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) ethon (0.12.0) ffi (>= 1.3.0) - eventmachine (1.2.7-x64-mingw32) + eventmachine (1.2.7-x86-mingw32) execjs (2.7.0) - faraday (1.0.1) + faraday (1.1.0) multipart-post (>= 1.2, < 3) - ffi (1.13.1-x64-mingw32) + ruby2_keywords + ffi (1.13.1-x86-mingw32) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (206) + github-pages (209) github-pages-health-check (= 1.16.1) - jekyll (= 3.8.7) + jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.13.0) + jekyll-feed (= 0.15.1) jekyll-gist (= 1.5.0) jekyll-github-metadata (= 2.13.0) - jekyll-mentions (= 1.5.1) + jekyll-mentions (= 1.6.0) jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.15.0) + jekyll-redirect-from (= 0.16.0) jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.1) + jekyll-remote-theme (= 0.4.2) jekyll-sass-converter (= 1.5.2) jekyll-seo-tag (= 2.6.1) jekyll-sitemap (= 1.4.0) @@ -55,7 +56,7 @@ GEM jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) + jekyll-theme-hacker (= 0.1.2) jekyll-theme-leap-day (= 0.1.1) jekyll-theme-merlot (= 0.1.1) jekyll-theme-midnight (= 0.1.1) @@ -66,13 +67,14 @@ GEM jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.11.1) - kramdown (>= 2.3.0) + jemoji (= 0.12.0) + kramdown (= 2.3.0) + kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.19.0) + rouge (= 3.23.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -80,20 +82,20 @@ GEM octokit (~> 4.0) public_suffix (~> 3.0) typhoeus (~> 1.3) - html-pipeline (2.13.0) + html-pipeline (2.14.0) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.8.7) + jekyll (3.9.0) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) i18n (~> 0.7) jekyll-sass-converter (~> 1.0) jekyll-watch (~> 2.0) - kramdown (~> 2.3) + kramdown (>= 1.17, < 3) liquid (~> 4.0) mercenary (~> 0.3.3) pathutil (~> 0.9) @@ -113,14 +115,14 @@ GEM rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.13.0) + jekyll-feed (0.15.1) jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.5.1) + jekyll-mentions (1.6.0) html-pipeline (~> 2.3) jekyll (>= 3.7, < 5.0) jekyll-optional-front-matter (0.3.2) @@ -128,14 +130,15 @@ GEM jekyll-paginate (1.1.0) jekyll-readme-index (0.3.0) jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.15.0) + jekyll-redirect-from (0.16.0) jekyll (>= 3.3, < 5.0) jekyll-relative-links (0.6.1) jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.1) + jekyll-remote-theme (0.4.2) addressable (~> 2.0) jekyll (>= 3.5, < 5.0) - rubyzip (>= 1.3.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) jekyll-seo-tag (2.6.1) @@ -152,8 +155,8 @@ GEM jekyll-theme-dinky (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) + jekyll-theme-hacker (0.1.2) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) jekyll-theme-leap-day (0.1.1) jekyll (~> 3.5) @@ -187,17 +190,20 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.11.1) + jemoji (0.12.0) gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - just-the-docs (0.3.1) + just-the-docs (0.3.3) jekyll (>= 3.8.5) jekyll-seo-tag (~> 2.0) rake (>= 12.3.1, < 13.1.0) - kramdown (>= 2.3.0) + kramdown (2.3.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) liquid (4.0.3) - listen (3.2.1) + listen (3.3.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) @@ -206,11 +212,11 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.1) + minitest (5.14.2) multipart-post (2.1.1) - nokogiri (1.10.10-x64-mingw32) + nokogiri (1.10.10-x86-mingw32) mini_portile2 (~> 2.4.0) - octokit (4.18.0) + octokit (4.19.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) @@ -220,9 +226,11 @@ GEM rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - rouge (3.19.0) + rexml (3.2.4) + rouge (3.23.0) ruby-enum (0.8.0) i18n + ruby2_keywords (0.0.2) rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) @@ -240,24 +248,25 @@ GEM thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.7) + tzinfo (1.2.8) thread_safe (~> 0.1) - tzinfo-data (1.2020.1) + tzinfo-data (1.2020.4) tzinfo (>= 1.0.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7-x64-mingw32) + unf_ext (0.0.7.7-x86-mingw32) unicode-display_width (1.7.0) wdm (0.1.1) - zeitwerk (2.4.0) + zeitwerk (2.4.1) PLATFORMS - x64-mingw32 + x86-mingw32 DEPENDENCIES github-pages jekyll-feed (~> 0.12) just-the-docs + kramdown (>= 2.3.0) tzinfo (~> 1.2) tzinfo-data wdm (~> 0.1.1) diff --git a/current/first-application.md b/current/first-application.md index 60463cd..f3d2a40 100644 --- a/current/first-application.md +++ b/current/first-application.md @@ -18,14 +18,14 @@ nav_order: 20 ## USSD menus -A USSD application consists of Menus also known as Screens, that follow each other and process the user's interaction in a USSD session. Building a USSD application will be actually implementing this interactions with the user. Rejoice allows you to build the USSD menus and implement how the follow each other in a simple a beautiful way. It allows you to have full control on the user's interaction, the user's answer to each menu. +A USSD application consists of Menus also known as Screens, that follow each other and process the user's interaction in a USSD session. Building a USSD application will be actually implementing this interactions with the user. Rejoice allows you to build the USSD menus and implement how they follow each other in a simple and beautiful way. It allows you to have full control on the user's interaction, the user's answer to each menu. Let's see how to create our USSD menus. All your menus will be in the app/Menus folder (or sub-folders). ## The default menus -When you open the `app/Menus` folder, You will notice that there is already a `Menu.php` and a `Welcome.php` files in it. Those two are the minimum for every application. +When you open the `app/Menus` folder, You will notice that there is already existing `Menu.php` and a `Welcome.php` files in it. Those two are the minimum for every application. The `Menu.php` contains the `Menu` class which is the base menu class that all your menus will extend.