Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@wilcorrea
Created January 28, 2020 13:30
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 wilcorrea/c79f4c621b595ddd0abe52bfce22f315 to your computer and use it in GitHub Desktop.
Save wilcorrea/c79f4c621b595ddd0abe52bfce22f315 to your computer and use it in GitHub Desktop.
Slim 4 Action Composition
<?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');
}
}
<?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);
}
}
<?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]);
}
}
<?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