Created
January 28, 2020 13:30
-
-
Save wilcorrea/c79f4c621b595ddd0abe52bfce22f315 to your computer and use it in GitHub Desktop.
Slim 4 Action Composition
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Application\Http; | |
use Psr\Http\Message\ResponseInterface as Response; | |
use Psr\Http\Message\ServerRequestInterface as Request; | |
use Psr\Log\LoggerInterface; | |
use Slim\Exception\HttpBadRequestException; | |
/** | |
* Class AbstractController | |
* @package App\Application\Http | |
*/ | |
abstract class AbstractController | |
{ | |
/** | |
* @var LoggerInterface | |
*/ | |
protected $logger; | |
/** | |
* @var Request | |
*/ | |
protected $request; | |
/** | |
* @var Response | |
*/ | |
protected $response; | |
/** | |
* @var array | |
*/ | |
protected $args; | |
/** | |
* @param LoggerInterface $logger | |
*/ | |
public function __construct(LoggerInterface $logger) | |
{ | |
$this->logger = $logger; | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param $args | |
*/ | |
public function boot(Request $request, Response $response, $args) | |
{ | |
$this->request = $request; | |
$this->response = $response; | |
$this->args = $args; | |
} | |
/** | |
* @return array|object | |
* @throws HttpBadRequestException | |
*/ | |
protected function getFormData() | |
{ | |
$input = json_decode(file_get_contents('php://input'), true); | |
if (json_last_error() !== JSON_ERROR_NONE) { | |
throw new HttpBadRequestException($this->request, 'Malformed JSON input.'); | |
} | |
return $input; | |
} | |
/** | |
* @param string $name | |
* @param mixed [$fallback] | |
* @return mixed | |
*/ | |
protected function getBodyValue(string $name, $fallback = null) | |
{ | |
$parsedBody = $this->request->getParsedBody(); | |
if (isset($parsedBody[$name])) { | |
return $parsedBody[$name]; | |
} | |
try { | |
$formData = $this->getFormData(); | |
if (isset($formData[$name])) { | |
return $formData[$name]; | |
} | |
} catch (HttpBadRequestException $exception) { | |
} | |
return $fallback; | |
} | |
/** | |
* @return array | |
*/ | |
protected function getBodyValues(): array | |
{ | |
$parsedBody = $this->request->getParsedBody(); | |
try { | |
$formData = $this->getFormData(); | |
} catch (HttpBadRequestException $exception) { | |
$formData = []; | |
} | |
return array_merge($parsedBody, $formData); | |
} | |
/** | |
* @param string $name | |
* @param mixed [$fallback] | |
* @return mixed | |
*/ | |
protected function getQueryValue(string $name, $fallback = null) | |
{ | |
$parsedBody = $this->request->getQueryParams(); | |
if (isset($parsedBody[$name])) { | |
return $parsedBody[$name]; | |
} | |
return $fallback; | |
} | |
/** | |
* @return array | |
*/ | |
protected function getQueryValues(): array | |
{ | |
return $this->request->getQueryParams(); | |
} | |
/** | |
* @param string $name | |
* @return mixed | |
* @throws HttpBadRequestException | |
*/ | |
protected function resolveArg(string $name) | |
{ | |
if (!isset($this->args[$name])) { | |
throw new HttpBadRequestException($this->request, "Could not resolve argument `{$name}`."); | |
} | |
return $this->args[$name]; | |
} | |
/** | |
* @param array|object|null $data | |
* @return Response | |
*/ | |
protected function respondWithData($data = null): Response | |
{ | |
$payload = new ActionPayload(200, $data); | |
return $this->respond($payload); | |
} | |
/** | |
* @param ActionPayload $payload | |
* @return Response | |
*/ | |
protected function respond(ActionPayload $payload): Response | |
{ | |
$json = json_encode($payload, JSON_PRETTY_PRINT); | |
$this->response->getBody()->write($json); | |
return $this->response->withHeader('Content-Type', 'application/json'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Application\Http; | |
use App\Application\Exceptions\ErrorInvalidArgument; | |
use App\Application\Exceptions\ErrorRuntime; | |
use App\Infrastructure\Helper\Invoke; | |
use DI\Container; | |
use DI\DependencyException; | |
use DI\NotFoundException; | |
use Psr\Http\Message\ResponseInterface as Response; | |
use Psr\Http\Message\ServerRequestInterface as Request; | |
use Psr\Log\LoggerInterface; | |
use ReflectionException; | |
/** | |
* Class ActionController | |
* @package App\Application\Http\Rest | |
*/ | |
abstract class ActionController extends AbstractController | |
{ | |
/** | |
*/ | |
use Invoke; | |
/** | |
* @var Container | |
*/ | |
private $container; | |
/** | |
* @param LoggerInterface $logger | |
* @param Container $container | |
*/ | |
public function __construct(LoggerInterface $logger, Container $container) | |
{ | |
parent::__construct($logger); | |
$this->container = $container; | |
} | |
/** | |
* @param Request $request | |
* @param Response $response | |
* @param array $args | |
* @return Response | |
* @throws DependencyException | |
* @throws ErrorInvalidArgument | |
* @throws ErrorRuntime | |
* @throws NotFoundException | |
* @throws ReflectionException | |
*/ | |
public function __invoke(Request $request, Response $response, $args): Response | |
{ | |
$this->boot($request, $response, $args); | |
$result = $this->callWithContainer($this->container, 'run', $this->args); | |
if ($result instanceof Response) { | |
return $result; | |
} | |
return $this->respondWithData($result); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Infrastructure\Helper; | |
use App\Application\Exceptions\ErrorInvalidArgument; | |
use App\Application\Exceptions\ErrorRuntime; | |
use DI\Container; | |
use DI\DependencyException; | |
use DI\NotFoundException; | |
use ReflectionException; | |
use ReflectionMethod; | |
use ReflectionParameter; | |
use stdClass; | |
/** | |
* Trait Invoke | |
* @package App\Infrastructure\Helper | |
*/ | |
trait Invoke | |
{ | |
/** | |
* @param Container $container | |
* @param object $class | |
* @param array $parameters | |
* @return mixed | |
* @throws DependencyException | |
* @throws ErrorInvalidArgument | |
* @throws ErrorRuntime | |
* @throws NotFoundException | |
* @throws ReflectionException | |
*/ | |
public function callWithContainer(Container $container, string $method, array $parameters) | |
{ | |
if (!method_exists($this, $method)) { | |
throw new ErrorRuntime([$method => 'required']); | |
} | |
$reflection = new ReflectionMethod($this, $method); | |
$arguments = $this->resolveParameters($container, $reflection->getParameters(), $parameters); | |
return call_user_func_array([$class, $method], $arguments); | |
} | |
/** | |
* Generate a list of values to be used like parameters to one method or function | |
* | |
* @param Container $container | |
* @param array $parameters | |
* @param array $data | |
* @return array | |
* @throws DependencyException | |
* @throws ErrorInvalidArgument | |
* @throws NotFoundException | |
* @throws ReflectionException | |
*/ | |
private function resolveParameters(Container $container, array $parameters, array $data): array | |
{ | |
$resolved = []; | |
/** @var ReflectionParameter $reflectionParameter */ | |
foreach ($parameters as $reflectionParameter) { | |
if (isset($reflectionParameter->getClass()->name)) { | |
$resolved[] = $container->get($reflectionParameter->getClass()->name); | |
continue; | |
} | |
$resolved[] = $this->parseParameter($reflectionParameter, $data); | |
} | |
return $resolved; | |
} | |
/** | |
* Configure the best resource to each parameter of one method or function | |
* | |
* @param ReflectionParameter $reflectionParameter | |
* @param array $data | |
* @return mixed | |
* @throws ErrorInvalidArgument | |
* @throws ReflectionException | |
*/ | |
private function parseParameter(ReflectionParameter $reflectionParameter, array $data) | |
{ | |
// get the principal properties | |
$name = $reflectionParameter->getName(); | |
if (isset($data[$name])) { | |
return $data[$name]; | |
} | |
if ($reflectionParameter->isOptional()) { | |
return $reflectionParameter->getDefaultValue(); | |
} | |
if (!$reflectionParameter->hasType()) { | |
return null; | |
} | |
if ($reflectionParameter->getType()->allowsNull()) { | |
return null; | |
} | |
$type = $reflectionParameter->getType()->getName(); | |
switch ($type) { | |
case 'string': | |
return ''; | |
case 'int': | |
case 'integer': | |
case 'double': | |
return 0; | |
case 'boolean': | |
return false; | |
case 'array': | |
return []; | |
case 'object': | |
return new stdClass; | |
} | |
throw new ErrorInvalidArgument(['type' => 'invalid', 'parameter' => $name]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Http\Admin\Profile; | |
use App\Application\Exceptions\ErrorInvalidArgument; | |
use App\Application\Http\ActionController; | |
use App\Domain\Admin\Profile\ProfileRepository; | |
/** | |
* Class ProfileExampleAction | |
* @package App\Http\Admin\Profile | |
*/ | |
class ProfileExampleController extends ActionController | |
{ | |
/** | |
* @param ProfileRepository $repository | |
* @param string $id | |
* @return array | |
* @throws ErrorInvalidArgument | |
*/ | |
protected function run(ProfileRepository $repository, string $id) | |
{ | |
return []; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment