src/AppRemix/Logging/DatabaseActivitySubscriber.php line 49

Open in your IDE?
  1. <?php
  2. //----------------------------------------------------------------------
  3. // src/AppRemix/Logging/DatabaseActivitySubscriber.php
  4. //----------------------------------------------------------------------
  5. namespace App\AppRemix\Logging;
  6. use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
  7. use Doctrine\ORM\EntityManagerInterface;
  8. use Doctrine\ORM\Events;
  9. use Doctrine\Persistence\Event\LifecycleEventArgs;
  10. use Doctrine\Persistence\Proxy;
  11. use App\AppRemix\Logging\Objects\ClientLog;
  12. use App\AppRemix\Logging\Objects\DevisLog;
  13. use App\AppRemix\Logging\Objects\FileLog;
  14. use App\AppRemix\Logging\Objects\InvoiceLog;
  15. use App\AppRemix\Logging\Objects\MissionLog;
  16. use App\AppRemix\Logging\Objects\PaymentLog;
  17. use App\AppRemix\Logging\Objects\ProductLog;
  18. use App\AppRemix\Logging\Objects\PurchaseLog;
  19. use App\AppRemix\Logging\Objects\ReportLog;
  20. use App\AppRemix\Logging\Objects\SocietyLog;
  21. use App\AppRemix\Logging\Objects\OpenAIConfigLog;
  22. use App\AppRemix\Logging\Objects\OpenAIPriceLog;
  23. use App\AppRemix\Logging\Objects\SocietySettingsLog;
  24. use App\AppRemix\Logging\Objects\TemplateLog;
  25. use App\AppRemix\Logging\DeletionContextLogger;
  26. use App\AppRemix\Logging\Tools;
  27. use App\AppRemix\Entity\LoggableInterface;
  28. use App\Services\LogTools;
  29. class DatabaseActivitySubscriber implements EventSubscriberInterface
  30. {
  31.     private array $pendingLogs = [];
  32.     private array $avoidDuplicates = [];
  33.     public function __construct(EntityManagerInterface $em, LogTools $logTools, Tools $tools,
  34.         DeletionContextLogger $contextLogger,
  35.         SocietyLog $societyLog, MissionLog $missionLog, PurchaseLog $purchaseLog, ReportLog $reportLog, TemplateLog $templateLog, ProductLog $productLog, DevisLog $devisLog, InvoiceLog $invoiceLog, PaymentLog $paymentLog, FileLog $fileLog, SocietySettingsLog $societySettingsLog, OpenAIConfigLog $openAIConfigLog, ClientLog $clientLog, OpenAIPriceLog $openAIPriceLog)
  36.     {        
  37.         $this->em = $em;        
  38.         $this->logTools = $logTools;
  39.         $this->tools = $tools;
  40.         $this->contextLogger = $contextLogger;
  41.         $this->societyLog = $societyLog;
  42.         $this->missionLog = $missionLog;
  43.         $this->purchaseLog = $purchaseLog;
  44.         $this->reportLog = $reportLog;
  45.         $this->templateLog = $templateLog;
  46.         $this->productLog = $productLog;
  47.         $this->devisLog = $devisLog;
  48.         $this->invoiceLog = $invoiceLog;
  49.         $this->paymentLog = $paymentLog;
  50.         $this->fileLog = $fileLog;
  51.         $this->societySettingsLog = $societySettingsLog;
  52.         $this->openAIConfigLog = $openAIConfigLog;
  53.         $this->clientLog = $clientLog;
  54.         $this->openAIPriceLog = $openAIPriceLog;
  55.         $this->logServices = [            
  56.             "App\AppRemix\Entity\Society"    =>    $this->societyLog,
  57.             "App\AppRemix\Entity\Mission"    =>    $this->missionLog,
  58.             "App\AppRemix\Entity\Purchase"    =>    $this->purchaseLog,
  59.             "App\AppRemix\Entity\Report"    =>    $this->reportLog,
  60.             "App\AppRemix\Entity\Template"    =>    $this->templateLog,
  61.             "App\AppRemix\Entity\Product"    =>    $this->productLog,
  62.             "App\AppRemix\Entity\Devis"    =>    $this->devisLog,
  63.             "App\AppRemix\Entity\Invoice"    =>    $this->invoiceLog,
  64.             "App\AppRemix\Entity\Payment"    =>    $this->paymentLog,
  65.             "App\AppRemix\Entity\File"    =>    $this->fileLog,
  66.             "App\AppRemix\Entity\SocietySettings"    =>    $this->societySettingsLog,
  67.             "App\AppRemix\Entity\OpenAIConfig"    =>    $this->openAIConfigLog,
  68.             "App\AppRemix\Entity\OpenAIPrice"    =>    $this->openAIPriceLog,
  69.             "App\AppRemix\Entity\Client"    =>    $this->clientLog,
  70.         ];
  71.     }
  72.     // this method can only return the event names; you cannot define a
  73.     // custom method name to execute when each event triggers
  74.     public function getSubscribedEvents(): array
  75.     {
  76.         // Comment this to activate subscriber
  77.         // return [];
  78.         return [
  79.             Events::prePersist,
  80.             Events::preUpdate,
  81.             Events::preRemove,
  82.             Events::postFlush,
  83.         ];
  84.     }
  85.     // Do not return in this method, it breaks Doctrine cycles
  86.     // Called when an object is created
  87.     public function prePersist(LifecycleEventArgs $args): void
  88.     {
  89.         $data = $this->handleData("prePersist", $args);
  90.         if ($data !== null)
  91.         {
  92.             $entity = $data['entity'];
  93.             $entityClass = $data['entityClass'];
  94.             $logService = $data['logService'];
  95.             // Create logging args for object creation
  96.             $logArgs = $logService->logCreation($entity);
  97.             // Save log args for postFlush
  98.             $this->handleLogs("prePersist", $logArgs, $entity);
  99.         }
  100.     }
  101.     // Do not return in this method, it breaks Doctrine cycles
  102.     public function preUpdate(LifecycleEventArgs $args): void
  103.     {
  104.         $data = $this->handleData("preUpdate", $args);
  105.         if ($data !== null)
  106.         {
  107.             $entity = $data['entity'];
  108.             $entityClass = $data['entityClass'];
  109.             $logService = $data['logService'];
  110.             // Get the changes from the Unit of Work
  111.             $changes = $args->getEntityChangeSet();
  112.             // Create logging args for object modification
  113.             $logArgs = $logService->logChanges($entity, $changes);
  114.             // Save log args for postFlush
  115.             $this->handleLogs("prePersist", $logArgs, $entity);
  116.         }
  117.     }
  118.     // Do not return in this method, it breaks Doctrine cycles
  119.     public function preRemove(LifecycleEventArgs $args): void
  120.     {
  121.         $data = $this->handleData("preRemove", $args);
  122.         if ($data !== null)
  123.         {
  124.             $entity = $data['entity'];
  125.             $entityClass = $data['entityClass'];
  126.             $logService = $data['logService'];
  127.             // Create logging args for object removal
  128.             $logArgs = $logService->logRemoval($entity);
  129.             // Save log args for postFlush
  130.             $this->handleLogs("preRemove", $logArgs, $entity);
  131.         }
  132.     }
  133.     // Specific Installment case (and also Attachment, Address etc ...)
  134.     // Some entities are logged not as an object, but attached to another object
  135.     // An Installment related to an Invoice will have log.objectId = installment.invoice.id
  136.     // However, in those cases, log.objectId is not null even in prePersist
  137.     // since we are prePersisting the newly created installment,
  138.     // but actually logging for an Invoice that is already created.
  139.     // So the main condition if (empty($log->getObjectId()))
  140.     // is never true for such cases.
  141.     // So no error and no problem ;)
  142.     public function postFlush(): void
  143.     {
  144.         if (!empty($this->pendingLogs))
  145.         {
  146.             // Handle logs of objects that have been created => Update ID now that we have it
  147.             foreach ($this->pendingLogs as $logData)
  148.             {
  149.                 $log = $logData['log'];
  150.                 $object = $logData['object'];
  151.                 if (empty($log->getObjectId()))
  152.                 {
  153.                     $log->setObjectId($object->getId());
  154.                 }                
  155.                 // Handle Special Cases
  156.                 $this->tools->handleNewSociety($log);
  157.                 $this->tools->handleMission($log);
  158.                 $this->tools->handleClient($log, $object);
  159.                 $this->em->persist($log);
  160.             }
  161.             // Reset arrays
  162.             $this->pendingLogs = [];
  163.             try
  164.             {
  165.                 // Save data to database
  166.                 $this->em->flush();
  167.             }
  168.             catch (\Exception $e)
  169.             {
  170.                 $allGood = false;
  171.                 $this->logTools->errorlog($e->getMessage());
  172.                 $this->logTools->errorlog_db($e);
  173.             }
  174.             $this->contextLogger->clear();
  175.         }
  176.         // Reset duplicates array
  177.         $this->avoidDuplicates = [];
  178.     }
  179.     protected function handleData($caller, $args)
  180.     {
  181.         // Get Entity and Entity class
  182.         $entity = $args->getObject();
  183.         // Avoid getting Proxies\__CG__\App\Entity\NameOfEntity
  184.         // instead of App\Entity\NameOfEntity
  185.         $entityClass = $entity instanceof Proxy
  186.         ? get_parent_class($entity)
  187.         : get_class($entity);
  188.         // Optionally ignore certain entity types (like internal logs themselves)
  189.         if (!$entity instanceof LoggableInterface)
  190.         {
  191.             return null;
  192.         }
  193.         if (!array_key_exists($entityClass, $this->logServices))
  194.         {
  195.             return null;
  196.         }
  197.         // Avoid duplicates
  198.         // I really have no idea if this is necessary
  199.         // And I really hope it does harm either
  200.         $hash = spl_object_hash($entity);
  201.         if (in_array($hash, $this->avoidDuplicates))
  202.         {
  203.             return null;
  204.         }
  205.         $this->avoidDuplicates[] = $hash;
  206.         // Get the corresponding Log Service
  207.         $logService = $this->logServices[$entityClass];
  208.         // Check to see if the Entity has some info stored
  209.         $info = null;
  210.         if (method_exists($entity, "getLoggingInfo"))
  211.         {
  212.             $info = $entity->getLoggingInfo();
  213.         }
  214.         return array(
  215.             'entity'        =>    $entity,
  216.             'entityClass'    =>    $entityClass,
  217.             'logService'    =>    $logService,
  218.             'info'            =>    $info,
  219.         );
  220.     }
  221.     protected function handleLogs($caller, $logArgs, $entity)
  222.     {
  223.         foreach ($logArgs as $key => $args)
  224.         {
  225.             $log = $this->tools->smartLog($args);
  226.             $this->pendingLogs[] = array(
  227.                 'log'        =>    $log,
  228.                 'object'    =>    $entity,
  229.             );
  230.         }
  231.     }
  232. }