Issues (4)

src/BusUndoable.php (1 issue)

1
<?php
2
3
namespace Joselfonseca\LaravelTactician;
4
5
use Joselfonseca\LaravelTactician\Handler\MethodNameInflectorUndoable;
6
use Joselfonseca\LaravelTactician\Handler\CommandNameExtractorUndoable;
7
use Joselfonseca\LaravelTactician\Locator\LocatorInterface;
8
use Joselfonseca\LaravelTactician\Middleware\LockingMiddlewareUndoable;
9
use Joselfonseca\LaravelTactician\Handler\CommandHandlerMiddlewareUndoable;
10
use Joselfonseca\LaravelTactician\Exception\CommandHistoryIsEmptyException;
11
use Joselfonseca\LaravelTactician\Exception\CommandHistoryWasEmptiedException;
12
use Joselfonseca\LaravelTactician\Exception\RedoCommandHistoryIsEmptyException;
13
14
/**
15
 * The default Command bus Using Tactician, this is an implementation to dispatch commands to their handlers
16
 * trough a middleware stack, every class is resolved from the laravel's service container.
17
 *
18
 * @package Joselfonseca\LaravelTactician
19
 */
20
class BusUndoable extends Bus implements CommandBusUndoableInterface
21
{
22
    private $commandHistoryCounter = -1; // history is empty
23
    private $reDoCommandHistoryCounter = -1; // redo history is empty
24
    private $commandHistory = [];
25
    private $reDoCommandHistory = [];
26
27
    public function __construct(
28
        MethodNameInflectorUndoable $MethodNameInflector,
29
        CommandNameExtractorUndoable $CommandNameExtractor,
30
        LocatorInterface $HandlerLocator
31
    ) {
32
        parent::__construct($MethodNameInflector, $CommandNameExtractor, $HandlerLocator);
33
34
        // $this->commandHistoryCounter = $this->reDoCommandHistoryCounter = -1;
35
    }
36
37
    /**
38
     * Dispatch a command and recall it
39
     *
40
     * @param  object $command    Command to be dispatched
41
     * @param  array  $input      Array of input to map to the command
42
     * @param  array  $middleware Array of middleware class name to add to the stack, they are resolved from the laravel container
43
     * @return mixed
44
     */
45
    public function dispatch($command, array $input = [], array $middleware = [])
46
    {
47
        $commandDispatched = parent::dispatch($command, $input, $middleware);
48
49
        $this->pushCommandHistory( [clone $commandDispatched, $middleware] ); // Memento Pattern
50
51
        return $commandDispatched;
52
    }
53
54
    private function pushCommandHistory (array $commandDispatchedAndMiddleware)
55
    {
56
        $this->commandHistory[++$this->commandHistoryCounter] = $commandDispatchedAndMiddleware;
57
    }
58
59
    public function undo()
60
    {
61
        try{
62
            $commandDispatchedAndMiddleware = $this->popCommandHistory();
63
            $this->pushRedoCommandHistory($commandDispatchedAndMiddleware);
64
65
            $this->handleTheUndoCommand($commandDispatchedAndMiddleware[0], $commandDispatchedAndMiddleware[1]);
66
67
            return $this->topCommandHistory()[0];
68
69
        }catch (CommandHistoryWasEmptiedException $exception){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
70
    }
71
72
    private function popCommandHistory()
73
    {
74
        if ($this->commandHistoryCounter == -1) {
75
            throw new CommandHistoryIsEmptyException();
76
        }
77
78
        $commandDispatchedAndMiddleware = $this->commandHistory[$this->commandHistoryCounter];
79
        unset($this->commandHistory[$this->commandHistoryCounter]);
80
        --$this->commandHistoryCounter;
81
82
        return $commandDispatchedAndMiddleware;
83
    }
84
85
    private function topCommandHistory()
86
    {
87
        if ($this->commandHistoryCounter == -1){
88
            throw new CommandHistoryWasEmptiedException();
89
        }
90
91
        return $this->commandHistory[$this->commandHistoryCounter];
92
    }
93
94
    private function pushRedoCommandHistory(array $commandDispatchedAndMiddleware)
95
    {
96
        $this->reDoCommandHistory[++$this->reDoCommandHistoryCounter] = $commandDispatchedAndMiddleware;
97
    }
98
99
    private function handleTheUndoCommand($commandDispatched, array $middleware): void
100
    {
101
        $this->bus = new CommandBusUndoable(
102
            array_merge(
103
                [new LockingMiddlewareUndoable()],
104
                $this->resolveMiddleware(array_reverse($middleware)), // in reversed order
105
                [new CommandHandlerMiddlewareUndoable($this->CommandNameExtractor, $this->HandlerLocator, $this->MethodNameInflector)]
106
            )
107
        );
108
        $this->bus->handlerUndo($commandDispatched);
109
    }
110
111
    public function redo()
112
    {
113
        $commandDispatchedAndMiddleware = $this->popRedoCommandHistory();
114
        $this->pushCommandHistory($commandDispatchedAndMiddleware);
115
116
        $this->handleTheRedoCommand($commandDispatchedAndMiddleware[0], $commandDispatchedAndMiddleware[1]);
117
118
        return $commandDispatchedAndMiddleware[0];
119
    }
120
121
    private function popRedoCommandHistory()
122
    {
123
        if( $this->reDoCommandHistoryCounter == -1 ) {
124
            throw new RedoCommandHistoryIsEmptyException();
125
        }
126
127
        $commandDispatchedAndMiddleware = $this->reDoCommandHistory[$this->reDoCommandHistoryCounter];
128
        unset($this->reDoCommandHistory[$this->reDoCommandHistoryCounter]);
129
        --$this->reDoCommandHistoryCounter;
130
131
        return $commandDispatchedAndMiddleware;
132
    }
133
134
    private function handleTheRedoCommand($commandDispatched, $middleware): void
135
    {
136
        $this->bus = new CommandBusUndoable(
137
            array_merge(
138
                [new LockingMiddlewareUndoable()],
139
                $this->resolveMiddleware($middleware), // in original order again !!!!
140
                [new CommandHandlerMiddlewareUndoable($this->CommandNameExtractor, $this->HandlerLocator, $this->MethodNameInflector)]
141
            )
142
        );
143
        $this->bus->handlerRedo($commandDispatched);
144
    }
145
146
    public function clearHistory()
147
    {
148
        $this->commandHistory = [];
149
        $this->reDoCommandHistory = [];
150
        $this->commandHistoryCounter = $this->reDoCommandHistoryCounter = -1;
151
    }
152
153
    public function dump()
154
    {return;
155
        echo PHP_EOL.'commandHistoryCounter: '.$this->commandHistoryCounter;
156
        echo PHP_EOL.'reDoCommandHistoryCounter: '.$this->reDoCommandHistoryCounter.PHP_EOL;
157
    }
158
}
159