flush not work after reset manager in doctrine - php

i have a error in my data and i get error out of range on a integer column and i try to prevent closed entity manager for proceeding work and for this purpose i reset manager in exception
public function renewDeliveryTime($delayReport) : void
{
try {
$this->delayReportRepository->updateRenewedDeliveryTimeAt($delayReport, 50000000);
}catch (\Exception $exception){
// out of range error
$this->managerRegistry->resetManager();
}
}
public function updateRenewedDeliveryTimeAt($delayReport,$delayDuration)
{
/**
* #var DelayReport $delayReport
*/
$delayReport->setDelayDuration($delayDuration);
$delayReport->setStatus(DelayReport::STATUS['DONE']);
$this->getEntityManager()->flush();
}
the problem is after i have another object and almost same operation in database but seems $this->getEntityManager()->flush() not work any more and nothing happens in database . it is related to $this->managerRegistry->resetManager()
public function enqueue($delayReport) : void
{
$this->pushInQueueReport($delayReport);
$this->delayReportRepository->updateStatus($delayReport, DelayReport::STATUS['IN_QUEUE']);
}
public function updateStatus($delayReport, $status)
{
/**
* #var DelayReport $delayReport
*/
$delayReport->setStatus($status);
$this->getEntityManager()->flush();
}
what is the problem and solution for this?

The problem with resetManager() is, that not all services wich has a reference to the entitymanager directly will be magically updated to have the new instance.
In updateStatus() Method you can easy check if your entity is Managed by the entity manager.
$uow = $this->getEntityManager()->getUnitOfWork();
if($uow->getEntityState($delayReport) !== UnitOfWork::STATE_MANAGED) {
// not managed
}
Dont know if a reassign can help here like $this->getEntityManager()->merge($delayReport).
BUT its really better to avoid a closed manager and validate your data before.
EDIT:
Not testet, if you will get the resetted EntityManager over the Registry. But its worth a try.
$entityManager = $managerRegistry->getManagerForClass(get_class($delayReport));

Related

How do I manage/close connections if I can't change max_user_connections variable?

I am using shared hosting that has "max user connections" set to 30. I've contacted them and they said they could not change this value. By surfing through my website I get an exception thrown that says:
SQLSTATE[42000]: Syntax error or access violation: 1203 User id12578877_odonto already has more than 'max_user_connections' active connections
Most of the actions in my web are just repositories (that have been generated with make:crud command) filtering some data. Upon changing between these pages rapidly for like 30s I get this error.
Is there any way I could reduce the number of connections or just basically anything I could do that would help?
Here is an example of how some of the actions in my controller look:
/**
* #Route("/", name="pacientai_index", methods={"GET"})
*/
public function index(PacientaiRepository $pacientaiRepository): Response
{
if($this->getUser()==null){
return $this->redirectToRoute('main');
}
if($this->getUser()->getRole()=="NEW"){
return $this->redirectToRoute('main');
}
if($this->getUser()->getMiestas()=="London"){
return $this->redirectToRoute('main');
}
return $this->render('pacientai/index.html.twig', [
'pacientais' => $pacientaiRepository->findByStatus("NEW"),
'sutarti' => $pacientaiRepository->findByStatus("SUTARTAS"),
]);
}
/**
* #Route("/mine", name="pacientai_index_mine", methods={"GET", "POST"})
*/
public function myPatients(PacientaiRepository $pacientaiRepository, UserRepository $userRepository): Response
{
if($this->getUser()==null){
return $this->redirectToRoute('main');
}
if($this->getUser()->getRole()=="NEW"){
return $this->redirectToRoute('main');
}
if($this->getUser()->getMiestas()=="London"){
return $this->redirectToRoute('main');
}
$entityManager = $this->getDoctrine()->getManager();
$entity = $this->getDoctrine()
->getRepository(User::class)
->find($this->getUser()->getId());
$entity->setYraNauju(0);
$entityManager->persist($entity);
$entityManager->flush();
return $this->render('pacientai/myPatients.html.twig');
}
Am I doing something wrong here that could cause this?
I got this error while just testing with one user. Would it get even worse if, let's say, 20 users were connected to the website?
If there are too may open connections, how could I make that all requests would be from one connection?
You can, after do whatever you need to do, do this:
$entityManager->getConnection()->close();
Or, if you have a class that handles that, you can actually put that code into the __destruct method, then when the garbage collector runs, it will also close the connection, like this:
public function __destruct()
{
$entityManager = $this->getDoctrine()->getManager();
$entityManager->getConnection()->close();
}

How to use Redis cache in Laravel?

It's my first time to use Laravel and Redis. I understand how to get, set, etc of Redis on Terminal. But no idea how to apply Redis on Laravel application.
I have application that saves participant's information in DB with MVC pattern. and I'd like to change it to use Redis cache to make it faster(and for practice). What do I have to do? Could you explain it with code?
This is ParticipantController. 'edit' function send user to edit page, and user edit the information and push 'save', it activate 'update' function. store/updateUserInput functions are just saving data to DB nothing else.
/**
* Show the form for editing the specified participant.
*
* #param int $id
* #return View
*/
public function edit(int $id): View
{
$participant = Participant::find($id);
if(empty($participant)){
return view('errors.404');
}
return view('participants.edit', ['participant'=>$participant]);
}
/**
* Update the specified participant in storage.
*
* #param ParticipantValidation $request
* #return RedirectResponse
*/
public function update(ParticipantValidation $request): RedirectResponse
{
$participant = Participant::find($request->id);
if(empty($participant)){
return view('errors.404');
}
$detail = $request->all();
Participant::updateUserInput($detail);
return redirect()->route('participants.create', $detail['event_id'])->with('success', 'Updated!');
}
+plus I tried this code on top of 'Controller' to use sth like $redis->set('message', 'Hello world'); but there's error that they cannot find 'Predis/Autoload.php'
require 'Predis/Autoload.php';
PredisAutoloader::register();
try {
$redis = new PredisClient();
}
catch (Exception $e) {
die($e->getMessage());
}
You can use the Cache facade
In your .env file, you must add CACHE_DRIVER=redis
Then whenever you want to get an instance of Participant:
$participant = null;
$key ="Participant".$id;
if(Cache::has($key)//get participant from cache
$participant = Cache::get($key);
else{//get participant and cache for 3 minutes
$participant = Participant::find($id);
$seconds = 180;
Cache::set($key, $participant, $seconds);
}

Save new Entity with relations on existed Entity (Symfony/Doctrine2)

I want to create new Entity SlideTranslation, and assign existed slide.
But every time entity had created without id of Slide. I can create SlideTranslation and than assign Slide to it, but it seems bad solution for me.
$slide = $em->getRepository('Model:Slide')->find($id);
if(isset($slide)) {
try {
$slideTranslation = new SlideTranslation();
$slideTranslation->setTranstable($slide);
$slideTranslation->setLocale('uk');
$slideTranslation->setAltText('Alt text');
$em->persist($slideTranslation);
$em->flush();
} catch (Exception $e) {
dump($e->getMessage());
}
}
Relations.
/**
* #ORM\ManyToOne(targetEntity="Model\Entity\Slide", inversedBy="tranlations")
* #ORM\JoinColumn(name="translatable_id", referencedColumnName="id")
*/
private $transtable;
I have tried method with getReference, but no result. Maybe I am breaking some patterns or principles and It's not possible in Doctrine2.
You will have to probably do it in the other way around
$slide = $em->getRepository('Model:Slide')->find($id);
$slideTranslation = new SlideTranslation();
$slideTranslation->setLocale('uk');
$slideTranslation->setAltText('Alt text');
$slide->addTranslation($slideTranslation);
$em->flush();
Then add cascade to the Slide entity, and you don't even need to persist the entity Translation
/**
* #ORM\OneToMany(targetEntity="Model\Entity\SlideTranslation", mappedBy="transtable", cascade={"persist", "remove"})
*/
private $translations;

doctrine 2 cascade persist saving too much

so this is my prePersist on EventListener
public function prePersist(LifecycleEventArgs $args)
{
//the first entity will have the PMP, so we catch it and continue to skip this if after this
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//here it says that i have created a new entity, need to persist it via cascade={"persist"}
$taxonomicClass->setPmp($this->pmp);
}
}
}
that's fine, i had added the annotation on it:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Pmp", cascade={"persist"})
* #ORM\JoinColumn(name="pmp_id", referencedColumnName="id", nullable=false)
**/
private $pmp;
and it saves everything from my hierarchy, even a new PMP, an object that already exist in the database!
what i want is that everything that im saving from my hierarchy needs to be related to the PMP that i passed, but when i set $taxonomicClass->setPmp($this->pmp); doctrine thinks that i created a new instance of PMP, since im not, i just want to this guy have an associaton with the PMP.
i tried put merge on the cascade option, but it only works with persist, how to make doctrine dont create a new instance, and instead use the one that i passed?
noticed my problem, i was assigning an attribute from memory, i should retrive him from database to doctrine understand.
public function prePersist(LifecycleEventArgs $args)
{
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//this solved the problem
$pmp = $args->getEntityManager()->getRepository("AppBundle:Pmp")->find($this->pmp->getId());
$taxonomicClass->setPmp($pmp);
}
}
}
i will keep in mind now that when a new entity is created, but it doesn't need to be saved, you must retrieve it from db, cascade={"persist"} wasn't even necessary

Laravel multi tenant migrate

Wondering if anyone knows how or of a package that allows migrations to be done on multiple databases without having the additional connection in the config file. Currently I have two different connections, one for the default and another for the tenants. The tenant connection has the credentials input, but it doesn't have the database name since each tenant will have a different database name. Problem is when I'm doing the initial setup and need the migrations to be run it will do it for the default or the tenant(if I edit the file to include the database name). I had found a package that would do it but it is not currently compatible with laravel 5. Obviously if I have to do it manually it can be done. It would just be a pain. Thanks in advance for suggestions.
Looking through the tenancy related questions here, I stumbled upon your open one.
What I've done is override the default MigrateCommand provided by Laravel and add the --tenant option. This will allow me to migrate one, more or all tenants. You can check the implementation out at my repository. Knowing the code of conduct here, I'll also add an example implementation:
<?php
namespace Hyn\MultiTenant\Commands\Migrate;
use Hyn\MultiTenant\Traits\TenantDatabaseCommandTrait;
use Illuminate\Database\Migrations\Migrator;
use PDOException;
class MigrateCommand extends \Illuminate\Database\Console\Migrations\MigrateCommand
{
use TenantDatabaseCommandTrait;
/**
* MigrateCommand constructor.
*
* #param Migrator $migrator
*/
public function __construct(Migrator $migrator)
{
parent::__construct($migrator);
$this->website = app('Hyn\MultiTenant\Contracts\WebsiteRepositoryContract');
}
public function fire()
{
// fallback to default behaviour if we're not talking about multi tenancy
if (! $this->option('tenant')) {
$this->info('No running tenancy migration, falling back on native laravel migrate command due to missing tenant option.');
return parent::fire();
}
if (! $this->option('force') && ! $this->confirmToProceed()) {
$this->error('Stopped no confirmation and not forced.');
return;
}
$websites = $this->getWebsitesFromOption();
// forces database to tenant
if (! $this->option('database')) {
$this->input->setOption('database', 'tenant');
}
foreach ($websites as $website) {
$this->info("Migrating for {$website->id}: {$website->present()->name}");
$website->database->setCurrent();
$this->prepareDatabase($website->database->name);
// The pretend option can be used for "simulating" the migration and grabbing
// the SQL queries that would fire if the migration were to be run against
// a database for real, which is helpful for double checking migrations.
$pretend = $this->input->getOption('pretend');
// Next, we will check to see if a path option has been defined. If it has
// we will use the path relative to the root of this installation folder
// so that migrations may be run for any path within the applications.
if (! is_null($path = $this->input->getOption('path'))) {
$path = $this->laravel->basePath().'/'.$path;
} else {
$path = $this->getMigrationPath();
}
try {
$this->migrator->run($path, $pretend);
} catch (PDOException $e) {
if (str_contains($e->getMessage(), ['Base table or view already exists'])) {
$this->comment("Migration failed for existing table; probably a system migration: {$e->getMessage()}");
continue;
}
}
// Once the migrator has run we will grab the note output and send it out to
// the console screen, since the migrator itself functions without having
// any instances of the OutputInterface contract passed into the class.
foreach ($this->migrator->getNotes() as $note) {
$this->output->writeln($note);
}
}
// Finally, if the "seed" option has been given, we will re-run the database
// seed task to re-populate the database, which is convenient when adding
// a migration and a seed at the same time, as it is only this command.
if ($this->input->getOption('seed')) {
$this->call('db:seed', ['--force' => true, '--tenant' => $this->option('tenant')]);
}
}
/**
* Prepare the migration database for running.
*
* #return void
*/
protected function prepareDatabase($connection = null)
{
if (! $connection) {
$connection = $this->option('database');
}
$this->migrator->setConnection($connection);
if (! $this->migrator->repositoryExists()) {
$options = ['--database' => $connection];
$this->call('migrate:install', $options);
}
}
/**
* #return array
*/
protected function getOptions()
{
return array_merge(
parent::getOptions(),
$this->getTenantOption()
);
}
}
If you have any questions to solve this issue, let me know.

Categories