📜 ⬆️ ⬇️

Phalcon PHP freymork. Work with annotations

"Vivo, presto, prestissimo ..."

About Phalcon is still not enough materials, but the framework is quite interesting and worthy of attention. One of Phalcon's interesting solutions is its advanced annotation usage. The parser is written in C, it works very fast. Allows you to easily and naturally transfer part of the settings (almost most) from the configuration files to the code.


')

Part I. Vivo (Fast).


Assigning routes to Phalcon is quite a “creative” task.
Many examples are full of different route assignments.
On Habré even skipped an example of using xml files ...
And this is at a time when many frameworks offer routing through annotations.
And what about Phalcon?
Phalcon modestly and quietly indicates in the documentation that routing on annotations is possible.
And for this, the Phalcon authors simply created an annotation parser in C.
First!
What do you need to include annotations?
Nothing at all.
In the bootstrap file we implement the annotation service, just a few lines of code. And that's all.
... //set routers $di->set('router', function() { $router = new \Phalcon\Mvc\Router\Annotations(false); $router->removeExtraSlashes(true); $router->setUriSource(\Phalcon\Mvc\Router::URI_SOURCE_SERVER_REQUEST_URI); $router->addResource('Index', "/"); $router->notFound([ "controller" => "index", "action" => "page404" ]); return $router; }); ... 

Now in the routes (including prefixes), we indicate directly in the controller.
 ... /** * @Post("create") */ public function createAction() { /... } ... 

For more details on routing (request types, parameters), see the documentation.

Part II. Presto (Fast as possible)


Routing on annotations is, of course, good. But we still have to deal with data in projects. And here you can see one feature of Phalcon.
When working with a database, it usually makes 3 requests.
The 1st checks the presence of a table in the database.
The 2nd gets the table metadata.
3rd request directly.
I do not know how it is right, I will not argue. But somewhat uncomfortable.
We do not need 3 requests. We would like to have table metadata somewhere in store.
In the cache, for example.
And Phalcon is of the same opinion. Therefore, it suggests storing metadata in the cache. In this case, you can use annotations.
Again DI, again botstrap:
 ... //Set a models manager $di->set('modelsManager', new \Phalcon\Mvc\Model\Manager()); //Set the models cache service $di->set('modelsCache', function() { //Cache data for one day by default $frontCache = new \Phalcon\Cache\Frontend\Data([ "lifetime" => 86400 ]); $cache = new \Phalcon\Cache\Backend\Memcache($frontCache, [ "host" => "localhost", "port" => "11211", 'persistent' => TRUE, ]); return $cache; }); $di->set('modelsMetadata', function() { // Create a meta-data manager with APC //$metaData = new \Phalcon\Mvc\Model\MetaData\Apc([ // "lifetime" => 86400, // "prefix" => "general-phsql" //]); $metaData = new \Phalcon\Mvc\Model\MetaData\Memory([ 'prefix' => 'general', ]); $metaData->setStrategy(new StrategyAnnotations()); return $metaData; }); ... 

After that, metadata, read only once, is stored in the cache.
At the time of development, we can switch to storage in memory.

Here, in principle, everything. Again, more detail in the documentation.

Well, there is nothing surprising in these mechanisms. Except that it works very fast. As quickly as it can be achieved in a component created in C. That is, almost imperceptibly.
But these mechanisms also exist in other frameworks.
We want something tasty, some raisin ...

Part III. Prestissimo (Even faster).


Probably, it is no secret that when designing a site, you have to write admin panel.
And these are forms, forms, and once again forms. Many forms. And this is tiring ...
I want automation.
What does the form consist of? Of course, the key element is the input tag.
It is usually of type type, length length, pattern fill pattern, etc.
And all this for each form must be specified ... For each field of the table ...
I want automation. You do not want to write a lot of code.
And describe the universal form for any entity.
And here again we will need annotations. Parser, again, on C.
Phalcon offers the Phalcon \ Forms \ Form component.
Take the simplest Users table. Her model:
 <?php namespace Frontend\Model; class Users extends \Phalcon\Mvc\Model { /** * @Primary * @Identity * @Column(type="integer", nullable=false) * @FormOptions(type=hidden) */ public $id; /** * @Column(type="string", nullable=false) * @FormOptions(type=text, length=32) */ public $name; /** * @Column(type="integer", nullable=false) * @FormOptions(type=email) */ public $email; /** * @Column(type="integer", nullable=false) * @FormOptions(type=text, length=9, pattern='[0-9]{9}') */ public $indcode; } 

Here we apply our own annotation, where we specify the options we need to build the form.
Yes, we have invented this annotation now, ourselves, for use in a particular case.
We have this @FormOptions. In the type attribute, we specify the type of field we need for input.
Phalcon offers the following types of Phalcon \ Forms \ Element :
Phalcon \ Forms \ Element \ Check
Phalcon \ Forms \ Element \ Date
Phalcon \ Forms \ Element \ Email
Phalcon \ Forms \ Element \ File
Phalcon \ Forms \ Element \ Hidden
Phalcon \ Forms \ Element \ Numeric
Phalcon \ Forms \ Element \ Password
Phalcon \ Forms \ Element \ Select
Phalcon \ Forms \ Element \ Submit
Phalcon \ Forms \ Element \ Text
Phalcon \ Forms \ Element \ TextArea
More than enough.
It remains the case for small ...
We need to somehow “teach” Phalcon to recognize our annotations ...
Nothing is easier!
bootstrap - enable annotation parser.
 ... //Annotations $di->set('annotations', function() { return new \Phalcon\Annotations\Adapter\Memory(); }); ... 

During the development process, you can use a memory adapter,
in production, you can switch to storage in files, APC, XCache.
Now create a form class for any entity.
Form class
 <?php use Phalcon\Forms\Form, \Phalcon\Forms\Element\Submit as Submit; class EntityForm extends Form { public $fields = []; private $classprefix = '\\Phalcon\\Forms\\Element\\'; public $action; /** * @param object $model, action */ public function initialize($model, $action) { $this->action = $action; //      $object = $model; $this->setEntity($object); //   $attributes = $this->modelsMetadata->getAttributes($object); //     $metadata = $this->annotations->get($object); //   @FormOptions foreach ( $attributes as $attribute ) { $this->fields[$attribute] = $metadata ->getPropertiesAnnotations()[$attribute] ->get('FormOptions') ->getArguments(); } //       foreach ($this->fields as $field => $type) { $fieldtype = array_shift($type); //  type       $fieldclass = $this->classprefix.$fieldtype; $this->add(new $fieldclass($field, $type)); // label     if ( $fieldtype !== 'hidden') { $this->get($field)->setLabel($this->get($field)->getName()); } } //    $this->add(new Submit('submit',[ 'value' => 'Send', ])); } public function renderform() { echo $this->tag->form([ $this->action, 'id' => 'actorform', ]); //fill form tags foreach ($this as $element) { // collect messages $messages = $this->getMessagesFor($element->getName()); if (count($messages)) { // each element render echo '<div class="messages">'; foreach ($messages as $message) { echo $message; } echo '</div>'; } echo '<div>'; echo '<label for="', $element->getName(), '">', $element->getLabel(), '</label>'; echo $element; echo '</div>'; } echo $this->tag->endForm(); } } 


Here, when we initialize the EntityForm class, we read the metadata of the passed object and its annotation.
After that we implement all the necessary fields in the form.
The renderform function simply displays our form in a browser.

Let's go back to the controller, and create a form output action:
 ... /** * @Get("form") */ public function formAction() { $myform = new EntityForm(new Users(), 'create'); $this->view->setVars([ 'myform' => $myform, ]); } ... 

and recipient:
 ... /** * @Post("create") */ public function createAction() { echo '<pre>'; var_dump($_POST); echo '</pre>'; } ... 

It remains only to display the form in the output template (Volt):
{{ myform.renderform() }}
That's all.
Of course, you need to add to the class form CSRF protection, data validation, preservation.
But the task of this article is to show the simplicity and usability of annotations in Phalcon.
These features are provided to us through the powerful and fast annotation parser PhalconPHP.
And when you start using Phalcon, you understand that it is really fast.
And not only in the output “Hello, world!”.
The speed and convenience of working with Phalcon is really amazing.
index.php
 <?php use Phalcon\Mvc\View\Engine\Volt; use Phalcon\Mvc\Model\MetaData\Strategy\Annotations as StrategyAnnotations; try { //Register an autoloader $loader = new \Phalcon\Loader(); $loader->registerDirs([ '../app/controllers/', '../app/models/', '../app/forms/' ]); $loader->registerNamespaces([ 'Frontend\\Model' => __DIR__.'/../app/models/', ]); $loader->register(); //Create a DI $di = new \Phalcon\DI\FactoryDefault(); //Set a models manager $di->set('modelsManager', new \Phalcon\Mvc\Model\Manager()); //Set the models cache service $di->set('modelsCache', function() { //Cache data for one day by default $frontCache = new \Phalcon\Cache\Frontend\Data([ "lifetime" => 86400 ]); $cache = new \Phalcon\Cache\Backend\Memcache($frontCache, [ "host" => "localhost", "port" => "11211", 'persistent' => TRUE, ]); return $cache; }); $di->set('modelsMetadata', function() { // Create a meta-data manager with APC //$metaData = new \Phalcon\Mvc\Model\MetaData\Apc([ // "lifetime" => 86400, // "prefix" => "general-phsql" //]); $metaData = new \Phalcon\Mvc\Model\MetaData\Memory([ 'prefix' => 'general', ]); $metaData->setStrategy(new StrategyAnnotations()); return $metaData; }); //SQL profiler $di->set('profiler', function(){ return new \Phalcon\Db\Profiler(); }, true); //set database connection $di->set('db', function() use ($di) { $eventsManager = new \Phalcon\Events\Manager(); //Get a shared instance of the DbProfiler $profiler = $di->getProfiler(); //Listen all the database events $eventsManager->attach('db', function($event, $connection) use ($profiler) { if ($event->getType() == 'beforeQuery') { $profiler->startProfile($connection->getSQLStatement()); } if ($event->getType() == 'afterQuery') { $profiler->stopProfile(); } }); $connection = new \Phalcon\Db\Adapter\Pdo\Mysql([ "host" => "localhost", "username" => "root", "password" => "12345", "dbname" => "general" ]); //Assign the eventsManager to the db adapter instance $connection->setEventsManager($eventsManager); return $connection; }); //Register Volt as a service $di->set('voltService', function($view, $di) { $volt = new Volt($view, $di); $volt->setOptions([ "compiledPath" => "../app/cache/", ]); return $volt; }); //Setting up the view component $di->set('view', function(){ $view = new \Phalcon\Mvc\View(); $view->setViewsDir('../app/views/'); $view->registerEngines([ ".volt" => 'voltService' ]); return $view; }); //Create Form manager $di->set('forms', function() { $forms = new \Phalcon\Forms\Manager(); return $forms; }); $di->set('session', function() use($di) { $session = new Phalcon\Session\Adapter\Files(); $session->setoptions([ 'uniqueId' => 'privatRsc', ]); $session->start(); return $session; }); //set routers $di->set('router', function() { $router = new \Phalcon\Mvc\Router\Annotations(false); $router->removeExtraSlashes(true); $router->setUriSource(\Phalcon\Mvc\Router::URI_SOURCE_SERVER_REQUEST_URI); $router->addResource('Index', "/"); $router->notFound([ "controller" => "index", "action" => "page404" ]); return $router; }); //Annotations $di->set('annotations', function() { return new \Phalcon\Annotations\Adapter\Memory(); }); //Handle the request $application = new \Phalcon\Mvc\Application($di); echo $application->handle()->getContent(); } catch(\Phalcon\Exception $e) { echo "PhalconException: ", $e->getMessage(); } 


IndexController.php
 <?php use \Frontend\Model\Users as Users; /** * @RoutePrefix("/") **/ class IndexController extends \Phalcon\Mvc\Controller { /** * @Get("") */ public function indexAction() { echo <h3>Index Action</h3>; } /** * @Get("form") */ public function formAction() { $myform = new EntityForm(new Users(), 'create'); $this->view->setVars([ 'myform' => $myform, ]); } /** * @Post("create") */ public function createAction() { echo '<pre>'; var_dump($_POST); echo '</pre>'; } /** * @Get("page404") */ public function page404Action() { echo '404 - route not found'; } } 


EntityForm.php
 <?php use Phalcon\Forms\Form, \Phalcon\Forms\Element\Submit as Submit; class EntityForm extends Form { public $fields = []; private $classprefix = '\\Phalcon\\Forms\\Element\\'; public $action; /** * @param object $model, action */ public function initialize($model, $action) { $this->action = $action; //Set fields options from annotations $object = $model; $this->setEntity($object); $attributes = $this->modelsMetadata->getAttributes($object); $metadata = $this->annotations->get($object); foreach ( $attributes as $attribute ) { $this->fields[$attribute] = $metadata ->getPropertiesAnnotations()[$attribute] ->get('FormOptions') ->getArguments(); } foreach ($this->fields as $field => $type) { $fieldtype = array_shift($type); $fieldclass = $this->classprefix.$fieldtype; $this->add(new $fieldclass($field, $type)); if ( $fieldtype !== 'hidden') { $this->get($field)->setLabel($this->get($field)->getName()); } } $this->add(new Submit('submit',[ 'value' => 'Send', ])); } public function renderform() { echo $this->tag->form([ $this->action, 'id' => 'actorform', ]); //fill form tags foreach ($this as $element) { // collect messages $messages = $this->getMessagesFor($element->getName()); if (count($messages)) { // each element render echo '<div class="messages">'; foreach ($messages as $message) { echo $message; } echo '</div>'; } echo '<div>'; echo '<label for="', $element->getName(), '">', $element->getLabel(), '</label>'; echo $element; echo '</div>'; } echo $this->tag->endForm(); } } 


Users.php
 <?php namespace Frontend\Model; class Users extends \Phalcon\Mvc\Model { /** * @Primary * @Identity * @Column(type="integer", nullable=false) * @FormOptions(type=hidden) */ public $id; /** * @Column(type="string", nullable=false) * @FormOptions(type=text, length=32) */ public $name; /** * @Column(type="integer", nullable=false) * @FormOptions(type=email) */ public $email; /** * @Column(type="integer", nullable=false) * @FormOptions(type=text, length=9, pattern='[0-9]{9}') */ public $indcode; } 


form.volt
 <h2>Test form in Volt</h2> <hr> {{ myform.renderform() }} <hr> 

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


All Articles