Skip to content

Instantly share code, notes, and snippets.

@tox2ik
Created April 17, 2015 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tox2ik/1f5d4123759f44673132 to your computer and use it in GitHub Desktop.
Save tox2ik/1f5d4123759f44673132 to your computer and use it in GitHub Desktop.
<?php
/*
* Service-registration with the command design-pattern:
* For details, see: http://www.oodesign.com/command-pattern.html
*
* A notable deviation from recommended approach in this implementation;
* A command (RegistrationTask) does most of the work. The recommended structure
* is to do the expensive work in the Receiver (taskReceiver).
*
* I'm not sure how we can make the receiver do the work without redefining a
* receiver for every service.
*
* The intention here is to reuse common registration steps without duplicating
* much code. Thhis is acheived because only the task-creation code (new Task())
* is duplicated in each service.
*
* Besides, we do not need to run the various tasks in separate threads
* or via a (complex) queue. So I think it's ok to do the work in the
* Task->execute(). Perhaps we could think about forking things like the
* payex-registration in the future.
*
*/
interface TaskInterface {
/**
* @return void
*/
public function execute();
public function setReceiver(TaskReceiver $taskReceiver);
}
class TaskInvoker {
private $tasks = [];
function addTask(RegistrationTask $task) {
$tasks[]= $task;
}
function runTasks() {
foreach ($tasks as $e)
$e->execute();
}
}
class RegistrationTaskReceiver {
private $completedTasks;
private $failedTasks;
function receiveReport(TaskReport $tr) {
if ($tr->isCompleted()) {
$this->completedTasks[] = $tr;
} else {
$this->failedTasks[]= $tr;
}
}
function getSuccessReports() {
return $this->completedTasks;
}
function getFailureReports() {
return $this->failedTasks;
}
}
/*
*
* Tasks
*
*
*/
abstract class RegistrationTask implements TaskInterface {
/* @var RegistrationTaskWorker */
protected $taskReceiver;
public function setReceiver(TaskReceiver $tr) {
$this->taskReceiver = $taskReceiver;
}
public function execute() {
if (empty($this->taskReceiver)) {
throw new \mko\IllegalArgumentException('execute() can not be invoked before a taskReceiver is set.');
}
}
}
class PlaceOrderWithPayexTask extends RegistrationTask {
/* @var PayexGateway */
private $payex;
/* @var User */
private $customer;
/* @var Order */
private $order;
public function __construct(
PayexGateway $payex,
User $customer,
Order $order
) {
$this->payex = $payex;
$this->customer = $customer;
$this->order = $order;
}
public function execute() {
parent::execute();
$failureReason = new Exception('Order could not be completed.');
if (! $customer->hasPayexAgreementRef()) {
$payex->createAgreementRef($customer);
}
$orderRef = $payex->completeOrder($customer, $order);
$report = TaskReport::createFailureReport()->setData([
'errors' => [ $failureReason ]
]);
if ($payex->checkUserHasPaid($orderRef)) {
$report = TaskReport::createSuccessfulReport()->setData([
'success' => __CLASS__
]);
}
$this->taskReceiver->receiveReport($report);
}
}
class CreateUserTask extends RegistrationTask {
private $user;
private $userRepository;
public function __construct(User $user, UserRepository $userRepo) {
$this->user = $user;
}
public function execute() {
$failureReason = new Exception('User was not created. Unknown reason.');
try {
$haveUser = $userRepo->addNew($this->user);
} catch (\Exception $e) {
$failureReason = $e;
}
$report = TaskReport::createFailureReport()->addData([ 'errors' => [ $failureReason]]);
if ($haveUser) {
$report = TaskReport::createSuccessfulReport()->setData([
'success' => __CLASS__,
'user' => $this->user ,
]);
}
$this->taskReceiver->receiveReport($report);
}
}
/*
*
* Services
*
*
*/
abstract class RegistrationService {
use \Myworkout\Traits\SetOrGetTrait;
private $taskReceiver;
private $taskInvoker;
/* registration steps */
private $registrationTaskList;
public $request;
/* state flags */
private $registrationSuccessState;
/*
These dependencies should not be in the constructor of the service,
because they belong to a task
"check coupon" is a task
CouponService $couponService,
$this->couponService = $couponService;
"create user" is a task.
UserRepositoryInterface $userRepository
$this->userRepository = $userRepository;
"send email" is a task.
MailerInterface $mailer,
$this->mailer = $mailer;
The nonce interface is should not be here because the controller
(validation) should not pass bad requests to the registration service
NonceInterface $nonce,
$this->nonce = $nonce;
*/
public function __construct(
Config $config,
SessionInterface $session,
TaskInvoker $taskInvoker,
TaskReceiver $taskReceiver
) {
$this->config = $config;
$this->session = $session;
$this->taskInvoker = $taskReceiver;
$this->taskReceiver = $taskReceiver;
}
public function addRegistrationTask(TaskInterface $task) {
$this->registrationTasksList[] = $task;
}
public function processRegistration($request) {
$this->request = $request;
foreach ($this->registrationTasksList as $e) {
$this->taskInvoker->addTask($e);
}
$this->taskInvoker->runTasks();
$failedTasks = $this->taskInvoker->getFailureReports();
$completedTasks = $this->taskInvoker->getFailureReports();
if (!empty($failedTasks)) {
$this->isSuccessfulRegistration(false);
//return?
$this->signalController(array_merge($failedTasks, $completedTasks));
} else {
$this->isSuccessfulRegistration(true);
//return?
$this->signalController($completedTasks);
}
}
/* Helpers that subclasses will need */
protected function createUserFromRequest() {
return User::create()
->firstName($this->request->get('user_name'))
->emal($this->request->get('user_email'))
->password($this->request->get('user_password'));
}
public function isSuccessfulRegistration() {
return $this->setOrGet(func_get_args(), $this->registrationSuccessState);
}
}
class PutUserInGroupTask extends RegistrationTask {
}
class MyworkoutRegistrationService extends RegistrationService {
public function __construct(
Config $config,
SessionInterface $session,
TaskInvoker $taskInvoker,
TaskReceiver $taskReceiver,
PayexGateway $payex
) {
$this->config = $config;
$this->session = $session;
$this->taskInvoker = $taskReceiver;
$this->taskReceiver = $taskReceiver;
$this->payex = $payex;
}
public function processRegistration($request) {
$user = $this->createUserFromForm();
$createUserTask = new CreateUserTask(
$user,
$this->userRepo
);
$payexTask = new PlaceOrderWithPayexTask(
$this->payex,
$user,
$this->order
);
$this->addRegistrationTask($createUserTask);
$this->addRegistrationTask($payexTask);
return parent::processRegistration($request);
}
}
class PowelRegistrationService extends RegistrationService {
public function __construct(
Config $config,
SessionInterface $session,
TaskInvoker $taskInvoker,
TaskReceiver $taskReceiver,
MkoGroup $rootGroup
) {
$this->config = $config;
$this->session = $session;
$this->taskInvoker = $taskReceiver;
$this->taskReceiver = $taskReceiver;
$this->rootGroup = $rootGroup;
}
public function processRegistration($request) {
$user = $this->createUserFromForm();
$createUserTask = new CreateUserTask(
$user,
$this->userRepo
);
$putUserInGroupTask = new PutUserInGroupTask(
$user,
$request->group_id,
$rootGroup
);
$this->addRegistrationTask($createUserTask);
$this->addRegistrationTask($putUserInGroupTask);
return parent::processRegistration($request);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment