📜 ⬆️ ⬇️

Several "buns" for symfony 2 && Doctrine

This article will discuss how you can solve some problems in Symfony 2 and Doctrine using the basic components out of the box, namely:


I think many who worked with Symfony 2 faced the problem of introducing a container service into a model (Entity) or some other component (service). Before you know how to do this, consider whether you need it? You can create a specific service (utility) and do everything that is needed there. Well, if you decide that this is necessary, then:

Bun â„–1 The introduction of service in the model:


There are several ways to implement, and often all choose the easiest.
')
Option number 1

Take and "stupidly" call the kernel from the global scope

class MyEntity { // ... properties // ... methods public function someAction() { global $kernel; return $kernel->getContainer() ->..... } } 

As for me, the method is very simple, but also very leaky , since it is impossible to verify the existence of the same variable ...

Option number 2

Here it will be necessary to “sweat” a little for a simple function, but this solution will be much better than the first one and guarantees full performance. Here will be used events doctrines that allow us to do anything during the load / save model.

To begin, we will need to create either Event or Subscriber for Doctrine, which will be implemented by the necessary component during model loading ( postLoad ). And we will need to create this whole thing as a service so that you can transfer any service.

Create a Subscriber for Doctrine:

 namespace Acme\DemoBundle\EventListener; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\LifecycleEventArgs; use Symfony\Component\DependencyInjection\ContainerInterface; use Acme\DemoBundle\Entity\MyEntity; class MyEntitySubscriber implements EventSubscriber { protected $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function postLoad(LifecycleEventArgs $event) { $entity = $event->getEntity(); //  ,   ,       , //     if ($entity instanceof MyEntity) { $entity-> setMyComponent($this->container->get('....')); } } /** * ,       * :    -    * ,    */ public function getSubscribedEvents() { return array( 'postLoad' ); } } 

Here is the new model:

 class MyEntity { // … properties protected $myComponent; // … methods public function setMyComponent($myComponent) { $this->myComponent = $myComponent; } public function someAction() { return $this->myComponent ->.... } } 

Register as a service

 <service id="my_entity.doctrine.subscriber" class="Acme\DemoBundle\EventListener\MyEntitySubscriber"> <!--    ,     subscriber --> <tag name="doctrine.event_subscriber" /> <argument type="service" id="service_container" /> </service> 

Well, that's all, when the model is loaded, the system will check if this model is necessary, and if so, “plug in” the necessary component into the myComponent variable using a separate setter.

Conclusion: try to avoid such cases, since the models are only for reading / writing in the database.

Bun number 2 Saving history:


Sometimes you have to keep the entire history of a change in a certain model, for example, the record change history for further rollback. Events of the doctrine itself, namely onFlush , will help greatly in this situation .

Suppose we have a “news” model that we will need to follow, namely, the title field:

 class News { protected $title; // ... properties // ... methods } 

Well, in the dogonku, you will need to create another model, which just will control the changes:

 class NewsHistory { protected $title; // ... properties // ... methods } 

At the event itself, it will be necessary to check whether this field has changed. This will help UnitOfWork , which carefully monitors all the models in a particular manager.

 namespace Acme\DemoBundle\EventListener; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\OnFlushEventArgs; use Symfony\Component\DependencyInjection\ContainerInterface; use Acme\DemoBundle\Entity\News; use Acme\DemoBundle\Entity\NewsHistory; class NewsSubscriber implements EventSubscriber { public function onFlush(OnFlushEventArgs $event) { $em = $event->getEntityManager(); $uow = $em->getUnitOfWork(); //    ,    foreach ($uow->getScheduledEntityUpdates() as $entity) { //    if ($entity instanceof News) { //    ,   $changeSet = $uow->getEntityChangeSet($entity); //     title? if (isset($changeSet['title'])) { //      list ($oldTitle, $newTitle) = $changeSet['title']; //  entity    $newsHistory = new NewsHistory; $newsHistory->setTitle($oldTitle); //      $em->persist($newsHistory); // !!! //        ,  //  ,   ,  flush  //  ,     ,   // ,   . //     UnitOfWork,    , //       //    ClassMetadata   //    ""     $classMetadata = $em->getClassMetadata(get_class($newsHistory)); //    ""  UnitOfWork,      $uow->computeChangeSet($classMetadata, $newsHistory); } } } } public function getSubscribedEvents() { return array( 'onFlush' ); } } 

Well, as always, you need to register as a service, indicating that this is a subscriber for the doctrine:

 <service id="news.doctrine.subscriber" class="Acme\DemoBundle\EventListener\NewsSubscriber"> <tag name="doctrine.event_subscriber" /> </service> 

UnitOfWork is the so-called Api , with which you can do any machinations with models.
In the same way, you can control the creation of new models and the removal of old ones.

Bun number 3 Disable SQL logger, cleaning the cache in Doctrine:


Sometimes there is a need to run commands (from the console) that make so many queries to the database. A good example is the transfer of a site from a CMS or a firewall to Symfony 2 in the presence of more than 100,000 entries in the database. If you run a command on the Symphony from the console, then by default, the system will start in dev mode and with the debug enabled, in which the doctrine saves all requests for subsequent debag. As a result, the memory allocated for the script is constantly increasing, which subsequently affects the speed and can throw out the critical memory limit error. In such situations, we can disable all the loggers that the doctrine (manager) uses, or completely redefine to control only what is needed. The logger itself hangs in the configuration of the doctrine.

An example of a complete shutdown:
Php
 $container->get('doctrine') ->getManager() ->getConnection() ->getConfiguration() ->setSQLLogger(null); //      


Configuration
doctrine:
dbal:
connections:
default:
logging: false


You should also not forget that all models that were loaded from the database are saved in the manager until unset is called or the “static” cache is reset. It was designed not to constantly make queries on a sample of the same model. As a result, with such large scripts (which do a lot of operations with models), it is necessary to periodically clean the cache:

 $container->get('doctrine')->getManager()->clear(); 

Bun # 4 Separation of console environments:


When raising a new Symphony assembly, we always encounter the problem of some folders ( Permission denied ): app / cache and app / logs . This problem occurs when you have already launched the site from a browser and are trying to do something in the console. The problem is that the web server itself works under a different user.
Those who have full access to the server (root), this is not necessary, look better: http://symfony.com/doc/current/book/installation.html#configuration-and-setup
But if you do not have full access to the system (for example, hosting) and you already have this problem, then you can divide the startup system on dev and console. This is done as follows:

  1. Create an app / config / config_console.yml file into which we import the config_dev.yml config
  2. In the app / console file, by default, we set not dev and console
  3. Correct the app / AppKernel.php file in the place where additional bundles are connected (environment is being checked) and add another console

Now when you start app / console, the system will work under a completely different environment, which will not interfere with the dev.
Warning : this is only if the system is in dev mode!

PS Codes were written for example, and not to work on prod systems. Symfony 2.2 was used.
PSS Please do not kick much ... My first post. Thanks for attention!

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


All Articles