Przeglądaj źródła

[Uploadable] Simplified usage of extension and added some missing functionalities (allowOverwrite, appendNumber, etc).

comfortablynumb 13 lat temu
rodzic
commit
48b42927a9

+ 20 - 0
lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Gedmo\Exception;
+
+use Gedmo\Exception;
+
+/**
+ * UploadableFileAlreadyExistsException
+ *
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Exception
+ * @subpackage UploadableFileAlreadyExistsException
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class UploadableFileAlreadyExistsException
+    extends RuntimeException
+    implements Exception
+{}

+ 1 - 1
lib/Gedmo/Mapping/Annotation/All.php

@@ -32,8 +32,8 @@ include __DIR__.'/TreeRight.php';
 include __DIR__.'/TreeRoot.php';
 include __DIR__.'/Versioned.php';
 include __DIR__.'/Uploadable.php';
+include __DIR__.'/UploadableFileInfo.php';
 include __DIR__.'/UploadableFileMimeType.php';
 include __DIR__.'/UploadableFilePath.php';
 include __DIR__.'/UploadableFileSize.php';
 include __DIR__.'/UploadablePath.php';
-include __DIR__.'/UploadableFilesArrayIndex.php';

+ 5 - 1
lib/Gedmo/Mapping/Annotation/Uploadable.php

@@ -21,7 +21,11 @@ final class Uploadable extends Annotation
 {
     /** @var boolean */
     public $allowOverwrite = false;
+
     /** @var boolean */
-    public $appendNumber = true;
+    public $appendNumber = false;
+
+    /** @var string */
+    public $path = '';
 }
 

+ 4 - 4
lib/Gedmo/Mapping/Annotation/UploadableFilesArrayIndex.php

@@ -5,19 +5,19 @@ namespace Gedmo\Mapping\Annotation;
 use Doctrine\Common\Annotations\Annotation;
 
 /**
- * UploadableFilexArrayIndex Annotation for Uploadable behavioral extension
+ * UploadableFileInfo Annotation for Uploadable behavioral extension
  *
  * @Annotation
- * @Target("METHOD")
+ * @Target("PROPERTY")
  *
  * @author Gustavo Falco <comfortablynumb84@gmail.com>
  * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  * @package Gedmo.Mapping.Annotation
- * @subpackage UploadableFilesArrayIndex
+ * @subpackage UploadableFileInfo
  * @link http://www.gediminasm.org
  * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-final class UploadableFilesArrayIndex extends Annotation
+final class UploadableFileInfo extends Annotation
 {
 }
 

+ 20 - 5
lib/Gedmo/Uploadable/Mapping/Driver/Annotation.php

@@ -29,7 +29,7 @@ class Annotation implements AnnotationDriverInterface
     const UPLOADABLE_FILE_MIME_TYPE = 'Gedmo\\Mapping\\Annotation\\UploadableFileMimeType';
     const UPLOADABLE_FILE_PATH = 'Gedmo\\Mapping\\Annotation\\UploadableFilePath';
     const UPLOADABLE_FILE_SIZE = 'Gedmo\\Mapping\\Annotation\\UploadableFileSize';
-    const UPLOADABLE_FILES_ARRAY_INDEX = 'Gedmo\\Mapping\\Annotation\\UploadableFilesArrayIndex';
+    const UPLOADABLE_FILE_INFO = 'Gedmo\\Mapping\\Annotation\\UploadableFileInfo';
     const UPLOADABLE_PATH = 'Gedmo\\Mapping\\Annotation\\UploadablePath';
 
     /**
@@ -77,7 +77,7 @@ class Annotation implements AnnotationDriverInterface
             $config['uploadable'] = true;
             $config['allowOverwrite'] = $annot->allowOverwrite;
             $config['appendNumber'] = $annot->appendNumber;
-            $config['filesArrayIndexMethod'] = '';
+            $config['path'] = $annot->path;
             $config['pathMethod'] = '';
             $config['fileMimeTypeField'] = false;
             $config['filePathField'] = false;
@@ -101,6 +101,10 @@ class Annotation implements AnnotationDriverInterface
 
                     Validator::validateFileSizeField($meta, $config['fileSizeField']);
                 }
+
+                if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_INFO)) {
+                    $config['fileInfoField'] = $prop->getName();
+                }
             }
 
             if (!$config['filePathField']) {
@@ -109,14 +113,25 @@ class Annotation implements AnnotationDriverInterface
                 ));
             }
 
+            if (!$config['fileInfoField']) {
+                throw new InvalidMappingException(sprintf('Class "%s" must have an UploadableFileInfo field.',
+                    $class->getName()
+                ));
+            }
+
             foreach ($class->getMethods() as $method) {
                 if ($this->reader->getMethodAnnotation($method, self::UPLOADABLE_PATH)) {
                     $config['pathMethod'] = $method->getName();
                 }
+            }
 
-                if ($this->reader->getMethodAnnotation($method, self::UPLOADABLE_FILES_ARRAY_INDEX)) {
-                    $config['filesArrayIndexMethod'] = $method->getName();
-                }
+            if ($config['path'] && $config['pathMethod'] === '') {
+                $msg = 'You need to define the path in the %s annotation, or add a method with %s annotation.';
+
+                throw new InvalidMappingException(sprintf($msg,
+                    self::UPLOADABLE,
+                    self::UPLOADABLE_PATH
+                ));
             }
         } else {
             // We need to check if this class has a relation with Uploadable entities

+ 7 - 3
lib/Gedmo/Uploadable/Mapping/Validator.php

@@ -18,6 +18,10 @@ use Gedmo\Exception\InvalidMappingException;
 
 class Validator 
 {
+    const UPLOADABLE_FILE_MIME_TYPE = 'UploadableFileMimeType';
+    const UPLOADABLE_FILE_PATH = 'UploadableFilePath';
+    const UPLOADABLE_FILE_SIZE = 'UploadableFileSize';
+
     /**
      * List of types which are valid for UploadableFileMimeType field
      *
@@ -48,17 +52,17 @@ class Validator
 
     public static function validateFileMimeTypeField(ClassMetadataInfo $meta, $field)
     {
-        self::validateField($meta, $field, 'UploadableFileMimeType', self::$validFileMimeTypeTypes);
+        self::validateField($meta, $field, self::UPLOADABLE_FILE_MIME_TYPE, self::$validFileMimeTypeTypes);
     }
 
     public static function validateFilePathField(ClassMetadataInfo $meta, $field)
     {
-        self::validateField($meta, $field, 'UploadableFilePath', self::$validFilePathTypes);
+        self::validateField($meta, $field, self::UPLOADABLE_FILE_PATH, self::$validFilePathTypes);
     }
 
     public static function validateFileSizeField(ClassMetadataInfo $meta, $field)
     {
-        self::validateField($meta, $field, 'UploadableFileSize', self::$validFileSizeTypes);
+        self::validateField($meta, $field, self::UPLOADABLE_FILE_SIZE, self::$validFileSizeTypes);
     }
 
     public static function validateField($meta, $field, $uploadableField, $validFieldTypes)

+ 58 - 102
lib/Gedmo/Uploadable/UploadableListener.php

@@ -16,7 +16,8 @@ use Doctrine\Common\Persistence\ObjectManager,
     Gedmo\Exception\UploadableIniSizeException,
     Gedmo\Exception\UploadableNoFileException,
     Gedmo\Exception\UploadableNoTmpDirException,
-    Gedmo\Exception\UploadableUploadException;
+    Gedmo\Exception\UploadableUploadException,
+    Gedmo\Exception\UploadableFileAlreadyExistsException;
 
 /**
  * Uploadable listener
@@ -104,18 +105,21 @@ class UploadableListener extends MappedEventSubscriber
     public function processFile(UnitOfWork $uow, AdapterInterface $ea, ClassMetadata $meta, array $config, $object, $action)
     {
         $refl = $meta->getReflectionClass();
-        $pathMethod = $refl->getMethod($config['pathMethod']);
-        $pathMethod->setAccessible(true);
-        $path = $pathMethod->invoke($object);
-        $path = substr($path, strlen($path) - 1) === '/' ? substr($path, 0, strlen($path) - 2) : $path;
-        $indexMethod = $refl->getMethod($config['filesArrayIndexMethod']);
-        $indexMethod->setAccessible(true);
-        $index = $indexMethod->invoke($object);
+        $fileInfoProp = $refl->getProperty($config['fileInfoField']);
+        $fileInfoProp->setAccessible(true);
+        $file = $fileInfoProp->getValue($object);
         $filePathField = $refl->getProperty($config['filePathField']);
         $filePathField->setAccessible(true);
-        $identifierProp = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName());
-        $identifierProp->setAccessible(true);
-        $identifier = $identifierProp->getValue($object);
+
+        $path = $config['path'];
+
+        if ($path === '') {
+            $pathMethod = $refl->getMethod($config['pathMethod']);
+            $pathMethod->setAccessible(true);
+            $path = $pathMethod->invoke($object);
+        }
+
+        $path = substr($path, strlen($path) - 1) === '/' ? substr($path, 0, strlen($path) - 2) : $path;
 
         if ($config['fileMimeTypeField']) {
             $fileMimeTypeField = $refl->getProperty($config['fileMimeTypeField']);
@@ -127,37 +131,33 @@ class UploadableListener extends MappedEventSubscriber
             $fileSizeField->setAccessible(true);
         }
 
-        $file = $this->getFile($index);
-
-        if ($file && is_array($file)) {
+        if (is_array($file)) {
             // If it's a single file we create an array anyway, so we can process
             // a collection or a single file in the same way
             if (isset($file['size'])) {
                 $file = array($file);
             }
 
-            foreach ($file as $id => $f) {
-                if ($action === self::ACTION_INSERT || $id === $identifier) {
-                    // First we remove the original file
-                    $this->removefile($meta, $config, $object);
+            foreach ($file as $f) {
+                // First we remove the original file
+                $this->removefile($meta, $config, $object);
 
-                    $info = $this->moveFile($f, $path);
-                    $filePathField->setValue($object, $info['filePath']);
-                    $changes = array(
-                        $config['filePathField'] => array($filePathField->getValue($object), $info['filePath'])
-                    );
+                $info = $this->moveFile($f, $path, $config['allowOverwrite'], $config['appendNumber']);
+                $filePathField->setValue($object, $info['filePath']);
+                $changes = array(
+                    $config['filePathField'] => array($filePathField->getValue($object), $info['filePath'])
+                );
 
-                    if ($config['fileMimeTypeField']) {
-                        $changes[$config['fileMimeTypeField']] = array($fileMimeTypeField->getValue($object), $info['fileMimeType']);
-                    }
-
-                    if ($config['fileSizeField']) {
-                        $changes[$config['fileSizeField']] = array($fileSizeField->getValue($object), $info['fileSize']);
-                    }
+                if ($config['fileMimeTypeField']) {
+                    $changes[$config['fileMimeTypeField']] = array($fileMimeTypeField->getValue($object), $info['fileMimeType']);
+                }
 
-                    $uow->scheduleExtraUpdate($object, $changes);
-                    $ea->setOriginalObjectProperty($uow, spl_object_hash($object), $config['filePathField'], $info['filePath']);
+                if ($config['fileSizeField']) {
+                    $changes[$config['fileSizeField']] = array($fileSizeField->getValue($object), $info['fileSize']);
                 }
+
+                $uow->scheduleExtraUpdate($object, $changes);
+                $ea->setOriginalObjectProperty($uow, spl_object_hash($object), $config['filePathField'], $info['filePath']);
             }
         }
     }
@@ -207,7 +207,7 @@ class UploadableListener extends MappedEventSubscriber
      * 
      * @return array - Information about the moved file
      */
-    public function moveFile(array $fileInfo, $path)
+    public function moveFile(array $fileInfo, $path, $overwrite = false, $appendNumber = false)
     {
         if ($fileInfo['error'] > 0) {
             switch ($fileInfo) {
@@ -256,6 +256,31 @@ class UploadableListener extends MappedEventSubscriber
         $info['fileName'] = $fileInfo['name'];
         $info['filePath'] = $path.'/'.$info['fileName'];
 
+        if (is_file($info['filePath'])) {
+            if ($overwrite) {
+                $this->doRemoveFile($info['filePath']);
+            } else if ($appendNumber) {
+                $counter = 1;
+                $extensionPos = strrpos($info['filePath'], '.');
+
+                if ($extensionPos !== false) {
+                    $extension = substr($info['filePath'], $extensionPos);
+
+                    $fileWithoutExt = substr($info['filePath'], 0, strrpos($info['filePath'], '.'));
+
+                    $info['filePath'] = $fileWithoutExt.'-'.$counter.$extension;
+                }
+
+                do {
+                    $info['filePath'] = $fileWithoutExt.'-'.(++$counter).$extension;
+                } while (is_file($info['filePath']));
+            } else {
+                throw new UploadableFileAlreadyExistsException(sprintf('File "%s" already exists!',
+                    $info['filePath']
+                ));
+            }
+        }
+
         if ($this->moveUploadedFile($fileInfo['tmp_name'], $info['filePath'])) {
             return $info;
         } else {
@@ -298,75 +323,6 @@ class UploadableListener extends MappedEventSubscriber
         return move_uploaded_file($source, $dest);
     }
 
-    /**
-     * Gets the $_FILES item at the index represented
-     * by the string passed as first argument
-     *
-     * @param string
-     *
-     * @return array
-     */
-    public function getFile($indexString)
-    {
-        $len = strlen($indexString);
-
-        if (empty($indexString) || $indexString{0} !== '[' || $indexString{$len - 1} !== ']') {
-            $msg = 'Index string "%s" is invalid. It should be something like "[image]" or "[image_form][images]';
-
-            throw new \InvalidArgumentException(sprintf($msg), $indexString);
-        }
-
-        $indexString = substr($indexString, 1, strlen($indexString) - 2);
-
-        if (strpos($indexString, '][') !== false) {
-            $indexArray = explode('][', $indexString);
-        } else {
-            $indexArray = array($indexString);
-        }
-
-        return $this->getItemFromArray($_FILES, $indexArray);
-    }
-
-    /**
-     * Helper method to return an item from an array using an array of indexes
-     * as an index path
-     *
-     * @param array - Source array
-     * @param array - Array of indexes
-     */
-    public function getItemFromArray(array $sourceArray, array $indexesArray)
-    {
-        // Nothing to do
-        if (empty($sourceArray)) {
-            return false;
-        }
-
-        if (empty($indexesArray)) {
-            $msg = 'Second argument: "indexesArray" must be a non empty array with the indexes to search.';
-
-            throw new \InvalidArgumentException($msg);
-        }
-
-        if (isset($sourceArray[$indexesArray[0]])) {
-            if (count($indexesArray) === 1) {
-                return $sourceArray[$indexesArray[0]];
-            } else {
-                $source = $sourceArray[$indexesArray[0]];
-
-                if (!is_array($source)) {
-                    return false;
-                }
-
-                unset($sourceArray[$indexesArray[0]]);
-                $indexesArray = array_slice($indexesArray, 1);
-
-                return $this->getItemFromArray($source, $indexesArray);
-            }
-        } else {
-            return false;
-        }
-    }
-
     /**
      * Maps additional metadata
      *

+ 16 - 9
tests/Gedmo/Uploadable/Fixture/Entity/File.php

@@ -8,7 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 
 /**
  * @ORM\Entity
- * @Gedmo\Uploadable()
+ * @Gedmo\Uploadable(allowOverwrite=true)
  */
 class File
 {
@@ -30,6 +30,11 @@ class File
      */
     private $filePath;
 
+    /**
+     * @Gedmo\UploadableFileInfo
+     */
+    private $fileInfo;
+
     /**
      * @ORM\ManyToOne(targetEntity="Article", inversedBy="files")
      * @ORM\JoinColumn(name="article_id", referencedColumnName="id")
@@ -72,19 +77,21 @@ class File
         return $this->article;
     }
 
-    /**
-     * @Gedmo\UploadablePath
-     */
-    public function getPath()
+    public function setFileInfo(array $fileInfo)
     {
-        return __DIR__.'/../../../../temp/uploadable';
+        $this->fileInfo = $fileInfo;
+    }
+
+    public function getFileInfo()
+    {
+        return $this->fileInfo;
     }
 
     /**
-     * @Gedmo\UploadableFilesArrayIndex
+     * @Gedmo\UploadablePath
      */
-    public function getFilesArrayIndex()
+    public function getPath()
     {
-        return '[file]';
+        return __DIR__.'/../../../../temp/uploadable';
     }
 }

+ 15 - 8
tests/Gedmo/Uploadable/Fixture/Entity/Image.php

@@ -30,6 +30,11 @@ class Image
      */
     private $filePath;
 
+    /**
+     * @Gedmo\UploadableFileInfo
+     */
+    private $fileInfo;
+
 
     public function getId()
     {
@@ -56,19 +61,21 @@ class Image
         return $this->filePath;
     }
 
-    /**
-     * @Gedmo\UploadablePath
-     */
-    public function getPath()
+    public function setFileInfo(array $fileInfo)
     {
-        return __DIR__.'/../../../../temp/uploadable';
+        $this->fileInfo = $fileInfo;
+    }
+
+    public function getFileInfo()
+    {
+        return $this->fileInfo;
     }
 
     /**
-     * @Gedmo\UploadableFilesArrayIndex
+     * @Gedmo\UploadablePath
      */
-    public function getFilesArrayIndex()
+    public function getPath()
     {
-        return '[image]';
+        return __DIR__.'/../../../../temp/uploadable';
     }
 }

+ 16 - 53
tests/Gedmo/Uploadable/UploadableEntityTest.php

@@ -55,13 +55,6 @@ class UploadableEntityTest extends BaseTestCaseORM
         $this->testFileSize = 4;
         $this->testFileMimeType = 'text/plain';
 
-        mkdir($this->destinationTestDir);
-    }
-
-    protected function tearDown()
-    {
-        parent::tearDown();
-
         if (is_file($this->destinationTestFile)) {
             unlink($this->destinationTestFile);
         }
@@ -70,7 +63,11 @@ class UploadableEntityTest extends BaseTestCaseORM
             unlink($this->destinationTestFile2);
         }
 
-        rmdir($this->destinationTestDir);
+        if (is_dir($this->destinationTestDir)) {
+            rmdir($this->destinationTestDir);
+        }
+
+        mkdir($this->destinationTestDir);
     }
 
     public function testUploadableEntity()
@@ -88,14 +85,17 @@ class UploadableEntityTest extends BaseTestCaseORM
 
         // If there is an uploaded file, we process it
         $fileInfo = $this->generateUploadedFile();
+
         $image2 = new Image();
         $image2->setTitle('456');
+        $image2->setFileInfo($fileInfo);
 
         $this->em->persist($image2);
         $this->em->flush();
 
         $this->em->refresh($image2);
 
+        // We need to set this again because of the recent refresh
         $firstFile = $image2->getFilePath();
 
         $this->assertEquals($image2->getPath().DIRECTORY_SEPARATOR.$fileInfo['name'], $image2->getFilePath());
@@ -103,25 +103,20 @@ class UploadableEntityTest extends BaseTestCaseORM
 
         // UPDATE of an Uploadable Entity
 
-        // First we need to add the ID to the index of the element in $_FILES so it can be recognized
-        // by the listener as the update info for the file $image2.
-        unset($_FILES[$fileInfo['index']]);
-
         // We change the "uploaded" file
         $fileInfo['tmp_name'] = $this->testFile2;
         $fileInfo['name'] = $this->testFilename2;
 
-        $_FILES[$fileInfo['index']] = array(
-            $image2->getId()    => $fileInfo
-        );
+        $image2->setFileInfo($fileInfo);
 
         // For now, we need to force the update changing one of the managed fields. If we don't do this,
         // entity won't be marked for update
-        $image2->setTitle($image2->getTitle().'789');
+        $image2->setTitle($image2->getTitle().'7892');
 
         $this->em->flush();
 
         $this->em->refresh($image2);
+
         $lastFile = $image2->getFilePath();
 
         $this->assertEquals($image2->getPath().DIRECTORY_SEPARATOR.$fileInfo['name'], $image2->getFilePath());
@@ -151,13 +146,15 @@ class UploadableEntityTest extends BaseTestCaseORM
         $article->addFile($file2);
         $article->addFile($file3);
 
-        $filesArrayIndex = strtr($file1->getFilesArrayIndex(), array('[' => '', ']' => ''));
+        $filesArrayIndex = 'file';
 
         $fileInfo = $this->generateUploadedFile($filesArrayIndex);
         $fileInfo2 = $this->generateUploadedFile($filesArrayIndex);
         $fileInfo3 = $this->generateUploadedFile($filesArrayIndex);
 
-        $_FILES[$fileInfo['index']] = array($fileInfo, $fileInfo2, $fileInfo3);
+        $file1->setFileInfo($fileInfo);
+        $file2->setFileInfo($fileInfo2);
+        $file3->setFileInfo($fileInfo3);
 
         $this->em->persist($article);
 
@@ -174,37 +171,6 @@ class UploadableEntityTest extends BaseTestCaseORM
         $this->assertEquals($file3Path, $files[2]->getFilePath());
     }
 
-    public function testGetItemFromArrayMethod()
-    {
-        $dataText = 'My Data';
-        $data = array('first' => array('second' => array('third' => $dataText)));
-        $indexes = array('first', 'second', 'third');
-
-        $result = $this->listener->getItemFromArray($data, $indexes);
-
-        $this->assertEquals($dataText, $result);
-
-        $data = array('first' => $dataText);
-
-        $this->assertFalse($this->listener->getItemFromArray($data, $indexes));
-
-        $data = array();
-
-        $this->assertFalse($this->listener->getItemFromArray($data, $indexes));
-
-        $data = array('first' => array('second' => array('anotherIndex' => $dataText)));
-        $indexes = array('first', 'second', 'third');
-
-        $this->assertFalse($this->listener->getItemFromArray($data, $indexes));
-
-        $this->setExpectedException('InvalidArgumentException');
-
-        $data = array('first' => $dataText);
-        $indexes = array();
-
-        $this->listener->getItemFromArray($data, $indexes);
-    }
-
     private function generateUploadedFile($index = 'image', $file = false, array $info = array())
     {
         if (empty($info)) {
@@ -213,12 +179,9 @@ class UploadableEntityTest extends BaseTestCaseORM
                 'name'              => $this->testFilename,
                 'size'              => $this->testFileSize,
                 'type'              => $this->testFileMimeType,
-                'error'             => 0,
-                'index'             => $index
+                'error'             => 0
             );
         }
-        
-        $_FILES[$index] = $info;
 
         return $info;
     }