📜 ⬆️ ⬇️

We use annotations in PHP to the maximum

Being a back-end developer, I love microservice architectures with all the fibers of my soul, but even more, I like to develop microservices. When developing, whatever, I adhere to one simple principle - minimalism. By minimalism, I mean a simple truth: the code should be as transparent as possible, it should be at a minimum (the ideal code is the one that does not exist), and therefore I bet in favor of annotations.


In this article, I will bring to your attention the skeleton for future applications, in which annotations are used to solve the following tasks:



Such a skeleton will be based on the following packages:



Also, such a skeleton will be based on packages adhering to the following PSR recommendations:



In this article, I will not tell you what a composer is, about a beaten "onion," and even more so about PSR, it is assumed that you already know all this in one degree or another.


Too lazy to read, what is it?
composer create-project sunrise/awesome-skeleton app 

wheel bike


Controllers


As a rule, regardless of what tools we use, we always have routing separately, controllers separately, I don’t take account of Symfony Routing right now, this is a little about something else.


Routing in controllers


Consider the example below:


 declare(strict_types=1); namespace App\Http\Controller; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; /** * @Route( * id="resource.update", * path="/resource/{id<\d+>}", * methods={"PATCH"}, * before={ * "App\Http\Middleware\FooMiddleware", * "App\Http\Middleware\BarMiddleware" * }, * after={ * "App\Http\Middleware\BazMiddleware", * "App\Http\Middleware\QuxMiddleware" * } * ) */ class ResourceUpdateController implements MiddlewareInterface { /** * {@inheritDoc} */ public function process( ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface { $response = $handler->handle($request); // some code return $response; } } 

You probably noticed that the controller is middleware, as in Zend Expressive, moreover, through the annotations, you can specify which middleware will be launched before the launch of this controller, and which after.


The @Route annotation may contain the following properties:



The route path contains the usual for all {id} construct, however, to validate an attribute, you must specify a regular expression {id <\ d +>} , and if you need to make part of the path optional, you just need to put it in brackets / resource / {action} (/ {id }) .


It should be noted that the properties of the annotation are strictly validated, you will be notified immediately if your regular expressions are not correct, or the middleware does not exist, etc.


Injection dependencies in controllers


Consider the example below:


 declare(strict_types=1); namespace App\Http\Controller; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; /** * @Route( * id="resource.update", * path="/resource/{id<\d+>}", * methods={"PATCH"} * ) */ class ResourceUpdateController implements MiddlewareInterface { /** * @Inject * @var LoggerInterface */ protected $logger; /** * {@inheritDoc} */ public function process( ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface { $this->logger->debug('foo bar'); $response = $handler->handle($request); // some code return $response; } } 

There is nothing easier than getting some kind of dependency from a container ...


Register controller in the application


You just need to create a controller, the rest of the application will do for you, find such a controller, pass it to the router, which will launch it if necessary ... What could be simpler? The main thing that is required of you out of the box, create controllers in the src / Http / Controller directory.


wheel bike


Models


If you worked with Doctrine ORM and Symfony Validator, nothing interesting for you, except that everything is configured out of the box, for the rest I will present some examples. Immediately it is necessary to designate that the models should be created in the src / Entity directory and inherit from App \ Entity \ AbstractEntity .


A simple example of a model


Consider the example below:


 declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\Entity * @ORM\HasLifecycleCallbacks * @ORM\Table(name="resource") */ class Resource extends AbstractEntity { /** * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * @ORM\Column(type="integer") * * @var null|int */ protected $id; /** * @ORM\Column( * type="string", * length=128, * nullable=false * ) * * @Assert\NotBlank * @Assert\Type("string") * @Assert\Length(max=128) * * @var null|string */ protected $title; /** * @ORM\Column( * type="text", * nullable=false * ) * * @Assert\NotBlank * @Assert\Type("string") * * @var null|string */ protected $content; // setters and getters } 

Again, there is no need to register our model anywhere, there is no need to describe somewhere a validation, or a schema of the tables, all in one place, simple and clear. The only thing that needs to be done in order for the resource table to be created is to run the following script from the application root:


 composer db:update 

Database connection settings are in the file: src / config / environment.php


A simple example of using the model in the controller


 declare(strict_types=1); namespace App\Http\Controller; use App\Entity\Resource; use Doctrine\ORM\EntityManager; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; /** * @Route( * id="resource.create", * path="/resource", * methods={"POST"} * ) */ class ResourceCreateController implements MiddlewareInterface { /** * @Inject * * @var EntityManager */ protected $entityManager; /** * {@inheritDoc} */ public function process( ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface { $data = (array) $request->getParsedBody(); $response = $handler->handle($request); $resource = new Resource(); $resource->setTitle($data['title'] ?? null); $resource->setContent($data['content'] ?? null); $violations = $resource->validate(); if ($violations->count() > 0) { return $response->withStatus(400); } $this->entityManager->persist($resource); $this->entityManager->flush(); return $response->withStatus(201); } } 

The @Assert annotation is responsible for validation, the validation logic itself is described in the AbstractEntity class inherited by the model.


The implementation is of a demonstration nature, the purpose of the author is to reduce the number of lines of code ...


wheel bike


Application settings


FileDescription
config / cli-config.phpDoctrine CLI
config / container.phpPhp di
config / definitions.phpApplication dependencies
config / environment.phpApplication Environment Configuration

Your dependencies


To add a new dependency to the application, just open the file config / definitions.php and add a new dependency by analogy with the existing ones, after which it will be available through injections, as in the examples of this article.


Recommendations


After installing the skeleton, it is recommended to add the file config / environment.php to .gitignore , and duplicate the file itself as an example with a new name:


 cp config/environment.php config/environment.php.example 

You can go the other way by filling out the environment settings from .env using the symfony / dotenv package .


wheel bike


The game “why write it when you have it” can be played endlessly, but be that as it may, open-source can endure everything ...


To install the skeleton, use the following command:


 composer create-project sunrise/awesome-skeleton app 

To explore the source code of the skeleton, use the link: sunrise / awesome-skeleton .




I would like to express special thanks to Oscar Otero for his contribution to open-source, in particular for the excellent selection of Awesome PSR-15 Middleware , some of which are integrated into sunrise / awesome-skeleton .


')

Source: https://habr.com/ru/post/435434/


All Articles