1. Go to this page and download the library: Download wol-soft/php-workflow library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
class AcceptOpenSuggestionForSong implements \PHPWorkflow\Step\WorkflowStep {
/**
* Each step must provide a description. The description will be used in the debug
* log of the workflow to get a readable representation of an executed workflow
*/
public function getDescription(): string
{
return 'Accept open suggestions for songs which shall be added to a playlist';
}
/**
* Each step will get access to two objects to interact with the workflow.
* First the WorkflowControl object $control which provides methods to skip
* steps, mark tests as failed, add debug information etc.
* Second the WorkflowContainer object $container which allows us to get access
* to various workflow related objects.
*/
public function run(
\PHPWorkflow\WorkflowControl $control,
\PHPWorkflow\State\WorkflowContainer $container
) {
$openSuggestions = (new SuggestionRepository())
->getOpenSuggestionsByPlaylistId($container->get('playlist')->getId());
// If we detect a condition which makes a further execution of the step
// unnecessary we can simply skip the further execution.
// By providing a meaningful reason our workflow debug log will be helpful.
if (empty($openSuggestions)) {
$control->skipStep('No open suggestions for playlist');
}
foreach ($openSuggestions as $suggestion) {
if ($suggestion->getSongId() === $container->get('song')->getId()) {
if ((new SuggestionService())->acceptSuggestion($suggestion)) {
// If we detect a condition where the further workflow execution is
// unnecessary we can skip the further execution.
// In this example the open suggestion was accepted successfully so
// the song must not be added to the playlist via the workflow.
$control->skipWorkflow('Accepted open suggestion');
}
// We can add warnings to the debug log. Another option in this case could
// be to call $control->failWorkflow() if we want the workflow to fail in
// an error case.
// In our example, if the suggestion can't be accepted, we want to add the
// song to the playlist via the workflow.
$control->warning("Failed to accept open suggestion {$suggestion->getId()}");
}
}
// for completing the debug log we mark this step as skipped if no action has been
// performed. If we don't mark the step as skipped and no action has been performed
// the step will occur as 'ok' in the debug log - depends on your preferences :)
$control->skipStep('No matching open suggestion');
}
}
$workflowContainer = (new \PHPWorkflow\State\WorkflowContainer())
->set('user', Session::getUser())
->set('song', (new SongRepository())->getSongById($request->get('songId')))
->set('playlist', (new PlaylistRepository())->getPlaylistById($request->get('playlistId')));
// returns an item or null if the key doesn't exist
public function get(string $key)
// set or update a value
public function set(string $key, $value): self
// remove an entry
public function unset(string $key): self
// check if a key exists
public function has(string $key): bool
class AddSongToPlaylistWorkflowContainer extends \PHPWorkflow\State\WorkflowContainer {
public function __construct(
public User $user,
public Song $song,
public Playlist $playlist,
) {}
}
$workflowContainer = new AddSongToPlaylistWorkflowContainer(
Session::getUser(),
(new SongRepository())->getSongById($request->get('songId')),
(new PlaylistRepository())->getPlaylistById($request->get('playlistId')),
);
$workflowResult = (new \PHPWorkflow\Workflow('AddSongToPlaylist'))
// ...
->executeWorkflow($workflowContainer);
$workflowResult = (new \PHPWorkflow\Workflow('AddSongToPlaylist'))
// hard validator: if the user isn't allowed to edit the playlist
// the workflow execution will be cancelled immediately
->validate(new CurrentUserIsAllowedToEditPlaylistValidator(), true)
// soft validators: all validators will be executed
->validate(new PlaylistAlreadyContainsSongValidator())
->validate(new SongGenreMatchesPlaylistGenreValidator())
->validate(new PlaylistContainsNoSongsFromInterpret())
// ...
// Mark the current step as skipped.
// Use this if you detect, that the step execution is not necessary
// (e.g. disabled by config, no entity to process, ...)
public function skipStep(string $reason): void;
// Mark the current step as failed. A failed step before and during the processing of
// a workflow leads to a failed workflow.
public function failStep(string $reason): void;
// Mark the workflow as failed. If the workflow is failed after the process stage has
// been executed it's handled like a failed step.
public function failWorkflow(string $reason): void;
// Skip the further workflow execution (e.g. if you detect it's not necessary to process
// the workflow). If the workflow is skipped after the process stage has been executed
// it's handled like a skipped step.
public function skipWorkflow(string $reason): void;
// Useful when using loops to cancel the current iteration (all upcoming steps).
// If used outside a loop, it behaves like skipStep.
public function continue(string $reason): void;
// Useful when using loops to break the loop (all upcoming steps and iterations).
// If used outside a loop, it behaves like skipStep.
public function break(string $reason): void;
// Attach any additional debug info to your current step.
// The infos will be shown in the workflow debug log.
public function attachStepInfo(string $info): void
// Add a warning to the workflow.
// All warnings will be collected and shown in the workflow debug log.
// You can provide an additional exception which caused the warning.
// If you provide the exception, exception details will be added to the debug log.
public function warning(string $message, ?Exception $exception = null): void;
$parentWorkflowContainer = (new \PHPWorkflow\State\WorkflowContainer())->set('parent-data', 'Hello');
$nestedWorkflowContainer = (new \PHPWorkflow\State\WorkflowContainer())->set('nested-data', 'World');
$workflowResult = (new \PHPWorkflow\Workflow('AddSongToPlaylist'))
->validate(new CurrentUserIsAllowedToEditPlaylistValidator())
->before(new \PHPWorkflow\Step\NestedWorkflow(
(new \PHPWorkflow\Workflow('AcceptOpenSuggestions'))
->validate(new PlaylistAcceptsSuggestionsValidator())
->before(new LoadOpenSuggestions())
->process(new AcceptOpenSuggestions())
->onSuccess(new NotifySuggestor()),
$nestedWorkflowContainer,
))
->process(new AddSongToPlaylist())
->onSuccess(new NotifySubscribers())
->executeWorkflow($parentWorkflowContainer);
$workflowResult = (new \PHPWorkflow\Workflow('AddSongToPlaylist'))
->validate(new CurrentUserIsAllowedToEditPlaylistValidator())
->process(
(new \PHPWorkflow\Step\Loop(new SongLoop()))
->addStep(new AddSongToPlaylist())
->addStep(new ClearSongCache())
)
->onSuccess(new NotifySubscribers())
->executeWorkflow($workflowContainer);
class SongLoop implements \PHPWorkflow\Step\LoopControl {
/**
* As well as each step also each loop must provide a description.
*/
public function getDescription(): string
{
return 'Loop over all provided songs';
}
/**
* This method will be called before each loop run.
* $iteration will contain the current iteration (0 on first run etc)
* You have access to the WorkflowControl and the WorkflowContainer.
* If the method returns true the next iteration will be executed.
* Otherwise the loop is completed.
*/
public function executeNextIteration(
int $iteration,
\PHPWorkflow\WorkflowControl $control,
\PHPWorkflow\State\WorkflowContainer $container
): bool {
// all songs handled - end the loop
if ($iteration === count($container->get('songs'))) {
return false;
}
// add the current song to the container so the steps
// of the loop can access the entry
$container->set('currentSong', $container->get('songs')[$iteration]);
return true;
}
}
public function run(
\PHPWorkflow\WorkflowControl $control,
// The key customerId must contain a string
#[\PHPWorkflow\Step\Dependency\Required('customerId', 'string')]
// The customerAge must contain an integer. But also null is accepted.
// Each type definition can be prefixed with a ? to accept null.
#[\PHPWorkflow\Step\Dependency\Required('customerAge', '?int')]
// Objects can also be type hinted
#[\PHPWorkflow\Step\Dependency\Required('created', \DateTime::class)]
\PHPWorkflow\State\WorkflowContainer $container,
) {
// Implementation which can rely on the defined keys to be present in the container.
}
// check if the workflow execution was successful
public function success(): bool;
// check if warnings were emitted during the workflow execution
public function hasWarnings(): bool;
// get a list of warnings, grouped by stage
public function getWarnings(): array;
// get the exception which caused the workflow to fail
public function getException(): ?Exception;
// get the debug execution log of the workflow
public function debug(?OutputFormat $formatter = null);
// access the container which was used for the workflow
public function getContainer(): WorkflowContainer;
// get the last executed step
// (e.g. useful to determine which step caused a workflow to fail)
public function getLastStep(): WorkflowStep;
// assert the debug output of the workflow. See library tests for example usages
protected function assertDebugLog(string $expected, WorkflowResult $result): void
// provide a step which you expect to fail the workflow.
// example: $this->expectFailAtStep(MyFailingStep::class, $workflowResult);
protected function expectFailAtStep(string $step, WorkflowResult $result): void
// provide a step which you expect to skip the workflow.
// example: $this->expectSkipAtStep(MySkippingStep::class, $workflowResult);
protected function expectSkipAtStep(string $step, WorkflowResult $result): void