How to implement the Command design pattern in PHP for undoable operations?

by ryan.murray , in category: PHP General , 2 years ago

How to implement the Command design pattern in PHP for undoable operations?

Facebook Twitter LinkedIn Telegram Whatsapp

2 answers

Member

by larissa , 2 years ago

@ryan.murray 

The Command design pattern is a behavioral pattern that is used to encapsulate a request as an object, allowing the request to be parameterized with different arguments, queued, logged, or even undoable. In PHP, you can implement the Command pattern for undoable operations as follows:

  1. Define an interface for the command:
1
2
3
4
interface Command {
    public function execute();
    public function undo();
}


  1. Implement the interface in a concrete command class, which encapsulates the operation to be performed:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class ConcreteCommand implements Command {
    private $receiver;
    private $state;
    
    public function __construct(Receiver $receiver, $state) {
        $this->receiver = $receiver;
        $this->state = $state;
    }
    
    public function execute() {
        $this->receiver->performOperation($this->state);
    }
    
    public function undo() {
        $this->receiver->undoOperation($this->state);
    }
}


  1. Define a receiver class, which is responsible for performing the operation:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Receiver {
    private $state;
    
    public function performOperation($state) {
        // Perform the operation
        $this->state = $state;
    }
    
    public function undoOperation($state) {
        // Undo the operation
        $this->state = null;
    }
    
    public function getState() {
        return $this->state;
    }
}


  1. Create a invoker class, which maintains a queue of commands and can execute or undo them:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Invoker {
    private $history = [];
    
    public function execute(Command $command) {
        $command->execute();
        $this->history[] = $command;
    }
    
    public function undo() {
        $command = array_pop($this->history);
        if ($command) {
            $command->undo();
        }
    }
}


  1. Finally, create an instance of the receiver and the invoker, and use them to execute and undo commands:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$receiver = new Receiver();
$invoker = new Invoker();

$command1 = new ConcreteCommand($receiver, 'state1');
$command2 = new ConcreteCommand($receiver, 'state2');

$invoker->execute($command1);
$invoker->execute($command2);

echo $receiver->getState(); // Outputs 'state2'

$invoker->undo();
echo $receiver->getState(); // Outputs 'state1'

$invoker->undo();
echo $receiver->getState(); // Outputs null


In the above example, we created a ConcreteCommand class that encapsulates an operation on a Receiver object. We also created an Invoker class that maintains a history of executed commands and can undo them. Finally, we created an instance of the Receiver and Invoker classes, and used them to execute and undo commands.

by darrion.kuhn , 2 years ago

@ryan.murray 

The Command design pattern is a behavioral design pattern that provides a way to encapsulate a request as an object, thereby allowing the request to be parameterized with different arguments, queued, logged, or even undone.


To implement the Command design pattern in PHP for undoable operations, you can follow these steps:

  1. Define an interface for the Command that declares a method for executing the command and a method for undoing the command.
1
2
3
4
interface Command {
    public function execute();
    public function undo();
}


  1. Create a concrete command class that implements the Command interface. This class should encapsulate the operation that needs to be performed and its corresponding undo operation.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class AddProductCommand implements Command {
    protected $product;
    protected $quantity;
    protected $cart;
    
    public function __construct(Product $product, $quantity, Cart $cart) {
        $this->product = $product;
        $this->quantity = $quantity;
        $this->cart = $cart;
    }
    
    public function execute() {
        $this->cart->addProduct($this->product, $this->quantity);
    }
    
    public function undo() {
        $this->cart->removeProduct($this->product);
    }
}


  1. Create an Invoker class that will execute the commands and keep track of the undo history.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Invoker {
    protected $history = [];
    
    public function execute(Command $command) {
        $command->execute();
        $this->history[] = $command;
    }
    
    public function undo() {
        $command = array_pop($this->history);
        if ($command) {
            $command->undo();
        }
    }
}


  1. Finally, you can use the Command design pattern by creating the necessary objects and passing them to the Invoker.
1
2
3
4
5
6
7
8
9
$product = new Product('Apple', 0.5);
$cart = new Cart();
$command = new AddProductCommand($product, 2, $cart);

$invoker = new Invoker();
$invoker->execute($command);

// To undo the last command
$invoker->undo();


This implementation will allow you to encapsulate different operations as Command objects and execute them using the Invoker. The Invoker will keep track of the undo history, allowing you to undo any previous command.