Command pattern encapsulates a full request in an object. So, the object can be passed as a parameter or can be queued or saved in log. Later the object can be revived and we get the full request, which can be executed and/or undoned at any point in time.
This article demonstrates Command pattern implementations in PHP. Check the following examples.
Implementation
Follow the steps below for command pattern implementation-
- Define interface for the command classes. Declare methods for the process(es) execution and undoing the execution.
- Define command classes, and implement the interface for the classes. Define the declared method for the execution.
- Create command controller class. Declare an array for holding list of the executed command.
- In the command controller class define methods for execution and undoing. In the execution implementation call the execution form the command and then add the command to the list. In the undoing process implementation call the undoing method from the command then remove the command from list.
Here is a simple example of Command pattern implementation in PHP-
<?php
// Command pattern implementation in PHP
// Command interface
interface CommandI {
function execute(): void;
function undo(): void;
}
// First command class
// Implement CommandI.
// Implementation of the class will be according to our requirement.
class Command1 implements CommandI {
public function execute(): void {
echo "Command1: Executingn";
}
public function undo(): void {
echo "Command1: Undoingn";
}
}
// Second command class
// Implement CommandI.
// Implementation of the class will be according to our requirement.
class Command2 implements CommandI {
public function __construct(private string $prop1) {
}
public function execute(): void {
echo "Command2: executing -- Prop1 value: " . $this->prop1 . "n";
}
public function undo(): void {
echo "Command2: undoingn";
}
}
// Command control
class CommandControl {
private array $commandList = [];
public function executeCommand(CommandI $command): void {
// Call the command execution method
$command->execute();
// Add command to list to keep track of the command
array_push($this->commandList, $command);
}
public function removeCommand(CommandI $command): void {
// Call the undoing process of the command
$command->undo();
// Find the command and remove command from list
foreach ($this->commandList as $key => $currentCommand) {
if ($command === $currentCommand) {
unset($this->commandList[$key]);
break;
}
}
}
// Undo the last executed command
public function undo(): void {
$lastCommand = end($this->commandList);
$this->removeCommand($lastCommand);
}
}
// Demo
$commandControl = new CommandControl();
$command1 = new Command1();
$commandControl->executeCommand($command1);
$command2 = new Command2("my first prop");
$commandControl->executeCommand($command2);
$command3 = new Command1();
$commandControl->executeCommand($command3);
$command4 = new Command2("your command 2 prop");
$commandControl->executeCommand($command4);
// Undo specific command
$commandControl->removeCommand($command1);
// Undo last command
$commandControl->undo();
Output of the code will be as below-
Command1: Executing
Command2: executing -- Prop1 value: my first prop
Command1: Executing
Command2: executing -- Prop1 value: your command 2 prop
Command1: Undoing
Command2: undoing
Examples
Here are a few examples of Command pattern implementations.
Example #1: UI Elements
In this example we have some classes for UI Elements. In each class, we have a print method and a method for undoing.
We will finally implement a command controller for the command pattern implementation. Let’s check step-by-step:
Command Interface
- Create file “UiCommand.php”.
- Define interface “UiCommand”. This will ensure a common interface for all the UI command classes.
- Declare methods – “print”, “remove”.
<?php
// UiCommand.php
namespace BigBoxCodeDesignPatternCommandUiElement;
interface UiCommand {
function print();
function remove();
}
Button Concrete Class
- Create file “ButtonUi.php”.
- Create class “ButtonUi”.
- Implement interface “UiCommand” for the class.
- Define method “print” for printing the button.
- Define method “undo” for removing the button element.
<?php
// ButtonUi.php
namespace BigBoxCodeDesignPatternCommandUiElement;
class ButtonUi implements UiCommand {
public function __construct(private string $name) {
}
public function print(): void {
echo "Printing " . $this->name . " Buttonn";
}
public function remove(): void {
echo "Removing " . $this->name . " Buttonn";
}
}
Input Element Concrete Class
- Create file “InputUi.php”.
- Create class “InputUi”.
- Implement “UiCommand” interface for the class. Define method “print” and “undo” for the interface implementation.
<?php
// InputUi.php
namespace BigBoxCodeDesignPatternCommandUiElement;
class InputUi implements UiCommand {
public function print(): void {
echo "Printing Inputn";
}
public function remove(): void {
echo "Removing Inputn";
}
}
Table Element Concrete Class
- Create file “TableUi.php”.
- Create class “TableUi” and implement “UiCommand” interface for the class.
<?php
// TableUi.php
namespace BigBoxCodeDesignPatternCommandUiElement;
class TableUi implements UiCommand {
public function print(): void {
echo "Printing Tablen";
}
public function remove(): void {
echo "Removing Tablen";
}
}
UI Element Control Class
- Create file “UiControl.php”.
- Define class “UiControl”.
- Define a private array property named “$commandList” to store the list of executed commands.
- Define method “addElement”. Accept a param of type “UiCommand”. In the implementation of the method call the “print” method from the command and then add the command in the array “$commandList”.
- Define method “removeElement”. Accept a command as param, of the “UiCommand”. In the implementation call the “undo” method of the command object and then remove the command from list “$commandList”.
- Define an “undo” method, which undo the last executed command.
<?php
// UiControl.php
namespace BigBoxCodeDesignPatternCommandUiElement;
class UiControl {
private array $commandList = [];
public function addElement(UiCommand $command) {
$command->print();
array_push($this->commandList, $command);
}
public function removeElement(UiCommand $command) {
$command->remove();
foreach ($this->commandList as $key => $currentCommand) {
if ($command === $currentCommand) {
unset($this->commandList[$key]);
break;
}
}
}
// Remove the last command
public function undo() {
$command = end($this->commandList);
$this->removeElement($command);
}
}
Demo
In the client, create an object of “UiControl”. Then create some object of the “ButtonUi”, “TableUi”, “InputUi”, then add those command to the UI controller using “addElement” method. That will print the elemetns.
Once added, then the commands can be removed/undone after that.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternCommandUiElementButtonUi;
use BigBoxCodeDesignPatternCommandUiElementInputUi;
use BigBoxCodeDesignPatternCommandUiElementTableUi;
use BigBoxCodeDesignPatternCommandUiElementUiControl;
$uiControl = new UiControl();
$inputUi = new InputUi();
$tableUi = new TableUi();
$buttonUi = new ButtonUi("Submit");
$uiControl->addElement($inputUi);
$uiControl->addElement($tableUi);
$uiControl->addElement($buttonUi);
$uiControl->removeElement($tableUi);
$uiControl->addElement(new ButtonUi("Cancel"));
$uiControl->addElement(new TableUi());
$uiControl->addElement(new InputUi());
$uiControl->addElement(new ButtonUi("Wrong button"));
// Undo commands
$uiControl->undo();
$uiControl->undo();
Output
The following output will be generated.
Printing Input
Printing Table
Printing Submit Button
Removing Table
Printing Cancel Button
Printing Table
Printing Input
Printing Wrong button Button
Removing Wrong button Button
Removing Input
Example #2: Database Migration
Here we are considering a database migration process. We have some DB migration files and we want those migrations to be executed.
Each of the migrations is a self-contained command. So we can implement the command pattern to perform the migration process.
Migration Interface
- Create file “Migration.php”.
- Define interface “Migration”.
- Declare methods – “up”, and “down”.
<?php
// Migration.php
namespace BigBoxCodeDesignPatternCommandDbMigration;
interface Migration {
function up(): void;
function down(): void;
}
Migration Files
Let’s create a directory named “migrations“. All our migration field will be in this directory. The filenames can be determined by us.
Each of the migration file will return a migration object, which implements the “Migration” interface.
Let’s create a few migration files in the “Migrations” directory.
Migration File #1 [Command Class]
- Create file “1234_create_user_table.php” inside the “migrations” directory.
- Create a class and return an object of the class.
- Implement interface “Migration” for the class.
- Define method “up” for the migration process.
- Define method “down” for migration rollback.
<?php
// migrations/1234_create_user_table.php
use BigBoxCodeDesignPatternCommandDbMigrationMigration;
return new class implements Migration {
public function up(): void {
// dummy migration
echo "Creating User tablen";
// put actual migration code here
}
public function down(): void {
// dummy migration
echo "Dropping User tablen";
// put actual migration code here
}
};
Migration File #2 [Command Class]
- Create file “2345_create_permission_table.php” inside “migrations” directory.
- Create a class and return an object of the class. Implement interface “Migration” for the class. Define method “up” and “down”.
<?php
// migrations/2345_create_permission_table.php
use BigBoxCodeDesignPatternCommandDbMigrationMigration;
return new class implements Migration {
public function up(): void {
// dummy migration
echo "Creating Permission tablen";
// put actual migration code here
}
public function down(): void {
// dummy migration
echo "Dropping Permission tablen";
// put actual migration code here
}
};
Migration File #3 [Command Class]
- Create file “9921_add_token_to_user_table.php” inside “migrations” directory.
- Create a class and return an object of the class. Implement interface “Migration” for the class. Define method “up” and “down”.
<?php
// migrations/9921_add_token_to_user_table.php
use BigBoxCodeDesignPatternCommandDbMigrationMigration;
return new class implements Migration {
public function up(): void {
// dummy migration
echo "Adding Token column from user tablen";
// put actual migration code here
}
public function down(): void {
// dummy migration
echo "Dropping Token column from user tablen";
// put actual migration code here
}
};
Migration Controller
- Create file “MigrationControl.php”.
- Define class “MigrationControl”.
- Define a private property “$executedMigrationList” array, which is used to store all executed migrations.
- Define method “migrate” which accept a migration object. In the meothd, check if migration is already ran or not, if not then call the “up” of the migration. Then add the migration object to the “$executedMigrationList”.
- Define method “rollback”. In the implementation call the “down” method of the migration object passed to the method.
<?php
// MigrationControl.php
namespace BigBoxCodeDesignPatternCommandDbMigration;
class MigrationControl {
private array $executedMigrationList = [];
public function migrate(Migration $migration) {
// Check if migration is in the list (already executed)
if (array_search($migration, $this->executedMigrationList) !== false) {
return;
}
// Call 'up' of the migration
$migration->up();
// Add migration to the list
array_push($this->executedMigrationList, $migration);
}
public function rollback(Migration $migration) {
// Check if migration exist in the list or not
$migrationKey = array_search($migration, $this->executedMigrationList);
if ($migrationKey === false) {
return;
}
// Call 'down' of the migration
$migration->down();
// Remove migration from list
unset($this->executedMigrationList[$migrationKey]);
}
}
Demo
First, we are getting all the migration objects. This is not part of the command pattern implementation, these are a general operation that gets the list of files in the “migrations” directory and then generates the object of that migration.
For using the command pattern implementation create a migration controller.
Then we can pass a migration object to the “migrate” or “rollback” method of the controller for running the migration process or rollback.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternCommandDbMigrationFileLoadUtil;
use BigBoxCodeDesignPatternCommandDbMigrationMigrationControl;
// Load migration classes
// These are general operation, not related to the Command pattern implementation
const MIGRATION_FILE_PATH = __DIR__ . "/migrations";
$migrationFileList = scandir(MIGRATION_FILE_PATH, SCANDIR_SORT_ASCENDING);
$migrations = [];
foreach ($migrationFileList as $fileName) {
$fullPath = MIGRATION_FILE_PATH . "/" . $fileName;
if (is_file($fullPath)) {
$migrations[] = require($fullPath);
}
}
// Migration loading end
// Create a command controller (MigrationControl) object
$migrationController = new MigrationControl();
// Run migration "up" for all migration
foreach ($migrations as $migration) {
$migrationController->migrate($migration);
}
// Rollback all migrations
$reverseMigration = array_reverse($migrations);
foreach ($reverseMigration as $migration) {
$migrationController->rollback($migration);
}
Output
The following output will be generated.
Creating User table
Creating Permission table
Adding Token column from user table
Dropping Token column from user table
Dropping Permission table
Dropping User table
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Command pattern implementation in other programming languages.