How to upload files with Doctrine

Notice: This thread is very old.
Čamo
Member | 786
+
0
-

Hello,
I have a little problem with reopening entityManager after exception. I have a script to multiupload which saves the files in foreach. It looks like:

	public function insert( Array $images )
	{
		$module = $this->moduleRepository->findOneBy( [ 'name =' => 'blog' ] );
		$path = $this->wwwDir . '/images/app';

		if ( ! is_dir( $path ) && ! mkdir( $path, 0777 ) ) ...

		$result = [ 'errors' => [], 'saved_items' => [] ];
		foreach ( $images as $image )
		{
			if ( $image->isOk() )
			{
				$name = $image->getName();
				$sName = $image->getSanitizedName();
				$tmpName = $image->getTemporaryFile();

				$spl = new \SplFileInfo( $sName );
				$sName = $spl->getBasename( '.' . $spl->getExtension() ) . '.' . $spl->getExtension();

				$this->em->beginTransaction();

				try
				{
					try
					{
						$image = new Entity\Image();
						$image->create( [
							'name'   => $sName,
							'module' => $module,
						] );

						$this->em->persist( $image );
						$this->em->flush();
					}
					catch ( UniqueConstraintViolationException $e )
					{
						$result['errors'][] = 'Súbor s názvom ' . $name . ' už existuje.';
						$this->em->rollback();
						$this->reopenEm();
						continue;
					}

					$img = Image::fromFile( $tmpName );
					$x = $img->width;
					$y = $img->height;

					if ( $x > 900 || $y > 900 )
					{
						$img->resize( 1200, 1000 );  // Keeps ratio => one of the sides can be shorter, but none will be longer
					}
					$img->save( $path . '/' . $sName );

					$result['saved_items'][] = $name;
					$this->em->commit();
				}
				catch ( \Exception $e )
				{
					$this->em->rollback();
					$this->reopenEm();
					Debugger::log( $e->getMessage(), 'ERROR' );
					@unlink( $path . '/' . $sName );
					$result['errors'][] = 'Pri ukladaní súboru ' . $name . ' došlo k chybe. Súbor nebol uložený.';
				}
			}
			else
			{
				$result['errors'][] = 'Pri ukladaní súboru došlo k chybe.';
			}
		}

		return $result;

	}

If this script throws e.g. UniqueConstraintViolationException it close an entityManager. As I found out I must reopen it if I want to continue. It ensures reopenEm()

	protected function reopenEm()
	{
		$this->em = $this->em->create( $this->em->getConnection(), $this->em->getConfiguration() );
		$this->em->clear();
	}

Em is open, but as you can see there is a $module entity (image belongs to module via manyToOne) which makes me some trouble. After exception $module is detached from unitOfWork. I tried to merge it, persist it but merge does not work and persist makes me duplicates of modules with new ids.

How can I fix it to avoid duplicates of modules?

Last edited by Čamo (2016-03-09 17:49)

Čamo
Member | 786
+
0
-

So the problem is that I was calling

catch( \Exception $e )
{
	...
	$this->em->merge( $module );
}

but $module entity has to be reassigned to $module variable.

catch( \Exception $e )
{
	...
	$module = $this->em->merge( $module );
}

Only a merge call does not affect original entity. It RETURNS a copy.

Last edited by Čamo (2016-03-17 00:11)

amik
Member | 118
+
0
-

Generally, you should try to avoid exceptions in entity manager rather than trying to reopen it.

The only exception that is usually expected to happen is UniqueConstraintViolationException (which you also explicitly handle). You can avoid it this way:

  • start a transaction (which you already have started)
  • query for entity which the same unique key(s) that you are going to save
  • if it does exist, show error to user without exception happening inside entity manager
  • if it does not exist, persist and flush new one (completely safe as transaction ensures atomicity)

If any other exception happens, just let the application crash without trying to continue running entity manager and log it. As there is happening something really unexpected now, you should fail fast and examine the logged exception.

Čamo
Member | 786
+
0
-

Yes I could query entity with same name.