├── .travis.composer.config.json
├── .travis.yml
├── Command
└── RepairFileDataCommand.php
├── DataStorage
├── DataStorageInterface.php
└── OrmDataStorage.php
├── DependencyInjection
├── Compiler
│ └── FormPass.php
├── Configuration.php
└── IphpFileStoreExtension.php
├── Driver
└── AnnotationDriver.php
├── EventListener
└── UploaderListener.php
├── File
└── File.php
├── FileStorage
├── FileStorageInterface.php
└── FileSystemStorage.php
├── Form
├── DataTransformer
│ ├── FileDataTransformer.php
│ └── FileDataViewTransformer.php
└── Type
│ ├── FileType.php
│ └── FileTypeBindSubscriber.php
├── IphpFileStoreBundle.php
├── Mapping
├── Annotation
│ ├── Uploadable.php
│ └── UploadableField.php
├── PropertyMapping.php
└── PropertyMappingFactory.php
├── Naming
├── DefaultDirectoryNamer.php
├── DefaultNamer.php
└── NamerServiceInvoker.php
├── README.md
├── Resources
├── config
│ ├── routing.xml
│ └── services.xml
├── translations
│ ├── messages.fr.xliff
│ └── messages.ru.xliff
└── views
│ └── Form
│ ├── fields-base.html.twig
│ └── fields.html.twig
├── Tests
├── ChildOfDummyEntity.php
├── DataStorage
│ └── Orm
│ │ └── OrmDataStorageTest.php
├── Driver
│ └── AnnotationDriverTest.php
├── DummyEntity.php
├── DummyEntityProxyORM.php
├── DummyEntitySeparateDataField.php
├── EventListener
│ └── UploaderListenerTest.php
├── File
│ └── FileTest.php
├── FileStorage
│ ├── 123.jpg
│ └── FileSystemStorageTest.php
├── Fixtures
│ ├── files
│ │ └── text.txt
│ └── images
│ │ ├── front-images-list.jpeg
│ │ ├── github1.png
│ │ ├── php-elephant.png
│ │ └── sonata-admin-iphpfile.jpeg
├── Form
│ ├── DataTransformer
│ │ ├── FileDataTransformerTest.php
│ │ └── FileDataViewTransformerTest.php
│ └── Type
│ │ └── FileTypeBindSubscriberTest.php
├── Functional
│ ├── AppKernel.php
│ ├── BaseTestCase.php
│ ├── FileSaveTest.php
│ ├── ImageEditTest.php
│ ├── ImageUploadTest.php
│ ├── TestBundle
│ │ ├── Controller
│ │ │ └── DefaultController.php
│ │ ├── Entity
│ │ │ └── Photo.php
│ │ ├── Resources
│ │ │ └── views
│ │ │ │ └── Photo
│ │ │ │ ├── edit.html.twig
│ │ │ │ └── index.html.twig
│ │ └── TestBundle.php
│ ├── TestXmlConfigBundle
│ │ ├── Entity
│ │ │ ├── File.php
│ │ │ └── UploadableEntity.php
│ │ ├── Resources
│ │ │ └── config
│ │ │ │ └── doctrine
│ │ │ │ └── File.orm.xml
│ │ └── TestXmlConfigBundle.php
│ └── config
│ │ ├── database.php
│ │ ├── default.yml
│ │ ├── default_newfilepath.yml
│ │ ├── doctrine.yml
│ │ ├── framework.yml
│ │ ├── routing.yml
│ │ └── twig.yml
├── Mapping
│ ├── Annotation
│ │ └── UploadableFieldTest.php
│ ├── PropertyMappingFactoryTest.php
│ └── PropertyMappingTest.php
├── Mocks.php
├── Naming
│ ├── DefaultDirectoryNamerTest.php
│ ├── DefaultNamerTest.php
│ └── NamerServiceInvokerTest.php
├── TwoFieldsDummyEntity.php
└── bootstrap.php
├── composer.json
└── phpunit.xml.dist
/.travis.composer.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "config":{
3 | "github-oauth":{
4 | "github.com":"3bb968e28b023f978095519704e606d315121c95"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.5
5 | - 5.6
6 | - 7.0
7 |
8 | env:
9 | - SYMFONY_VERSION=2.8.*
10 | - SYMFONY_VERSION=3.0.*
11 | - SYMFONY_VERSION=3.1.*
12 |
13 | addons:
14 | code_climate:
15 | repo_token: f0a01fd708e8f258d3532dd82d1b55c3873953ad56fa2796d178483b9c9d6228
16 |
17 | before_script:
18 |
19 | # Set the GitHub OAuth token to make use of the 6000 per hour rate limit
20 | - "mkdir -p ~/.composer"
21 | - cp .travis.composer.config.json ~/.composer/config.json
22 | - composer self-update
23 | - composer require symfony/asset:${SYMFONY_VERSION} --no-update
24 | - composer require symfony/intl:${SYMFONY_VERSION} --no-update
25 | - composer require symfony/event-dispatcher:${SYMFONY_VERSION} --no-update
26 | - composer require symfony/options-resolver:${SYMFONY_VERSION} --no-update
27 | - composer require symfony/form:${SYMFONY_VERSION} --no-update
28 | - composer require symfony/twig-bundle:${SYMFONY_VERSION} --no-update
29 | - composer require symfony/twig-bridge:${SYMFONY_VERSION} --no-update
30 | - composer require symfony/templating:${SYMFONY_VERSION} --no-update
31 | - composer require symfony/doctrine-bridge:${SYMFONY_VERSION} --no-update
32 | - composer require symfony/framework-bundle:${SYMFONY_VERSION}
33 | - composer install --dev
34 |
35 | notifications:
36 | email: vitiko@mail.ru
37 |
--------------------------------------------------------------------------------
/Command/RepairFileDataCommand.php:
--------------------------------------------------------------------------------
1 | setName('iphp:filestore:repair')
21 | ->addOption('entity', null, InputOption::VALUE_REQUIRED, 'The entity class name (shortcut notation)')
22 | ->addOption('field', null, InputOption::VALUE_REQUIRED, 'The field with file data')
23 | ->addOption('maxresults', null, InputOption::VALUE_OPTIONAL, 'Max results limitation')
24 | ->addOption('webdir', null, InputOption::VALUE_OPTIONAL, 'Web dir for searching file')
25 | ->addOption('force', null, InputOption::VALUE_OPTIONAL, 'Force reupload and rename files');
26 |
27 | }
28 |
29 |
30 | protected function getPropertyMappingFactory()
31 | {
32 | return $this->getContainer()->get('iphp.filestore.mapping.factory');
33 | }
34 |
35 |
36 | protected function getWebDir(InputInterface $input)
37 | {
38 | $webDir = $input->getOption('webdir');
39 |
40 | if (!$webDir && $this->getContainer()->has('iphp.web_dir'))
41 | $webDir = $this->getContainer()->getParameter('iphp.web_dir');
42 |
43 | if (!$webDir) $webDir = str_replace('\\', '/', realpath($this->getContainer()->getParameter('kernel.root_dir') . '/../web/'));
44 |
45 | if (!$webDir) throw new \InvalidArgumentException ('
46 | For resolving IphpFileStoreBundle uploaded files need to set --webdir option');
47 |
48 | return $webDir;
49 | }
50 |
51 |
52 | function getMaxResults(InputInterface $input)
53 | {
54 | $maxResults = $input->getOption('maxresults');
55 | if (!$maxResults || !is_numeric($maxResults)) $maxResults = 1000;
56 |
57 | return $maxResults;
58 | }
59 |
60 |
61 | function getEntityManager()
62 | {
63 | return $this->getContainer()->get('doctrine')->getManager();
64 | }
65 |
66 | function getMappingFromField($entity, $field)
67 | {
68 | return $this->getPropertyMappingFactory()->getMappingFromField($entity, new \ReflectionClass($entity), $field);
69 | }
70 |
71 |
72 | function getRepository($entityFullName)
73 | {
74 | return $this->getEntityManager()->getRepository($entityFullName);
75 | }
76 |
77 | function getEntityIds($entityFullName, $maxResults)
78 | {
79 | return $this->getEntityManager()->createQuery(
80 | "SELECT e.id FROM " . $entityFullName . " e ORDER BY e.id ASC"
81 | )->setMaxResults($maxResults)->getArrayResult();
82 | }
83 |
84 | protected function execute(InputInterface $input, OutputInterface $output)
85 | {
86 | $entityFullName = $input->getOption('entity');
87 | $field = $input->getOption('field');
88 | $force = $input->getOption('force') ? true : false;
89 | $webDir = $this->getWebDir($input);
90 |
91 |
92 | foreach ($this->getEntityIds($entityFullName, $this->getMaxResults($input)) as $pos => $e) {
93 |
94 | $toFlush = false;
95 | $entity = $this->getRepository($entityFullName)->findOneById($e['id']);
96 | $fileData = $entity->{'get' . ucfirst($field)}();
97 |
98 | if (!$fileData) continue;
99 |
100 | $fileNameByWebPath = $webDir . $fileData['path'];
101 | $fileNameByWebPathExists = file_exists($fileNameByWebPath);
102 |
103 | $resolvedFileName = $this->getMappingFromField($entity, $field)->resolveFileName($fileData['fileName']);
104 | $resolvedFileNameExists = file_exists($resolvedFileName) ? 'exists' : 'NO';
105 |
106 | if (!$fileNameByWebPathExists && !$resolvedFileNameExists) {
107 | $output->writeln("can't find file ");
108 | continue;
109 | }
110 |
111 | if ($fileNameByWebPathExists && $resolvedFileNameExists && !$force) continue;
112 |
113 | //uploadedFile because need to move to new destination (not copy)
114 | $file = new UploadedFile ($fileNameByWebPathExists ? $fileNameByWebPath : $resolvedFileNameExists,
115 | $fileData['originalName'], $fileData['mimeType'], null, null, true);
116 |
117 | $entity->{'set' . ucfirst($field)} ($file);
118 |
119 | $this->getEntityManager()->persist($entity);
120 |
121 | $toFlush = true;
122 | if ($pos % 20 == 0 && $toFlush) $this->getEntityManager()->flush();
123 | if ($pos % 100 == 0) $this->getEntityManager()->clear();
124 | }
125 |
126 | $this->getEntityManager()->flush();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/DataStorage/DataStorageInterface.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | interface DataStorageInterface
14 | {
15 | /**
16 | * Gets the mapped object from the event arguments.
17 | *
18 | * @param \Doctrine\Common\Persistence\Event\LifecycleEventArgs $e The event arguments.
19 | * @return object The mapped object.
20 | */
21 | public function getObjectFromArgs(EventArgs $e);
22 |
23 | /**
24 | * Recomputes the change set for the object.
25 | *
26 | * @param \Doctrine\Common\Persistence\Event\LifecycleEventArgs $e The event arguments.
27 | */
28 | public function recomputeChangeSet(EventArgs $e);
29 |
30 | /**
31 | * Gets the reflection class for the object taking
32 | * proxies into account.
33 | *
34 | * @param object $obj The object.
35 | * @return \ReflectionClass The reflection class.
36 | */
37 | public function getReflectionClass($obj);
38 |
39 |
40 | public function postFlush ($obj, EventArgs $args);
41 |
42 |
43 | public function previusFieldDataIfChanged ($fieldName, EventArgs $args);
44 | }
45 |
--------------------------------------------------------------------------------
/DataStorage/OrmDataStorage.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class OrmDataStorage implements DataStorageInterface
17 | {
18 | /**
19 | * {@inheritDoc}
20 | */
21 | public function getObjectFromArgs(EventArgs $e)
22 | {
23 | return $e->getEntity();
24 | }
25 |
26 | /**
27 | * {@inheritDoc}
28 | */
29 | public function recomputeChangeSet(EventArgs $e)
30 | {
31 | $obj = $this->getObjectFromArgs($e);
32 |
33 | /**
34 | * var \Doctrine\ORM\EntityManager
35 | */
36 | $em = $e->getEntityManager();
37 |
38 | $uow = $em->getUnitOfWork();
39 | $metadata = $em->getClassMetadata(get_class($obj));
40 | $uow->recomputeSingleEntityChangeSet($metadata, $obj);
41 | }
42 |
43 | /**
44 | * {@inheritDoc}
45 | */
46 | public function getReflectionClass($obj)
47 | {
48 | if ($obj instanceof Proxy) {
49 | return new \ReflectionClass(get_parent_class($obj));
50 | }
51 |
52 | return new \ReflectionClass($obj);
53 | }
54 |
55 |
56 | /**
57 | * {@inheritDoc}
58 | */
59 | public function postFlush($obj, EventArgs $args)
60 | {
61 | $args->getEntityManager()->persist($obj);
62 | $args->getEntityManager()->flush();
63 | }
64 |
65 |
66 | public function previusFieldDataIfChanged($fieldName, EventArgs $args)
67 | {
68 | return $args->hasChangedField($fieldName) ? $args->getOldValue($fieldName) : null;
69 | }
70 |
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/FormPass.php:
--------------------------------------------------------------------------------
1 | getParameter('twig.form.resources');
18 | $resources[] = 'IphpFileStoreBundle:Form:fields.html.twig';
19 |
20 |
21 | $container->setParameter('twig.form.resources', $resources);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class Configuration implements ConfigurationInterface
14 | {
15 | /**
16 | * Gets the configuration tree builder for the extension.
17 | *
18 | * @return Tree The configuration tree builder
19 | */
20 | public function getConfigTreeBuilder()
21 | {
22 | $tb = new TreeBuilder();
23 | $root = $tb->root('iphp_file_store');
24 |
25 | $root
26 | ->children()
27 | ->scalarNode('db_driver')->defaultValue('orm')->end()
28 |
29 | ->arrayNode('mappings')
30 | ->useAttributeAsKey('id')
31 | ->prototype('array')
32 | ->children()
33 | ->scalarNode('upload_dir')->end()
34 | ->scalarNode('upload_path')->end()
35 | ->scalarNode('delete_on_remove')->defaultTrue()->end()
36 | ->scalarNode('overwrite_duplicates')->defaultFalse()->end()
37 |
38 |
39 | ->arrayNode('namer')
40 | ->treatFalseLike(array ())
41 | ->treatNullLike(array ('translit' => array('service' => 'iphp.filestore.namer.default')))
42 | ->treatTrueLike(array ('translit' => array('service' => 'iphp.filestore.namer.default')))
43 | ->useAttributeAsKey('id')
44 | ->prototype('array')
45 |
46 | ->children()
47 |
48 | ->scalarNode('service')->defaultValue('iphp.filestore.namer.default')->end()
49 | ->arrayNode('params')
50 | ->useAttributeAsKey('name')
51 | ->prototype('scalar')->end()
52 | ->end()
53 | ->end()
54 | ->end()
55 | ->end()
56 |
57 |
58 |
59 | ->arrayNode('directory_namer')
60 |
61 | ->useAttributeAsKey('id')
62 | ->prototype('array')
63 |
64 | ->children()
65 |
66 | ->scalarNode('service')->defaultValue('iphp.filestore.directory_namer.default')->end()
67 | ->arrayNode('params')
68 | ->useAttributeAsKey('name')
69 | ->prototype('scalar')->end()
70 | ->end()
71 |
72 | ->end()
73 | ->end()
74 | ->end()
75 |
76 | ->end()
77 | ->end()
78 | ->end()
79 | ->end()
80 | ;
81 |
82 | return $tb;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/DependencyInjection/IphpFileStoreExtension.php:
--------------------------------------------------------------------------------
1 | 'doctrine.event_subscriber',
24 | // 'document' => ''
25 | );
26 |
27 | /**
28 | * @var array $adapterMap
29 | */
30 | protected $adapterMap = array(
31 | 'orm' => 'Iphp\FileStoreBundle\DataStorage\OrmDataStorage',
32 | //'document' => ''
33 | );
34 |
35 |
36 | /**
37 | * {@inheritDoc}
38 | */
39 | public function load(array $configs, ContainerBuilder $container)
40 | {
41 |
42 |
43 | $configuration = new Configuration();
44 | $config = $this->processConfiguration($configuration, $configs);
45 |
46 |
47 |
48 | $driver = strtolower($config['db_driver']);
49 | if (!in_array($driver, array_keys($this->tagMap))) {
50 | throw new \InvalidArgumentException(
51 | sprintf(
52 | 'Invalid "db_driver" configuration option specified: "%s"',
53 | $driver
54 | )
55 | );
56 | }
57 |
58 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
59 | $loader->load('services.xml');
60 |
61 | $mappings = isset($config['mappings']) ? $config['mappings'] : array();
62 |
63 | $container->setParameter('iphp.filestore.mappings', $mappings);
64 |
65 |
66 | $container->setParameter('iphp.filestore.datastorage.class', $this->adapterMap[$driver]);
67 |
68 | //Add tag orm or mongodb to listener service
69 | $container->getDefinition('iphp.filestore.event_listener.uploader')->addTag($this->tagMap[$driver]);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Driver/AnnotationDriver.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class AnnotationDriver
13 | {
14 | /**
15 | * @var Reader $reader
16 | */
17 | protected $reader;
18 |
19 |
20 | protected $uploadedClass = array();
21 |
22 | protected $uploadedFields = array();
23 |
24 | /**
25 | * Constructs a new instance of AnnotationDriver.
26 | *
27 | * @param \Doctrine\Common\Annotations\Reader $reader The annotation reader.
28 | */
29 | public function __construct(Reader $reader)
30 | {
31 | $this->reader = $reader;
32 | }
33 |
34 | /**
35 | * Attempts to read the uploadable annotation.
36 | *
37 | * @param \ReflectionClass $class The reflection class.
38 | * @return null|\Iphp\FileStoreBundle\Annotation\Uploadable The annotation.
39 | */
40 | public function readUploadable(\ReflectionClass $class)
41 | {
42 | $baseClassName = $className = $class->getNamespaceName() . '\\' . $class->getName();
43 | do {
44 | if (isset($this->uploadedClass[$className])) {
45 | if ($baseClassName != $className)
46 | $this->uploadedClass[$baseClassName] = $this->uploadedClass[$className];
47 | return $this->uploadedClass[$baseClassName];
48 | }
49 |
50 | $annotation = $this->reader->getClassAnnotation($class, 'Iphp\FileStoreBundle\Mapping\Annotation\Uploadable');
51 | if ($annotation) {
52 | $this->uploadedClass[$baseClassName] = $annotation;
53 | if ($baseClassName != $className) $this->uploadedClass[$className] = $annotation;
54 |
55 | return $annotation;
56 | }
57 | $class = $class->getParentClass();
58 | if ($class) $className = $class->getNamespaceName() . '\\' . $class->getName();
59 | } while ($class);
60 |
61 | return $annotation;
62 | }
63 |
64 | /**
65 | * Attempts to read the uploadable field annotations.
66 | *
67 | * @param \ReflectionClass $class The reflection class.
68 | * @return \Iphp\FileStoreBundle\Mapping\Annotation\UploadableField[]
69 | */
70 | public function readUploadableFields(\ReflectionClass $class)
71 | {
72 | $propertyAnnotations = array();
73 |
74 | foreach ($class->getProperties() as $prop) {
75 |
76 | $propertyAnnotation = $this->reader->getPropertyAnnotation($prop, 'Iphp\FileStoreBundle\Mapping\Annotation\UploadableField');
77 | if (null !== $propertyAnnotation) {
78 | $propertyAnnotation->setFileUploadPropertyName($prop->getName());
79 | $propertyAnnotations[] = $propertyAnnotation;
80 | }
81 | }
82 |
83 | return $propertyAnnotations;
84 | }
85 |
86 | /**
87 | * Attempts to read the uploadable field annotation of the
88 | * specified property.
89 | *
90 | * @param \ReflectionClass $class The class.
91 | * @param string $field The field
92 | * @return null|\Iphp\FileStoreBundle\Annotation\UploadableField The uploadable field.
93 | */
94 | public function readUploadableField(\ReflectionClass $class, $field)
95 | {
96 | try {
97 | $prop = $class->getProperty($field);
98 |
99 | $field = $this->reader->getPropertyAnnotation($prop, 'Iphp\FileStoreBundle\Mapping\Annotation\UploadableField');
100 | if (null === $field) {
101 | return null;
102 | }
103 |
104 | $field->setFileUploadPropertyName($prop->getName());
105 |
106 | return $field;
107 | } catch (\ReflectionException $e) {
108 | return null;
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/EventListener/UploaderListener.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | class UploaderListener implements EventSubscriber
25 | {
26 | /**
27 | * Adapter for ORMor MongoDb
28 | * @var \Iphp\FileStoreBundle\DataStorage\DataStorageInterface $dataStorage
29 | */
30 | protected $dataStorage;
31 |
32 |
33 | /**
34 | * @var \Iphp\FileStoreBundle\FileStorage\FileStorageInterface $fileStorage
35 | */
36 | protected $fileStorage;
37 |
38 |
39 | /**
40 | * @var \Iphp\FileStoreBundle\Mapping\PropertyMappingFactory $mappingFactory
41 | */
42 | protected $mappingFactory;
43 |
44 |
45 | /**
46 | * @var \SplObjectStorage Temporary store for using in fileStorage
47 | */
48 | protected $deferredFiles;
49 |
50 |
51 | /**
52 | * Constructs a new instance of UploaderListener.
53 | *
54 | * @param \Iphp\FileStoreBundle\DataStorage\DataStorageInterface $dataStorage The dataStorage
55 | * @param \Iphp\FileStoreBundle\FileStorage\FileStorageInterface $fileStorage The storage.
56 | * @param \Iphp\FileStoreBundle\Mapping\PropertyMappingFactory $mappingFactory Mapping Factore
57 | */
58 | public function __construct(DataStorageInterface $dataStorage,
59 | FileStorageInterface $fileStorage,
60 | PropertyMappingFactory $mappingFactory)
61 | {
62 | $this->dataStorage = $dataStorage;
63 | $this->fileStorage = $fileStorage;
64 | $this->mappingFactory = $mappingFactory;
65 |
66 | $this->deferredFiles = new \SplObjectStorage();
67 | }
68 |
69 |
70 | public function hasDeferredObject($obj)
71 | {
72 | return isset($this->deferredFiles [$obj]) && $this->deferredFiles [$obj];
73 | }
74 |
75 | public function hasDeferredPropertyMapping($obj, PropertyMapping $mapping)
76 | {
77 | return $this->hasDeferredObject($obj) &&
78 | isset($this->deferredFiles [$obj][$mapping]) && $this->deferredFiles [$obj][$mapping];
79 | }
80 |
81 |
82 | public function getDeferredObjectNum()
83 | {
84 | return count($this->deferredFiles);
85 | }
86 |
87 |
88 | /**
89 | * The events the listener is subscribed to.
90 | *
91 | * @return array The array of events.
92 | */
93 | public function getSubscribedEvents()
94 | {
95 | return array(
96 | 'prePersist',
97 | 'postFlush',
98 | 'preUpdate',
99 | 'postRemove',
100 | );
101 | }
102 |
103 |
104 | /**
105 | * @return \Iphp\FileStoreBundle\Mapping\PropertyMapping[]
106 | */
107 | protected function getMappingsFromArgs(EventArgs $args)
108 | {
109 | $obj = $this->dataStorage->getObjectFromArgs($args);
110 | return $this->mappingFactory->getMappingsFromObject($obj, $this->dataStorage->getReflectionClass($obj));
111 | }
112 |
113 | /**
114 | * Checks for for file to upload and store it for store at postFlush event
115 | *
116 | * @param \Doctrine\Common\EventArgs $args The event arguments.
117 | */
118 | public function prePersist(EventArgs $args)
119 | {
120 | $obj = $this->dataStorage->getObjectFromArgs($args);
121 | $mappings = $this->mappingFactory->getMappingsFromObject($obj, $this->dataStorage->getReflectionClass($obj));
122 | $curFiles = new \SplObjectStorage();
123 |
124 | foreach ($mappings as $mapping) {
125 | $file = $mapping->getFileUploadPropertyValue();
126 | if ($file instanceof File) $curFiles[$mapping] = $file;
127 | $mapping->setFileUploadPropertyValue(null);
128 | }
129 |
130 | //if ($curFiles) $this->deferredFiles [$mappings[0]->getObj()] = $curFiles;
131 | if (count($curFiles)) $this->deferredFiles [$obj] = $curFiles;
132 | }
133 |
134 |
135 | /**
136 | * Store at postFlush event because file namer mey need entity id, at prePersist event
137 | * system does not now auto generated entity id
138 | * @param \Doctrine\Common\EventArgs $args
139 | */
140 | public function postFlush(EventArgs $args)
141 | {
142 | if (!$this->deferredFiles) return;
143 |
144 | foreach ($this->deferredFiles as $obj) {
145 | if (!$this->deferredFiles[$obj]) continue;
146 |
147 | foreach ($this->deferredFiles[$obj] as $mapping) {
148 | $fileData = $this->fileStorage->upload($mapping, $this->deferredFiles[$obj][$mapping]);
149 | $mapping->setFileDataPropertyValue($fileData);
150 | }
151 |
152 | unset($this->deferredFiles[$obj]);
153 | $this->dataStorage->postFlush($obj, $args);
154 | }
155 | }
156 |
157 |
158 | /**
159 | * Update the mapped file for Entity (obj)
160 | *
161 | * @param \Doctrine\Common\EventArgs $args
162 | */
163 | public function preUpdate(\Doctrine\Common\EventArgs $args)
164 | {
165 | //All mappings from updated object
166 | $mappings = $this->getMappingsFromArgs($args);
167 |
168 | foreach ($mappings as $mapping) {
169 | if ($mapping->isUseOneProperty()) $this->updateUseOneProperties($args, $mapping);
170 | else $this->updateSeparateProperties($args, $mapping);
171 | }
172 | $this->dataStorage->recomputeChangeSet($args);
173 | }
174 |
175 |
176 | /**
177 | * upload field and file data field are NOT SAME ($obj->file and $obj->uploadFile)
178 | * @param EventArgs $args
179 | * @param PropertyMapping $mapping
180 | */
181 | protected function updateUseOneProperties(\Doctrine\Common\EventArgs $args, PropertyMapping $mapping)
182 | {
183 | $uploadedFile = $mapping->getFileUploadPropertyValue();
184 |
185 | //use getOldValue from ORM
186 | $currentFileData = $this->dataStorage->previusFieldDataIfChanged($mapping->getFileDataPropertyName(), $args);
187 | $currentFileName = $currentFileData ? $mapping->resolveFileName($currentFileData['fileName']) : null;
188 |
189 |
190 | //If no new file
191 | if (is_null($uploadedFile) || !($uploadedFile instanceof File)) {
192 |
193 | if ($currentFileData) {
194 | if (!$this->fileStorage->fileExists($currentFileName)) {
195 |
196 | $fileNameByWebDir = $_SERVER['DOCUMENT_ROOT'] . $currentFileData['path'];
197 |
198 | if ($this->fileStorage->fileExists($fileNameByWebDir)) {
199 | $uploadedFile = new UploadedFile ($fileNameByWebDir,
200 | $currentFileData['originalName'], $currentFileData['mimeType'],
201 | null, null, true);
202 | $fileData = $this->fileStorage->upload($mapping, $uploadedFile);
203 | $mapping->setFileDataPropertyValue($fileData);
204 | }
205 |
206 | } //Preserve old fileData if current file exist
207 | else $mapping->setFileDataPropertyValue($currentFileData);
208 |
209 | }
210 |
211 |
212 | } //set new File and uploaded file has deleted status - remove file
213 | else if ($uploadedFile instanceof \Iphp\FileStoreBundle\File\File && $uploadedFile->isDeleted()) {
214 | if ($this->fileStorage->removeFile($currentFileName)) $mapping->setFileDataPropertyValue(null);
215 | } //set new file - upload new file
216 | else {
217 |
218 | //Old value (file) exits and uploaded new file
219 | if ($currentFileData && !$this->fileStorage->isSameFile($uploadedFile, $currentFileName))
220 | //before upload new file delete old file
221 | $this->fileStorage->removeFile($currentFileName);
222 |
223 | $fileData = $this->fileStorage->upload($mapping, $uploadedFile);
224 | $mapping->setFileDataPropertyValue($fileData);
225 | }
226 |
227 | }
228 |
229 |
230 | /**
231 | * upload field and file data field are SAME ($obj->file)
232 | * @param EventArgs $args
233 | * @param PropertyMapping $mapping
234 | */
235 | protected function updateSeparateProperties(\Doctrine\Common\EventArgs $args, PropertyMapping $mapping)
236 | {
237 | $uploadedFile = $mapping->getFileUploadPropertyValue();
238 | $currentFileData = $mapping->getFileDataPropertyValue();
239 | $previousFileData = $this->dataStorage->previusFieldDataIfChanged($mapping->getFileDataPropertyName(), $args);
240 |
241 |
242 | $currentFileName = $previousFileData ? $mapping->resolveFileName($previousFileData['fileName']) : null;
243 |
244 |
245 | //delete current file
246 | if ($previousFileData && (
247 | // $obj->setFile (null)
248 | is_null($currentFileData) ||
249 | //$obj->setUploadFile (Iphp\File::createEmpty()->delete())
250 | $uploadedFile && $uploadedFile instanceof \Iphp\FileStoreBundle\File\File && $uploadedFile->isDeleted())
251 | ) {
252 | if ($this->fileStorage->removeFile($currentFileName)) $mapping->setFileDataPropertyValue(null);
253 | } //upload new file
254 | else if ($uploadedFile && $uploadedFile instanceof File) {
255 |
256 | //Old value (file) exists and uploaded new file
257 | if ($currentFileName && !$this->fileStorage->isSameFile($uploadedFile, $currentFileName))
258 | //before upload new file delete old file
259 | $this->fileStorage->removeFile($currentFileName);
260 |
261 | $fileData = $this->fileStorage->upload($mapping, $uploadedFile);
262 | $mapping->setFileDataPropertyValue($fileData);
263 | }
264 | }
265 |
266 |
267 | /**
268 | * Removes the file if necessary.
269 | *
270 | * @param \Doctrine\Common\EventArgs $args The event arguments.
271 | */
272 | public function postRemove(EventArgs $args)
273 | {
274 | $mappings = $this->getMappingsFromArgs($args);
275 |
276 | foreach ($mappings as $mapping) {
277 | if ($mapping->getDeleteOnRemove()) $this->fileStorage->removeFile($mapping->resolveFileName());
278 | }
279 |
280 | }
281 |
282 | }
283 |
--------------------------------------------------------------------------------
/File/File.php:
--------------------------------------------------------------------------------
1 | deleted = true;
34 | return $this;
35 | }
36 |
37 | public function isDeleted()
38 | {
39 | return $this->deleted ? true : false;
40 | }
41 |
42 |
43 | function isValid()
44 | {
45 | return true;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/FileStorage/FileStorageInterface.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | interface FileStorageInterface
14 | {
15 |
16 |
17 | /**
18 | * Uploads the files in the uploadable fields of the
19 | * specified object according to the property configuration.
20 | *
21 | * @param object $obj The object.
22 | */
23 | public function upload(PropertyMapping $mapping, File $file);
24 |
25 |
26 | /**
27 | * @abstract
28 | * @param $fullFileName
29 | * @return boolean
30 | */
31 | public function removeFile($fullFileName);
32 |
33 |
34 | /**
35 | * @abstract
36 | * @param $fullFileName
37 | * @return boolean
38 | */
39 | public function fileExists($fullFileName);
40 |
41 |
42 | /**
43 | * @abstract
44 | * @param \Symfony\Component\HttpFoundation\File\File $file
45 | * @param $fullFileName
46 | * @return boolean
47 | */
48 | public function isSameFile (File $file, $fullFileName);
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/FileStorage/FileSystemStorage.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | class FileSystemStorage implements FileStorageInterface
20 | {
21 |
22 | protected $webDir;
23 |
24 | protected $sameFileChecker;
25 |
26 | /**
27 | * Constructs a new instance of FileSystemStorage.
28 | *
29 | * @param
30 | */
31 | public function __construct($webDir = null)
32 | {
33 | $this->webDir = $webDir;
34 |
35 |
36 | // @codeCoverageIgnoreStart
37 | $this->sameFileChecker = function (File $file, $fullFileName)
38 | {
39 | return $file->getRealPath() == realpath($fullFileName);
40 | };
41 | // @codeCoverageIgnoreEnd
42 | }
43 |
44 | public function setWebDir($webDir )
45 | {
46 | $this->webDir = $webDir;
47 | }
48 |
49 | public function getWebDir()
50 | {
51 | return $this->webDir;
52 | }
53 |
54 | public function setSameFileChecker (\Closure $checker)
55 | {
56 | $this->sameFileChecker = $checker;
57 | }
58 |
59 |
60 |
61 |
62 | protected function getOriginalName(File $file)
63 | {
64 | return $file instanceof UploadedFile ?
65 | $file->getClientOriginalName() : $file->getFilename();
66 | }
67 |
68 |
69 | protected function getMimeType(File $file)
70 | {
71 | return $file instanceof UploadedFile ?
72 | $file->getClientMimeType() : $file->getMimeType();
73 | }
74 |
75 |
76 | public function isSameFile (File $file, $fullFileName)
77 | {
78 | return call_user_func(
79 | $this->sameFileChecker,
80 | $file,
81 | $fullFileName);
82 |
83 | }
84 |
85 |
86 | protected function copyFile($source, $directory, $name)
87 | {
88 | $this->checkDirectory($directory);
89 | $target = $directory . DIRECTORY_SEPARATOR . basename($name);
90 |
91 | if (!@copy($source, $target)) {
92 | $error = error_get_last();
93 | throw new FileException(sprintf('Could not copy the file "%s" to "%s" (%s)', $source, $target, strip_tags($error['message'])));
94 | }
95 |
96 | @chmod($target, 0666 & ~umask());
97 |
98 | return new File($target);
99 | }
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | protected function checkDirectory ($directory)
108 | {
109 | if (!is_dir($directory)) {
110 | if (false === @mkdir($directory, 0777, true)) {
111 |
112 | // @codeCoverageIgnoreStart
113 | throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
114 | // @codeCoverageIgnoreEnd
115 | }
116 | } elseif (!is_writable($directory)) {
117 | // @codeCoverageIgnoreStart
118 | throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
119 | // @codeCoverageIgnoreEnd
120 | }
121 |
122 | return true;
123 | }
124 |
125 |
126 | /**
127 | * {@inheritDoc}
128 | * File may be \Symfony\Component\HttpFoundation\File\File or \Symfony\Component\HttpFoundation\File\UploadedFile
129 | */
130 | public function upload(PropertyMapping $mapping, File $file)
131 | {
132 | $originalName = $this->getOriginalName($file);
133 | $mimeType = $this->getMimeType($file);
134 |
135 | //transform filename and directory name if namer exists in mapping definition
136 | list ($fileName, $webPath) = $mapping->prepareFileName($originalName, $this);
137 | $fullFileName = $mapping->resolveFileName($fileName);
138 |
139 | //check if file already placed in needed position
140 | if (!$this->isSameFile($file, $fullFileName)) {
141 | $fileInfo = pathinfo($fullFileName);
142 |
143 | if ($file instanceof UploadedFile)
144 | {
145 | $this->checkDirectory($fileInfo['dirname']);
146 | $file->move($fileInfo['dirname'], $fileInfo['basename']);
147 | }
148 | else $this->copyFile($file->getPathname(), $fileInfo['dirname'], $fileInfo['basename']);
149 | }
150 |
151 |
152 | $fileData = array(
153 | 'fileName' => $fileName,
154 | 'originalName' => $originalName,
155 | 'mimeType' => $mimeType,
156 | 'size' => filesize($fullFileName),
157 | 'path' => $webPath
158 | );
159 |
160 | if (!$fileData['path'])
161 | $fileData['path'] = substr($fullFileName, strlen($this->webDir));
162 |
163 |
164 | $ext = substr($originalName,strrpos ($originalName,'.')+1);
165 |
166 | if ((in_array($fileData['mimeType'], array('image/png', 'image/jpeg', 'image/pjpeg')) ||
167 | in_array ($ext,array ('jpeg','jpg','png')))
168 | && function_exists('getimagesize')
169 | ) {
170 | list($width, $height, $type) = @getimagesize($fullFileName);
171 | $fileData = array_merge($fileData, array(
172 | 'width' => $width, 'height' => $height
173 | ));
174 | }
175 |
176 | return $fileData;
177 | }
178 |
179 |
180 | /**
181 | * {@inheritDoc}
182 | */
183 | public function removeFile($fullFileName)
184 | {
185 |
186 | if ($fullFileName && file_exists($fullFileName)) {
187 | @unlink($fullFileName);
188 | return !file_exists($fullFileName);
189 | }
190 | return null;
191 | }
192 |
193 |
194 | /**
195 | * {@inheritDoc}
196 | */
197 | public function fileExists($fullFileName)
198 | {
199 | return file_exists($fullFileName);
200 | }
201 |
202 |
203 |
204 |
205 |
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/Form/DataTransformer/FileDataTransformer.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class FileDataTransformer implements DataTransformerInterface
17 | {
18 |
19 | const MODE_UPLOAD_FIELD = 'upload_field';
20 |
21 | const MODE_FILEDATA_FIELD = 'filedata_field';
22 |
23 | /**
24 | * @var \Iphp\FileStoreBundle\Mapping\PropertyMapping
25 | */
26 | protected $mapping;
27 |
28 | /**
29 | * @var \Iphp\FileStoreBundle\FileStorage\FileStorageInterface
30 | */
31 | protected $fileStorage;
32 |
33 |
34 | protected $mode;
35 |
36 |
37 | function __construct(FileStorageInterface $fileStorage)
38 | {
39 | $this->fileStorage = $fileStorage;
40 | }
41 |
42 |
43 | /**
44 | * Sets in PRE_BIND form event
45 | * @param PropertyMapping $mapping
46 | * @param $mode
47 | * @return $this
48 | */
49 | public function setMapping(PropertyMapping $mapping, $mode)
50 | {
51 | $this->mapping = $mapping;
52 | $this->mode = $mode;
53 | return $this;
54 | }
55 |
56 |
57 | public function transform($fileDataFromDb)
58 | {
59 | return $fileDataFromDb;
60 | }
61 |
62 |
63 | /**
64 | * array with 2 items - file (UploadedFile) and delete (checkbox)
65 | * @param $fileDataFromForm
66 | * @return int
67 | */
68 | public function reverseTransform($fileDataFromForm)
69 | {
70 | //if file field != file upload field - no need to store 'delete' in serialized file data
71 | if (isset($fileDataFromForm['delete']) && !$fileDataFromForm['delete'])
72 | unset($fileDataFromForm['delete']);
73 |
74 |
75 | if ($this->mapping && isset($fileDataFromForm['delete']) && $fileDataFromForm['delete']) {
76 |
77 | if ($this->mode == self::MODE_FILEDATA_FIELD) {
78 | return null;
79 | }
80 |
81 | //Todo: move to uploaderListener
82 | //File may no exists
83 | try {
84 | $this->fileStorage->removeFile($this->mapping->resolveFileName($fileDataFromForm['fileName']));
85 |
86 | } catch (\Exception $e) {
87 | }
88 |
89 | }
90 |
91 | return isset($fileDataFromForm['file']) ? $fileDataFromForm['file'] :
92 | ($this->mode == self::MODE_UPLOAD_FIELD ? null : $fileDataFromForm);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Form/DataTransformer/FileDataViewTransformer.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class FileDataViewTransformer implements DataTransformerInterface
13 | {
14 |
15 |
16 | public function transform($fileDataFromDb)
17 | {
18 | return $fileDataFromDb;
19 | }
20 |
21 |
22 | /**
23 | * array with 2 items - file (UploadedFile) and delete (checkbox)
24 | * @param $fileDataFromForm
25 | * @return int
26 | */
27 | public function reverseTransform($fileDataFromForm)
28 | {
29 | return $fileDataFromForm;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Form/Type/FileType.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | class FileType extends AbstractType
27 | {
28 |
29 | /**
30 | * @var \Iphp\FileStoreBundle\Mapping\PropertyMappingFactory
31 | */
32 | protected $mappingFactory;
33 |
34 | /**
35 | * @var \Iphp\FileStoreBundle\DataStorage\DataStorageInterface
36 | */
37 | protected $dataStorage;
38 |
39 | /**
40 | * @var \Iphp\FileStoreBundle\FileStorage\FileStorageInterface
41 | */
42 | protected $fileStorage;
43 |
44 | public function __construct(PropertyMappingFactory $mappingFactory,
45 | DataStorageInterface $dataStorage,
46 | FileStorageInterface $fileStorage)
47 | {
48 | $this->mappingFactory = $mappingFactory;
49 | $this->dataStorage = $dataStorage;
50 | $this->fileStorage = $fileStorage;
51 | }
52 |
53 | public function configureOptions (OptionsResolver $resolver)
54 | {
55 | $resolver->setDefaults(array(
56 | 'read_only' => false,
57 | 'upload' => true,
58 | 'show_uploaded' => true,
59 | 'show_preview' => true
60 | ));
61 | }
62 |
63 | public function setDefaultOptions(OptionsResolverInterface $resolver)
64 | {
65 | $resolver->setDefaults(array(
66 | 'read_only' => false,
67 | 'upload' => true,
68 | 'show_uploaded' => true,
69 | 'show_preview' => true
70 | ));
71 | }
72 |
73 |
74 | /**
75 | * {@inheritdoc}
76 | */
77 | public function buildForm(FormBuilderInterface $builder, array $options)
78 | {
79 |
80 | $transformer = new FileDataTransformer($this->fileStorage);
81 | $subscriber = new FileTypeBindSubscriber(
82 | $this->mappingFactory,
83 | $this->dataStorage,
84 | $transformer,
85 | $options);
86 | $builder->addEventSubscriber($subscriber);
87 |
88 |
89 | // $builder->add('file', 'file', array('required' => false))
90 | // ->add('delete', 'checkbox', array('required' => false))
91 | $builder->addViewTransformer($transformer);
92 |
93 |
94 |
95 | //for sonata admin
96 | // ->addViewTransformer(new FileDataViewTransformer());
97 | }
98 |
99 | //for using iphp_file_widget from Resources/views/Form/fields.html.twig
100 | public function getBlockPrefix()
101 | {
102 | return 'iphp_file';
103 | }
104 |
105 |
106 | public function buildView(FormView $view, FormInterface $form, array $options)
107 | {
108 | $view->vars['upload'] = $options['upload'];
109 | $view->vars['show_preview'] = $options['show_preview'];
110 |
111 | $view->vars['file_data'] = $view->vars['value'];
112 |
113 | }
114 |
115 |
116 | }
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/Form/Type/FileTypeBindSubscriber.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class FileTypeBindSubscriber implements EventSubscriberInterface
15 | {
16 |
17 | /**
18 | * @var \Iphp\FileStoreBundle\Mapping\PropertyMappingFactory
19 | */
20 | private $mappingFactory;
21 |
22 | /**
23 | * @var \Symfony\Component\Form\DataTransformerInterface
24 | */
25 | private $transformer;
26 |
27 |
28 | /**
29 | * @var \Iphp\FileStoreBundle\DataStorage\DataStorageInterface
30 | */
31 | private $dataStorage;
32 |
33 | public function __construct(PropertyMappingFactory $mappingFactory,
34 | DataStorageInterface $dataStorage,
35 | FileDataTransformer $transformer,
36 | array $options = array())
37 | {
38 | $this->mappingFactory = $mappingFactory;
39 | $this->dataStorage = $dataStorage;
40 | $this->transformer = $transformer;
41 | }
42 |
43 | public static function getSubscribedEvents()
44 | {
45 | return array(
46 | FormEvents::PRE_SUBMIT => 'preBind',
47 | FormEvents::PRE_SET_DATA => 'preSet');
48 | }
49 |
50 |
51 | public function preSet(FormEvent $event)
52 | {
53 | $form = $event->getForm();
54 | $propertyName = $form->getName();
55 |
56 | $obj = $form->getParent()->getData();
57 |
58 | if (!$obj) return;
59 |
60 | $mapping = $this->mappingFactory->getMappingFromField($obj,
61 | $this->dataStorage->getReflectionClass($obj),
62 | $propertyName);
63 |
64 | if ($mapping) {
65 | if ($propertyName == $mapping->getFileUploadPropertyName())
66 | $form->add('file', \Symfony\Component\Form\Extension\Core\Type\FileType::class, ['required' => false]);
67 |
68 | if ($propertyName == $mapping->getFileDataPropertyName())
69 | $form->add('delete', \Symfony\Component\Form\Extension\Core\Type\CheckboxType::class, ['required' => false]);
70 | }
71 | }
72 |
73 | public function preBind(FormEvent $event)
74 | {
75 | $form = $event->getForm();
76 | $propertyName = $form->getName();
77 | $obj = $form->getParent()->getData();
78 |
79 | if (!$obj) return;
80 |
81 | $mapping = $this->mappingFactory->getMappingFromField($obj,
82 | $this->dataStorage->getReflectionClass($obj),
83 | $propertyName);
84 |
85 | if ($mapping) {
86 | $this->transformer->setMapping($mapping,
87 | $mapping->getFileUploadPropertyName() == $propertyName ?
88 | FileDataTransformer::MODE_UPLOAD_FIELD : FileDataTransformer::MODE_FILEDATA_FIELD
89 | );
90 | }
91 | }
92 |
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/IphpFileStoreBundle.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class IphpFileStoreBundle extends Bundle
12 | {
13 |
14 | public function build(ContainerBuilder $container)
15 | {
16 | parent::build($container);
17 | $container->addCompilerPass(new FormPass());
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Mapping/Annotation/Uploadable.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Uploadable
13 | {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Mapping/Annotation/UploadableField.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class UploadableField
13 | {
14 | /**
15 | * @var string $mapping
16 | */
17 | protected $mapping;
18 |
19 | /**
20 | * @var string $name
21 | */
22 | protected $fileUploadPropertyName;
23 |
24 | /**
25 | * @var string $fileNameProperty
26 | */
27 | protected $fileDataPropertyName;
28 |
29 | /**
30 | * Constructs a new instance of UploadableField.
31 | *
32 | * @param array $options The options.
33 | */
34 | public function __construct(array $options)
35 | {
36 | if (isset($options['mapping'])) {
37 | $this->mapping = $options['mapping'];
38 | } else {
39 | throw new \InvalidArgumentException('The "mapping" attribute of UploadableField is required.');
40 | }
41 | if (isset($options['fileDataProperty']))
42 | {
43 | $this->setFileDataPropertyName($options['fileDataProperty']);
44 | }
45 | }
46 |
47 | /**
48 | * Gets the mapping name.
49 | *
50 | * @return string The mapping name.
51 | */
52 | public function getMapping()
53 | {
54 | return $this->mapping;
55 | }
56 |
57 | /**
58 | * Sets the mapping name.
59 | *
60 | * @param $mapping The mapping name.
61 | */
62 | public function setMapping($mapping)
63 | {
64 | $this->mapping = $mapping;
65 | }
66 |
67 | /**
68 | * Gets the property name.
69 | *
70 | * @return string The property name.
71 | */
72 | public function getFileUploadPropertyName()
73 | {
74 | return $this->fileUploadPropertyName;
75 | }
76 |
77 | /**
78 | * Sets the property name.
79 | *
80 | * @param $propertyName The property name.
81 | */
82 | public function setFileUploadPropertyName($propertyName)
83 | {
84 | $this->fileUploadPropertyName = $propertyName;
85 | }
86 |
87 | /**
88 | * Gets the file name property.
89 | * By default using propertyName
90 | * @return string The file name property.
91 | */
92 | public function getFileDataPropertyName()
93 | {
94 | return $this->fileDataPropertyName ? $this->fileDataPropertyName : $this->fileUploadPropertyName;
95 | }
96 |
97 | /**
98 | * Sets the file data property name.
99 | *
100 | * @param $fileNameProperty The file name property.
101 | */
102 | public function setFileDataPropertyName ($fileDataPropertyName)
103 | {
104 | $this->fileDataPropertyName = $fileDataPropertyName;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Mapping/PropertyMapping.php:
--------------------------------------------------------------------------------
1 |
15 | *
16 | */
17 | class PropertyMapping
18 | {
19 |
20 |
21 | protected $obj;
22 |
23 | /**
24 | * @var array $config
25 | */
26 | protected $config;
27 |
28 | /**
29 | * @var \Iphp\FileStoreBundle\Naming\NamerServiceInvoker $namerServiceInvoker
30 | */
31 | protected $namerServiceInvoker;
32 |
33 | /**
34 | * @var \ReflectionProperty $property reflection property that represents the annotated property
35 | */
36 | protected $fileUploadProperty;
37 |
38 | /**
39 | * @var \ReflectionProperty $fileNameProperty reflection property that represents property which holds data
40 | */
41 | protected $fileDataProperty;
42 |
43 | /**
44 | * @var string $mappingName
45 | */
46 | protected $mappingName;
47 |
48 |
49 | function __construct($obj, $config, NamerServiceInvoker $namerServiceInvoker)
50 | {
51 | $this->obj = $obj;
52 | $this->setConfig($config);
53 | $this->namerServiceInvoker = $namerServiceInvoker;
54 | }
55 |
56 |
57 | /**
58 | * Sets the reflection property that represents the annotated
59 | * property.
60 | *
61 | * @param \ReflectionProperty $property The reflection property.
62 | */
63 | public function setFileUploadProperty(\ReflectionProperty $property)
64 | {
65 | $this->fileUploadProperty = $property;
66 | $this->fileUploadProperty->setAccessible(true);
67 | }
68 |
69 |
70 | /**
71 | * Sets the reflection property that represents the property
72 | * which holds the file name for the mapping.
73 | *
74 | * @param \ReflectionProperty $fileNameProperty The reflection property.
75 | */
76 | public function setFileDataProperty(\ReflectionProperty $fileNameProperty)
77 | {
78 | $this->fileDataProperty = $fileNameProperty;
79 | $this->fileDataProperty->setAccessible(true);
80 | }
81 |
82 |
83 | /**
84 | * Invoke file namers
85 | * @param $fileName
86 | * @return mixed
87 | */
88 | public function useFileNamer($fileName)
89 | {
90 | if ($this->hasNamer()) {
91 | foreach ($this->config['namer'] as $method => $namer) {
92 | $fileName = $this->namerServiceInvoker->rename($namer['service'], $method, $this, $fileName,
93 | isset($namer['params']) ? $namer['params'] : array());
94 | }
95 | }
96 | return $fileName;
97 | }
98 |
99 |
100 | /**
101 | * Determines if the mapping has a custom namer configured.
102 | *
103 | * @return bool True if has namer, false otherwise.
104 | */
105 | public function hasNamer()
106 | {
107 | return isset($this->config['namer']) && $this->config['namer'];
108 | }
109 |
110 |
111 | /**
112 | * Determines if the mapping requires store full path to file
113 | * @return bool
114 | */
115 | public function isStoreFullDir()
116 | {
117 | return isset($this->config['store_fulldir']) && $this->config['store_fulldir'];
118 | }
119 |
120 |
121 | /**
122 | * Determines if the mapping has a custom directory namer configured.
123 | *
124 | * @return bool True if has directory namer, false otherwise.
125 | */
126 | public function hasDirectoryNamer()
127 | {
128 | return isset($this->config['directory_namer']) && $this->config['directory_namer'];
129 | }
130 |
131 | /**
132 | * create subdirectory name based on chain of directory namers
133 | *
134 | * @return array directory name and web path to file
135 | */
136 | public function useDirectoryNamer($fileName, $clientOriginalName)
137 | {
138 | $path = '';
139 |
140 | if ($this->hasDirectoryNamer()) {
141 | foreach ($this->config['directory_namer'] as $method => $namer) {
142 |
143 | $replaceMode = $method == 'replace' ||
144 | (isset($namer['params']['replace']) && $namer['params']['replace']);
145 |
146 |
147 | $subPath = $this->namerServiceInvoker->rename($namer['service'],
148 | $method,
149 | $this,
150 | $replaceMode ? $path : $fileName,
151 | isset($namer['params']) ? $namer['params'] : array());
152 |
153 |
154 | /* $subPath = call_user_func(
155 | array($this->container->get($namer['service']), $method . 'Rename'),
156 | $this,
157 | $replaceMode ? $path : $fileName,
158 | isset($namer['params']) ? $namer['params'] : array());*/
159 |
160 |
161 | if ($replaceMode) $path = $subPath;
162 | else $path .= ($subPath ? '/' : '') . $subPath;
163 | }
164 |
165 | }
166 |
167 | return $path;
168 | }
169 |
170 |
171 | public function needResolveCollision($fileName, FileStorageInterface $fileStorage)
172 | {
173 | //print "\n -->".$fileName;
174 | return !$this->isOverwriteDuplicates() && $fileStorage->fileExists($this->resolveFileName($fileName));
175 | }
176 |
177 |
178 | /**
179 | * @param $originalName
180 | * @param \Iphp\FileStoreBundle\FileStorage\FileStorageInterface $fileStorage
181 | * @return array relative or full fileName and file path at web
182 | * @throws \Exception
183 | */
184 | public function prepareFileName($originalName, FileStorageInterface $fileStorage)
185 | {
186 | $fileName = $origName = $this->useFileNamer($originalName);
187 | $dirName = $this->useDirectoryNamer($fileName, $originalName);
188 | if (substr($dirName,-1) != '/') $dirName.='/';
189 |
190 | $try = 0;
191 |
192 | while ($this->needResolveCollision( $dirName . $fileName , $fileStorage)) {
193 | if ($try > 15)
194 | throw new \Exception ("Can't resolve collision for file " . $fileName);
195 |
196 | $fileName = $this->resolveFileCollision($origName, $originalName, ++$try);
197 | }
198 |
199 |
200 | return array(
201 | //file system path
202 | ($this->isStoreFullDir() ? $this->getUploadDir() : '') . $dirName. $fileName ,
203 | //web path
204 | $this->getUploadPath() ? $this->getUploadPath() . $dirName . urlencode($fileName) : '');
205 | }
206 |
207 |
208 | /**
209 | * @param $fileName
210 | * @param $clientOriginalName
211 | * @param int $attempt
212 | * @return string new file path
213 | * @throws \Exception
214 | */
215 | public function resolveFileCollision($fileName, $clientOriginalName, $attempt = 1)
216 | {
217 |
218 | if ($this->hasNamer()) {
219 | $firstNamer = current($this->config['namer']);
220 |
221 |
222 | return $this->namerServiceInvoker->resolveCollision ($firstNamer['service'], $fileName, $attempt);
223 | }
224 |
225 | throw new \Exception ('Filename resolving collision not supported (namer is empty).Duplicate filename ' . $fileName);
226 | }
227 |
228 |
229 | public function getUploadDir()
230 | {
231 | return $this->config['upload_dir'];
232 | }
233 |
234 |
235 | public function getUploadPath()
236 | {
237 | return $this->config['upload_path'];
238 | }
239 |
240 |
241 | /**
242 | * Sets the configured configuration mapping.
243 | *
244 | * @param array $mapping The mapping;
245 | */
246 | public function setConfig(array $config)
247 | {
248 | $this->config = $config;
249 | }
250 |
251 | /**
252 | * Gets the configured configuration mapping name.
253 | *
254 | * @return string The mapping name.
255 | */
256 | public function getMappingName()
257 | {
258 | return $this->mappingName;
259 | }
260 |
261 | /**
262 | * Sets the configured configuration mapping name.
263 | *
264 | * @param $mappingName The mapping name.
265 | */
266 | public function setMappingName($mappingName)
267 | {
268 | $this->mappingName = $mappingName;
269 | }
270 |
271 | /**
272 | * Gets the name of the annotated property.
273 | *
274 | * @return string The name.
275 | */
276 | public function getFileUploadPropertyName()
277 | {
278 | return $this->fileUploadProperty->getName();
279 | }
280 |
281 | /**
282 | * Gets the value of the annotated property.
283 | * @return \Symfony\Component\HttpFoundation\File\UploadedFile
284 | */
285 | public function getFileUploadPropertyValue()
286 | {
287 | return $this->fileUploadProperty->getValue($this->obj);
288 | }
289 |
290 |
291 | public function setFileUploadPropertyValue($file)
292 | {
293 | $this->fileUploadProperty->setValue($this->obj, $file);
294 | }
295 |
296 | public function setFileDataPropertyValue($fileData)
297 | {
298 | $this->fileDataProperty->setValue($this->obj, $fileData);
299 | }
300 |
301 |
302 | public function getFileDataPropertyValue()
303 | {
304 | return $this->fileDataProperty->getValue($this->obj);
305 | }
306 |
307 |
308 | /**
309 | * Gets the configured file name property name.
310 | *
311 | * @return string The name.
312 | */
313 | public function getFileDataPropertyName()
314 | {
315 | return $this->fileDataProperty->getName();
316 | }
317 |
318 | /**
319 | * Property for upload and property for file data is one property
320 | * @return bool
321 | */
322 | public function isUseOneProperty()
323 | {
324 |
325 | return $this->getFileDataPropertyName() == $this->getFileUploadPropertyName() ? true : false;
326 | }
327 |
328 |
329 |
330 | /**
331 | * Determines if the file should be deleted upon removal of the
332 | * entity. Default true
333 | *
334 | * @return bool True if delete on remove, false otherwise.
335 | */
336 | public function getDeleteOnRemove()
337 | {
338 | return !isset($this->config['delete_on_remove']) || $this->config['delete_on_remove'];
339 | }
340 |
341 |
342 | /**
343 | * @return bool True if overwrite file duplicates, if false - using resolve collision
344 | */
345 | public function isOverwriteDuplicates()
346 | {
347 | return isset($this->config['overwrite_duplicates']) && $this->config['overwrite_duplicates'];
348 | }
349 |
350 |
351 | public function getObj()
352 | {
353 | return $this->obj;
354 | }
355 |
356 |
357 | public function resolveFileName($fileName = null)
358 | {
359 | if (!$fileName) {
360 | $fileData = $this->getFileDataPropertyValue();
361 | if ($fileData && isset($fileData['fileName'])) $fileName = $fileData['fileName'];
362 | }
363 | if (!$fileName) return null;
364 |
365 | $dir = $this->isStoreFullDir() ? '' : $this->getUploadDir();
366 | return $dir . (substr($dir,-1) != '/' && substr($fileName,0,1) != '/' ? '/' : ''). $fileName;
367 | }
368 |
369 |
370 | }
371 |
--------------------------------------------------------------------------------
/Mapping/PropertyMappingFactory.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class PropertyMappingFactory
17 | {
18 | /**
19 | * @var \Iphp\FileStoreBundle\Naming\NamerServiceInvoker $namerServiceInvoker
20 | */
21 | protected $namerServiceInvoker;
22 |
23 | /**
24 | * @var \Iphp\FileStoreBundle\Driver\AnnotationDriver $driver
25 | */
26 | protected $driver;
27 |
28 |
29 | /**
30 | * @var array $mappingsConfig MappingConfiguration
31 | */
32 | protected $mappingsConfig = array();
33 |
34 | /**
35 | * Constructs a new instance of PropertyMappingFactory.
36 | *
37 | * @param \Iphp\FileStoreBundle\Naming\NamerServiceInvoker $namerServiceInvoker Object for invoke rename methods.
38 | * @param \Iphp\FileStoreBundle\Driver\AnnotationDriver $driver The driver.
39 | * @param array $mappings The configured mappings.
40 | */
41 | public function __construct(NamerServiceInvoker $namerServiceInvoker,
42 | AnnotationDriver $driver,
43 | array $mappingsConfig)
44 | {
45 | $this->namerServiceInvoker = $namerServiceInvoker;
46 | $this->driver = $driver;
47 | $this->mappingsConfig = $mappingsConfig;
48 | }
49 |
50 |
51 | /**
52 | * Creates an array of PropetyMapping objects which contain the
53 | * configuration for the uploadable fields in the specified
54 | * object.
55 | *
56 | * @param object $obj The object.
57 | * @param \ReflectionClass $class
58 | * @return \Iphp\FileStoreBundle\Mapping\PropertyMapping[] objects.
59 | */
60 | public function getMappingsFromObject($obj, \ReflectionClass $class)
61 | {
62 | if (!$this->hasAnnotations($class)) return array();
63 |
64 | $mappings = array();
65 | foreach ($this->driver->readUploadableFields($class) as $field) {
66 | $mappings[] = $this->createMapping($obj, $class, $field);
67 | }
68 |
69 | return $mappings;
70 | }
71 |
72 |
73 | /**
74 | * Creates a property mapping object which contains the
75 | * configuration for the specified uploadable field.
76 | *
77 | * @param object $obj The object.
78 | * @param \ReflectionClass $class
79 | * @param string $field entity field name
80 | * @param bool $allFields search all fields (if upload field and file data field are separate)
81 | * @return null|\Iphp\FileStoreBundle\Mapping\PropertyMapping The property mapping.
82 | */
83 | public function getMappingFromField($obj, \ReflectionClass $class, $field, $allFields = true)
84 | {
85 | if (!$this->hasAnnotations($class)) return null;
86 |
87 | $annotation = $this->driver->readUploadableField($class, $field);
88 |
89 | if (!$annotation && $allFields) {
90 | $propertyAnnotations= $this->driver->readUploadableFields($class);
91 |
92 | foreach ($propertyAnnotations as $propertyAnnotation)
93 | {
94 | if ($propertyAnnotation->getFileDataPropertyName() == $field ||
95 | $propertyAnnotation->getFileUploadPropertyName() == $field)
96 | {
97 | $annotation = $propertyAnnotation;
98 | break;
99 | }
100 | }
101 | }
102 | if (null === $annotation) return null;
103 |
104 | return $this->createMapping($obj, $class, $annotation);
105 | }
106 |
107 | public function hasAnnotations(\ReflectionClass $class)
108 | {
109 | return null !== $this->driver->readUploadable($class);
110 | }
111 |
112 | /**
113 | * Creates the property mapping from the read annotation and configured mapping.
114 | *
115 | * @param object $obj The object.
116 | * @param \ReflectionClass $class
117 | * @param \Iphp\FileStoreBundle\Mapping\Annotation\UploadableField $field The read annotation.
118 | * @return \Iphp\FileStoreBundle\Mapping\PropertyMapping The property mapping.
119 | * @throws \InvalidArgumentException
120 | */
121 | protected function createMapping($obj, \ReflectionClass $class, UploadableField $field)
122 | {
123 | if (!array_key_exists($field->getMapping(), $this->mappingsConfig)) {
124 | throw new \InvalidArgumentException(sprintf(
125 | 'No mapping named "%s" configured.', $field->getMapping()
126 | ));
127 | }
128 |
129 | $config = $this->mappingsConfig[$field->getMapping()];
130 |
131 | $mapping = new PropertyMapping($obj, $config, $this->namerServiceInvoker);
132 | $mapping->setFileUploadProperty($class->getProperty($field->getFileUploadPropertyName()));
133 | $mapping->setFileDataProperty($class->getProperty($field->getFileDataPropertyName()));
134 |
135 | $mapping->setMappingName($field->getMapping());
136 |
137 |
138 | return $mapping;
139 | }
140 |
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/Naming/DefaultDirectoryNamer.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class DefaultDirectoryNamer
11 | {
12 |
13 |
14 | /**
15 | *
16 | */
17 | function propertyRename(PropertyMapping $propertyMapping, $fileName, $params)
18 | {
19 |
20 | if (isset($params['use_field_name']) && $params['use_field_name'])
21 | return $propertyMapping->getFileDataPropertyName();
22 |
23 | $obj = $propertyMapping->getObj();
24 | $field = isset($params['field']) && $params['field'] ? $params['field'] : 'id';
25 |
26 |
27 | $fields = explode('/', $field);
28 | $path = '';
29 |
30 |
31 | foreach ($fields as $f) {
32 | if (strpos($f, '.')) {
33 | $str = 'return $obj->get' . implode('()->get', array_map('ucfirst', explode('.', $f))) . '();';
34 | $fieldValue = eval ($str);
35 | } else $fieldValue = $obj->{'get' . ucfirst($f)}();
36 | $path .= ($path ? '/' : '') . $fieldValue;
37 | }
38 |
39 | return $path;
40 | }
41 |
42 |
43 | function entityNameRename(PropertyMapping $propertyMapping, $fileName, $params)
44 | {
45 | return implode('', array_slice(explode('\\', get_class($propertyMapping->getObj())), -1));
46 | }
47 |
48 |
49 | function replaceRename(PropertyMapping $propertyMapping, $name, $params)
50 | {
51 | return strtr($name, $params);
52 | }
53 |
54 |
55 | function dateRename(PropertyMapping $propertyMapping, $fileName, $params)
56 | {
57 | $obj = $propertyMapping->getObj();
58 |
59 | $field = isset($params['field']) && $params['field'] ? $params['field'] : 'id';
60 | $depth = isset($params['depth']) && $params['depth'] ? strtolower($params['depth']) : 'day';
61 |
62 | $date = $obj->{'get' . ucfirst($field)}();
63 | $date = $date ? $date->getTimestamp() : time();
64 |
65 | $tpl = "Y/m/d";
66 | if ($depth == 'month') $tpl = "Y/m";
67 | if ($depth == 'year') $tpl = "Y";
68 |
69 | $dirName = date($tpl, $date);
70 |
71 | return $dirName;
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/Naming/DefaultNamer.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class DefaultNamer
11 | {
12 | /**
13 | * Filename translitaration renamin
14 | * @param \Iphp\FileStoreBundle\Mapping\PropertyMapping $propertyMapping
15 | * @param $name
16 | * @return string
17 | */
18 | public function translitRename(PropertyMapping $propertyMapping, $name)
19 | {
20 | $name = transliterator_transliterate("Any-Latin; Latin-ASCII; [\u0100-\u7fff] remove", $name);
21 | $name = preg_replace('/[^\\pL\d.]+/u', '-', $name);
22 | $name = preg_replace('/[-\s]+/', '-', $name);
23 | $name = strtolower(trim($name, '-'));
24 |
25 | return $name;
26 | }
27 |
28 | /**
29 | * Rename file name based on value of object property (default: id)
30 | * @param \Iphp\FileStoreBundle\Mapping\PropertyMapping $propertyMapping
31 | * @param $name
32 | * @param $params
33 | * @return string
34 | */
35 | public function propertyRename(PropertyMapping $propertyMapping, $name, $params)
36 | {
37 | $fieldValue = $this->getFieldValueByParam($propertyMapping, $params);
38 | if ($fieldValue) $name = $fieldValue . substr($name, strrpos($name, '.'));
39 | return $name;
40 | }
41 |
42 | protected function getFieldValueByParam(PropertyMapping $propertyMapping, $params)
43 | {
44 | $obj = $propertyMapping->getObj();
45 |
46 | $fieldValue = '';
47 | if (isset($params['use_field_name']) && $params['use_field_name']) {
48 | $fieldValue = $propertyMapping->getFileDataPropertyName();
49 | } else {
50 | $field = isset($params['field']) && $params['field'] ? $params['field'] : 'id';
51 | $fieldValue = $obj->{'get' . ucfirst($field)}();
52 | }
53 |
54 | if (!$fieldValue) $fieldValue = $obj->getId();
55 | return $fieldValue;
56 | }
57 |
58 | public function propertyPrefixRename(PropertyMapping $propertyMapping, $name, $params)
59 | {
60 | $fieldValue = $this->getFieldValueByParam($propertyMapping, $params);
61 | $delimiter = isset($params['delimiter']) && $params['delimiter'] ? $params['delimiter'] : '-';
62 |
63 | return $fieldValue . $delimiter . $name;
64 | }
65 |
66 | public function propertyPostfixRename(PropertyMapping $propertyMapping, $name, $params)
67 | {
68 | $fieldValue = $this->getFieldValueByParam($propertyMapping, $params);
69 | $delimiter = isset($params['delimiter']) && $params['delimiter'] ? $params['delimiter'] : '-';
70 |
71 | $ppos = strrpos($name, '.');
72 | return substr($name, 0, $ppos) . $delimiter . $fieldValue . '' . substr($name, $ppos);
73 |
74 | }
75 |
76 | public function replaceRename(PropertyMapping $propertyMapping, $name, $params)
77 | {
78 | return strtr($name, $params);
79 | }
80 |
81 | /**
82 | * Разрешение коллизий с одинаковыми названиями файлов
83 | *
84 | * @param $name
85 | * @param int $attempt
86 | * @return string
87 | */
88 | public function resolveCollision($name, $attempt = 1)
89 | {
90 | $addition = $attempt;
91 | if ($attempt > 10) $addition = date('Y_m_d_H_i_s');
92 |
93 | $ppos = strrpos($name, '.');
94 |
95 | return ($ppos === false ? $name : substr($name, 0, $ppos))
96 | . '_' . $addition . ''
97 | . ($ppos === false ? '' : substr($name, $ppos));
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Naming/NamerServiceInvoker.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class NamerServiceInvoker
11 | {
12 |
13 | protected $container;
14 |
15 | public function __construct(ContainerInterface $container)
16 | {
17 | $this->container = $container;
18 | }
19 |
20 |
21 | public function rename($serviceName, $method, PropertyMapping $propertyMapping, $fileName, $args = array())
22 | {
23 |
24 | return call_user_func(
25 | array($this->container->get($serviceName), $method . 'Rename'),
26 | $propertyMapping,
27 | $fileName,
28 | $args);
29 |
30 | }
31 |
32 |
33 | public function resolveCollision ($serviceName, $fileName, $attempt)
34 | {
35 | return call_user_func(
36 | array($this->container->get($serviceName), 'resolveCollision'), $fileName, $attempt);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | IphpFileStoreBundle - Symfony 2 Doctrine ORM file upload bundle
2 | ===================
3 |
4 | [](http://travis-ci.org/vitiko/IphpFileStoreBundle)
5 | [](https://packagist.org/packages/iphp/filestore-bundle)
6 | [](https://codeclimate.com/github/vitiko/IphpFileStoreBundle)
7 |
8 | The IphpFileStoreBundle is a Symfony2 bundle that automates file uploads that are attached to an entity.
9 | The bundle will automatically name and save the uploaded file according to the configuration specified on a per property
10 | basis using a mix of configuration and annotations.
11 | After the entity has been created and the file has been saved, array with data of uploaded file
12 | will be saved to according property.
13 | The bundle provide different ways to naming uploaded files and directories.
14 |
15 | For Russian documentation see http://symfonydev.ru/iphpfilestorebundle/
16 |
17 |
18 | ## Installation
19 |
20 | ### Get the bundle
21 |
22 |
23 | Add the following lines in your composer.json:
24 | ```
25 | {
26 | "require": {
27 | "iphp/filestore-bundle" : "@stable"
28 | }
29 | }
30 | ```
31 |
32 | ### Initialize the bundle
33 |
34 | To start using the bundle, register the bundle in your application's kernel class:
35 |
36 | ``` php
37 | // app/AppKernel.php
38 | public function registerBundles()
39 | {
40 | $bundles = array(
41 | // ...
42 | new Iphp\FileStoreBundle\IphpFileStoreBundle(),
43 | );
44 | )
45 | ```
46 |
47 |
48 | ## Usage
49 |
50 | IphpFileStoreBundle try to handle file uploads according to a combination
51 | of configuration parameters and annotations. In order to have your upload
52 | working you have to:
53 |
54 | * Define a basic configuration set
55 | * Annotate your Entities
56 |
57 |
58 | ### Configuration
59 |
60 | ``` yaml
61 | # app/config/config.yml
62 | iphp_file_store:
63 | mappings:
64 | photo:
65 | upload_dir: %kernel.root_dir%/../web/photo
66 | upload_path: /photo
67 | ```
68 |
69 | The `upload_dir` and `upload_path` is the only required configuration options for an entity mapping.
70 |
71 | All options are listed below:
72 |
73 | - `upload_dir`: directory to upload the file to
74 | - `upload_path`: web path of upload dir
75 | - `namer`: configuration of file naming (See [Namers](#namers) section below)
76 | - `directory_namer`: configuration of directory naming
77 | - `delete_on_remove`: Set to true if the file should be deleted from the
78 | filesystem when the entity is removed
79 | - `overwrite_duplicates`: Set to true if the file with same name will be overwritten by a new file.
80 | In another case (by default), to the name of the new file will be added extra digits
81 |
82 |
83 |
84 | ### Annotate Entities
85 |
86 | In order for your entity to work with the bundle, you need to add a
87 | few annotations to it. First, annotate your class with the `Uploadable` annotation.
88 | This lets the bundle know that it should look for files to upload in your class when
89 | it is saved, inject the files when it is loaded and check to see if it needs to
90 | remove files when it is removed. Next, you should annotate the fields which hold
91 | the instance of `Symfony\Component\HttpFoundation\File\UploadedFile` when the form
92 | is submitted with the `UploadableField` annotation. The `UploadableField` annotation
93 | has a few required options. They are as follows:
94 |
95 | - `mapping`: The mapping specified in the bundle configuration to use
96 | - `fileDataProperty`: name of field, where are stored file data
97 |
98 |
99 | Lets look at an example using a fictional `Photo` ORM entity:
100 |
101 | #### recommended use case - upload in one field (uploadPhoto), store file data in another field (photo)
102 |
103 |
104 | ``` php
105 | getPhoto()['path'];
211 | ```
212 |
213 | or in a Twig template:
214 |
215 | ``` html
216 |
217 | ```
218 |
219 | Example of using entities with uploadable can be seen in [controller](https://github.com/vitiko/IphpFileStoreBundle/blob/master/Tests/Functional/TestBundle/Controller/DefaultController.php)
220 | and twig template [for uploading](https://github.com/vitiko/IphpFileStoreBundle/blob/master/Tests/Functional/TestBundle/Resources/views/Photo/index.html.twig)
221 | and [editing](https://github.com/vitiko/IphpFileStoreBundle/blob/master/Tests/Functional/TestBundle/Resources/views/Photo/edit.html.twig) entities.
222 |
223 |
224 | ###Example of interface with list of uploaded photos
225 |
226 | 
227 |
228 |
229 |
230 |
231 | ## Using form field type
232 |
233 | Form field type `Iphp\FileStoreBundle\Form\Type\FileType` can be used in admin class, created for SonataAdminBundle.
234 | If entity already has uploaded file - information about this file will be displayed. Also
235 | delete checkbox allows to delete uploaded file.
236 |
237 | ``` php
238 | addIdentifier('title')
252 | ->add ('date');
253 | }
254 |
255 | protected function configureFormFields(FormMapper $formMapper)
256 | {
257 | return $formMapper->add('title')
258 | ->add ('date')
259 | ->add('photo', IphpFileType::class);
260 | }
261 | }
262 | ```
263 |
264 | ### Example of sonata admin form for uploaded photo
265 | 
266 |
267 | ## Customizing form field
268 |
269 | For example we want show preview of original uploaded image, preview generated with LiipImagineBundle https://github.com/liip/LiipImagineBundle. For change ` ` of preview we need to modify original form field template wich defined in https://github.com/vitiko/IphpFileStoreBundle/blob/master/Resources/views/Form/fields.html.twig.
270 |
271 | When customizing the form field block in Twig, you have two options on where the customized form block can live:
272 |
273 | ### Method 1: Form theming
274 |
275 | The easiest way to customize the `iphp_file_widget` block is to customize it directly in the template that's actually rendering the form.
276 |
277 | ``` twig
278 | {% form_theme form _self %}
279 |
280 | {% block iphp_file_widget_image_preview %}
281 |
286 |
287 | {{ file_data.width ~ 'x' ~ file_data.height }}
288 | {% endblock iphp_file_widget_image_preview %}
289 | ```
290 | more info about form customization here http://symfony.com/doc/current/form/form_customization.html#form-theming
291 |
292 | ### Method 2: Override bundle template
293 |
294 | To override the bundle template, just copy the field.html.twig template from the vendor/iphp/filestore-bundle/Iphp/FileStoreBundle/Resources/views/Form/fields.html.twig to app/Resources/IphpFileStoreBundle/views/Form/fields.html.twig (the app/Resources/IphpFileStoreBundle directory won't exist, so you'll need to create it). You're now free to customize the template.
295 |
296 | for example, for display preview in all forms
297 | ``` twig
298 | {#app/Resources/IphpFileStoreBundle/views/Form/fields.html.twig#}
299 | {% extends 'IphpFileStoreBundle:Form:fields-base.html.twig' %}
300 |
301 | {% block iphp_file_widget_image_preview %}
302 |
307 |
308 | {{ file_data.width ~ 'x' ~ file_data.height }}
309 | {% endblock iphp_file_widget_image_preview %}
310 | ```
311 |
312 | more info about overriding templates from third-party bundle here http://symfony.com/doc/current/templating/overriding.html
313 |
314 |
315 | ## Namers
316 |
317 | The bundle uses namers to name the files and directories it saves to the filesystem. If no namer is
318 | configured for a mapping, the bundle will use default transliteration namer for files was uploaded.
319 | if you would like to change this then you can use one of the provided namers or implement a custom one.
320 |
321 | ### File Namers
322 |
323 |
324 | #### Translit
325 |
326 | Transliteration - replace cyrillic and other chars to ascii
327 |
328 | ``` yaml
329 | # app/config/config.yml
330 | iphp_file_store:
331 | mappings:
332 | some_entity:
333 | ...
334 | namer: ~ // default
335 | ```
336 |
337 |
338 | To cancel transliteration
339 | ``` yaml
340 | # app/config/config.yml
341 | iphp_file_store:
342 | mappings:
343 | some_entity:
344 | ...
345 | namer: false
346 | ```
347 |
348 | #### Using entity field value
349 |
350 | File name by value of entity field ( field name - title)
351 | ``` yaml
352 | # app/config/config.yml
353 | iphp_file_store:
354 | mappings:
355 | some_entity:
356 | namer:
357 | property:
358 | params: { field : title }
359 | translit: ~
360 | ```
361 |
362 | #### Adding entity field value
363 |
364 | Adding to the beginnng (propertyPrefix) or end (propertyPostfix) of file name value of entity field
365 | ``` yaml
366 | # app/config/config.yml
367 | iphp_file_store:
368 | mappings:
369 | some_entity:
370 | namer:
371 | translit: ~
372 | propertyPrefix: #or propertyPostfix
373 | params: { field : id, delimiter: "_" }
374 | ```
375 |
376 | #### Using entity field name
377 |
378 | One mapping can be used in multiple fields. Name of the field can be used for naming file
379 |
380 | ``` yaml
381 | # app/config/config.yml
382 | iphp_file_store:
383 | mappings:
384 | some_entity:
385 | namer:
386 | translit: ~
387 | propertyPostfix:
388 | params: { use_field_name : true }
389 | ```
390 |
391 | #### Replacing strings
392 |
393 | Params of replace namer are key-value pairs with search and replace strings
394 | ``` yaml
395 | # app/config/config.yml
396 | iphp_file_store:
397 | mappings:
398 | some_entity:
399 | namer:
400 | translit: ~
401 | propertyPostfix:
402 | params: { use_field_name : true }
403 | replace:
404 | params: { File : ~ }
405 | ```
406 |
407 |
408 | ### Directory Namers
409 |
410 | #### Create subdirectory by date
411 |
412 | For example: Uploaded file 123.jpg, entity createdAt field value 2013-01-01 - path to file will be 2013/01/123.jpg.
413 | Depth options - year, month, date.
414 |
415 | ``` yaml
416 | # app/config/config.yml
417 | iphp_file_store:
418 | mappings:
419 | some_entity:
420 | directory_namer:
421 | date:
422 | params: { field : createdAt, depth : month }
423 | ```
424 |
425 | #### Using entity field value
426 |
427 | ``` yaml
428 | # app/config/config.yml
429 | iphp_file_store:
430 | mappings:
431 | some_entity:
432 | directory_namer:
433 | property:
434 | params: { field : "id"}
435 | ```
436 |
437 | #### Using entity field name
438 |
439 | ``` yaml
440 | # app/config/config.yml
441 | iphp_file_store:
442 | mappings:
443 | some_entity:
444 | directory_namer:
445 | property:
446 | params: { use_field_name : true }
447 | ```
448 |
449 | #### Using entity class name
450 | ``` yaml
451 | # app/config/config.yml
452 | iphp_file_store:
453 | mappings:
454 | some_entity:
455 | directory_namer:
456 | entityName: ~
457 | ```
458 |
459 | #### Using chain of directory namers
460 |
461 | Using entity class name and entity field name
462 |
463 | ``` yaml
464 | # app/config/config.yml
465 | iphp_file_store:
466 | mappings:
467 | some_entity:
468 | directory_namer:
469 | entityName: ~
470 | property:
471 | params: { use_field_name : true }
472 | ```
473 |
--------------------------------------------------------------------------------
/Resources/config/routing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | IphpFilestoreBundle:Default:index
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Resources/config/services.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | Iphp\FileStoreBundle\Naming\DefaultNamer
10 | Iphp\FileStoreBundle\Naming\DefaultDirectoryNamer
11 | Iphp\FileStoreBundle\Naming\NamerServiceInvoker
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
42 |
43 |
44 | %iphp.filestore.mappings%
45 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Resources/translations/messages.fr.xliff:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Original file name
7 | Nom du fichier
8 |
9 |
10 |
11 | Delete
12 | Supprimer
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Resources/translations/messages.ru.xliff:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Original file name
7 | Исходное название файла
8 |
9 |
10 |
11 | Delete
12 | Удалить
13 |
14 |
15 |
16 | Upload File
17 | Загрузить файл
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Resources/views/Form/fields-base.html.twig:
--------------------------------------------------------------------------------
1 | {% block iphp_file_widget %}
2 |
3 |
4 |
5 |
6 | {% if form.file is defined %}
7 | {{ form_widget(form.file) }}
8 | {% endif %}
9 |
10 | {# if was upload error value is uploadedFile and originalName no exists#}
11 | {% if file_data and file_data.originalName is defined %}
12 | {% set fileUrl = file_data.path %}
13 |
14 | {% if show_preview and file_data.width is defined %}
15 | {% block iphp_file_widget_image_preview %}
16 |
17 |
22 |
23 |
{{ file_data.width ~ 'x' ~ file_data.height }}
24 | {% endblock iphp_file_widget_image_preview %}
25 | {% endif %}
26 |
27 |
28 |
29 | {% block iphp_file_widget_file_info %}
30 |
31 |
32 | {% block iphp_file_widget_file_link %}
33 |
36 | {% endblock iphp_file_widget_file_link %}
37 |
38 | {% block iphp_file_widget_file_attrs %}
39 |
{{ file_data.size /1000 }} Kb
40 |
41 | {% if file_data.originalName != file_data.fileName %}
42 |
43 | {% trans %}Original file name{% endtrans %}:
44 | {{ file_data.originalName }}
45 |
46 | {% endif %}
47 | {% endblock iphp_file_widget_file_attrs %}
48 |
49 |
50 | {% if form.delete is defined %}
51 | {% block iphp_file_widget_file_delete %}
52 |
53 | {{ form_row (form.delete, {'label_attr' : { 'style' :'width:auto;padding-right:10px' }}) }}
54 |
55 | {% endblock iphp_file_widget_file_delete %}
56 | {% endif %}
57 |
58 |
59 | {% endblock iphp_file_widget_file_info %}
60 |
61 |
62 |
63 |
64 | {% endif %}
65 |
66 |
67 | {% block iphp_file_widget_end %}
68 |
69 | {% endblock iphp_file_widget_end %}
70 |
71 |
72 |
73 |
74 | {% endblock iphp_file_widget %}
--------------------------------------------------------------------------------
/Resources/views/Form/fields.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'IphpFileStoreBundle:Form:fields-base.html.twig' %}
--------------------------------------------------------------------------------
/Tests/ChildOfDummyEntity.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ChildOfDummyEntity extends DummyEntity
11 | {
12 |
13 | }
--------------------------------------------------------------------------------
/Tests/DataStorage/Orm/OrmDataStorageTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class OrmDataStorageTest extends \PHPUnit_Framework_TestCase
15 | {
16 | /**
17 | * Test the getObjectFromArgs method.
18 | */
19 | public function testGetObjectFromArgs()
20 | {
21 | if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) {
22 | $this->markTestSkipped('Doctrine\ORM\Event\LifecycleEventArgs does not exist.');
23 | } else {
24 | $entity = $this->getMock('Iphp\FileStoreBundle\Tests\DummyEntity');
25 |
26 | $args = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs')
27 | ->disableOriginalConstructor()
28 | ->getMock();
29 | $args
30 | ->expects($this->once())
31 | ->method('getEntity')
32 | ->will($this->returnValue($entity));
33 |
34 | $storage = new OrmDataStorage();
35 |
36 | $this->assertEquals($entity, $storage->getObjectFromArgs($args));
37 | }
38 | }
39 |
40 | /**
41 | * Tests the getReflectionClass method.
42 | */
43 | public function testGetReflectionClass()
44 | {
45 | if (!interface_exists('Doctrine\ORM\Proxy\Proxy')) {
46 | $this->markTestSkipped('Doctrine\ORM\Proxy\Proxy does not exist.');
47 | } else {
48 | $obj = new DummyEntity();
49 | $adapter = new OrmDataStorage();
50 | $class = $adapter->getReflectionClass($obj);
51 |
52 | $this->assertEquals($class->getName(), get_class($obj));
53 | }
54 | }
55 |
56 | /**
57 | * Tests the getReflectionClass method with a proxy.
58 | */
59 | public function testGetReflectionClassProxy()
60 | {
61 | if (!interface_exists('Doctrine\ORM\Proxy\Proxy')) {
62 | $this->markTestSkipped('Doctrine\ORM\Proxy\Proxy does not exist.');
63 | } else {
64 | $obj = new DummyEntityProxyORM();
65 | $adapter = new OrmDataStorage();
66 | $class = $adapter->getReflectionClass($obj);
67 |
68 | $this->assertEquals($class->getName(), get_parent_class($obj));
69 | }
70 | }
71 |
72 |
73 | public function testRecomputeChangeSet()
74 | {
75 |
76 |
77 | if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) {
78 | $this->markTestSkipped('Doctrine\ORM\Event\LifecycleEventArgs does not exist.');
79 | } else {
80 | $entity = $this->getMock('Iphp\FileStoreBundle\Tests\DummyEntity');
81 |
82 | $args = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs')
83 | ->disableOriginalConstructor()
84 | ->getMock();
85 | $args
86 | ->expects($this->once())
87 | ->method('getEntity')
88 | ->will($this->returnValue($entity));
89 |
90 |
91 | $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
92 | ->disableOriginalConstructor()
93 | ->getMock();
94 |
95 |
96 | $args->expects($this->once())
97 | ->method('getEntityManager')
98 | ->will($this->returnValue($em));
99 |
100 |
101 | $uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
102 | ->disableOriginalConstructor()
103 | ->getMock();
104 |
105 |
106 | $em->expects($this->once())
107 | ->method('getUnitOfWork')
108 | ->will($this->returnValue($uow));
109 |
110 |
111 | $metadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')
112 | ->disableOriginalConstructor()
113 | ->getMock();
114 |
115 |
116 | $em->expects($this->once())
117 | ->method('getClassMetadata')
118 | ->with(get_class($entity))
119 | ->will($this->returnValue($metadata));
120 |
121 |
122 | $storage = new OrmDataStorage();
123 | $storage->recomputeChangeSet($args);
124 |
125 | }
126 |
127 | }
128 |
129 |
130 | function testPostFlush()
131 | {
132 | if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) {
133 | $this->markTestSkipped('Doctrine\ORM\Event\LifecycleEventArgs does not exist.');
134 | } else {
135 | $entity = $this->getMock('Iphp\FileStoreBundle\Tests\DummyEntity');
136 |
137 | $args = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs')
138 | ->disableOriginalConstructor()
139 | ->getMock();
140 |
141 |
142 | $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
143 | ->disableOriginalConstructor()
144 | ->getMock();
145 |
146 |
147 | $args->expects($this->any())
148 | ->method('getEntityManager')
149 | ->will($this->returnValue($em));
150 |
151 |
152 | $em->expects($this->once())->method('persist')->with($entity);
153 | $em->expects($this->once())->method('flush');
154 |
155 |
156 | $storage = new OrmDataStorage();
157 | $storage->postFlush($entity, $args);
158 | }
159 | }
160 |
161 |
162 | public function testCurrentFieldData()
163 | {
164 |
165 | if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) {
166 | $this->markTestSkipped('Doctrine\ORM\Event\PreUpdateEventArgs does not exist.');
167 | } else {
168 | $entity = $this->getMock('Iphp\FileStoreBundle\Tests\DummyEntity');
169 |
170 | $args = $this->getMockBuilder('Doctrine\ORM\Event\PreUpdateEventArgs')
171 | ->disableOriginalConstructor()
172 | ->getMock();
173 |
174 |
175 | $args->expects($this->once())
176 | ->method('hasChangedField')
177 | ->with('file')
178 | ->will($this->returnValue(true));
179 |
180 |
181 | $args->expects($this->once())
182 | ->method('getOldValue')
183 | ->with('file')
184 | ->will($this->returnValue(array(1)));
185 |
186 | $storage = new OrmDataStorage();
187 | $this->assertSame($storage->previusFieldDataIfChanged('file', $args), array(1));
188 | }
189 |
190 | }
191 |
192 | }
193 |
--------------------------------------------------------------------------------
/Tests/Driver/AnnotationDriverTest.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class AnnotationDriverTest extends \PHPUnit_Framework_TestCase
19 | {
20 | /**
21 | * Test that the driver can correctly read the Uploadable
22 | * annotation.
23 | */
24 | public function testReadUploadableAnnotation()
25 | {
26 | $uploadable = Mocks::getUploadableMock($this);
27 |
28 |
29 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
30 | $reader
31 | ->expects($this->once())
32 | ->method('getClassAnnotation')
33 | ->will($this->returnValue($uploadable));
34 |
35 | $entity = new DummyEntity();
36 | $driver = new AnnotationDriver($reader);
37 | $annot = $driver->readUploadable(new \ReflectionClass($entity));
38 |
39 | $this->assertEquals($uploadable, $annot);
40 | }
41 |
42 |
43 | public function testReadUploadableAnnotationFromParent()
44 | {
45 | $uploadable = Mocks::getUploadableMock($this);
46 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
47 |
48 |
49 | $reader
50 | ->expects($this->any())
51 | ->method('getClassAnnotation')
52 | ->will($this->returnCallBack ( function() use ( $uploadable) {
53 |
54 | $args = func_get_args();
55 |
56 | if ('Iphp\\FileStoreBundle\\Tests\\ChildOfDummyEntity' === $args[0]->getName()) return null;
57 | if ('Iphp\\FileStoreBundle\\Tests\\DummyEntity' === $args[0]->getName()) return $uploadable;
58 |
59 | }));
60 |
61 | $entity = new ChildOfDummyEntity();
62 | $driver = new AnnotationDriver($reader);
63 |
64 | $annot = $driver->readUploadable(new \ReflectionClass($entity));
65 |
66 | $this->assertEquals($uploadable, $annot);
67 |
68 | }
69 |
70 |
71 |
72 |
73 |
74 |
75 | /**
76 | * Tests that the driver returns null when no Uploadable annotation
77 | * is found.
78 | */
79 | public function testReadUploadableAnnotationReturnsNullWhenNonePresent()
80 | {
81 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
82 | $reader
83 | ->expects($this->once())
84 | ->method('getClassAnnotation')
85 | ->will($this->returnValue(null));
86 |
87 | $entity = new DummyEntity();
88 | $driver = new AnnotationDriver($reader);
89 | $annot = $driver->readUploadable(new \ReflectionClass($entity));
90 |
91 | $this->assertEquals(null, $annot);
92 | }
93 |
94 | /**
95 | * Tests that the driver correctly reads one UploadableField
96 | * property.
97 | */
98 | public function testReadOneUploadableField()
99 | {
100 | $uploadableField = Mocks::getUploadableFieldMock($this);
101 |
102 | $uploadableField
103 | ->expects($this->once())
104 | ->method('setFileUploadPropertyName');
105 |
106 | $entity = new DummyEntity();
107 | $class = new \ReflectionClass($entity);
108 |
109 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
110 | $reader
111 | ->expects($this->any())
112 | ->method('getPropertyAnnotation')
113 | ->will($this->returnCallback(function() use ($class, $uploadableField) {
114 | $args = func_get_args();
115 |
116 | if ( $args[0]->class === $class->getName() && 'file' === $args[0]->getName()) {
117 | return $uploadableField;
118 | }
119 |
120 | return null;
121 | }));
122 |
123 | $driver = new AnnotationDriver($reader);
124 | $fields = $driver->readUploadableFields($class);
125 |
126 | $this->assertEquals(1, count($fields));
127 | }
128 |
129 |
130 |
131 |
132 | public function testReadOneUploadableFieldFromParent()
133 | {
134 | $uploadableField = Mocks::getUploadableFieldMock($this);
135 | $uploadableField
136 | ->expects($this->once())
137 | ->method('setFileUploadPropertyName')
138 | ->with ('file');
139 |
140 |
141 | $entity = new ChildOfDummyEntity();
142 | $class = new \ReflectionClass($entity);
143 |
144 |
145 |
146 |
147 |
148 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
149 | $reader
150 | ->expects($this->any())
151 | ->method('getPropertyAnnotation')
152 | ->will($this->returnCallback(function() use ( $entity , $uploadableField) {
153 | $args = func_get_args();
154 | if (get_parent_class($entity) === $args[0]->class && 'file' === $args[0]->getName()) {
155 | return $uploadableField;
156 | }
157 |
158 | return null;
159 | }));
160 |
161 |
162 | $driver = new AnnotationDriver($reader);
163 | $fields = $driver->readUploadableFields($class);
164 |
165 | $this->assertEquals(1, count($fields));
166 | }
167 |
168 |
169 |
170 | /**
171 | * Tests that the driver correctly reads one UploadableField
172 | * property.
173 | */
174 | public function testReadUploadableFieldSingle()
175 | {
176 | $uploadableField = Mocks::getUploadableFieldMock($this);
177 | $uploadableField->expects($this->once())->method('setFileUploadPropertyName');
178 |
179 | $entity = new DummyEntity();
180 | $class = new \ReflectionClass($entity);
181 |
182 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
183 | $reader
184 | ->expects($this->any())
185 | ->method('getPropertyAnnotation')
186 | ->will($this->returnCallback(function() use ($uploadableField) {
187 | $args = func_get_args();
188 | if ('file' === $args[0]->getName()) {
189 | return $uploadableField;
190 | }
191 |
192 | return null;
193 | }));
194 |
195 | $driver = new AnnotationDriver($reader);
196 | $this->assertEquals ($driver->readUploadableField($class, 'file'), $uploadableField);
197 |
198 |
199 | }
200 |
201 |
202 | /**
203 | * Tests that the driver correctly reads one UploadableField
204 | * property.
205 | */
206 | public function testReadUploadableFieldNoMapping()
207 | {
208 | $uploadableField = Mocks::getUploadableFieldMock($this);
209 | $uploadableField->expects($this->never())->method('setPropertyName');
210 |
211 | $entity = new DummyEntity();
212 | $class = new \ReflectionClass($entity);
213 |
214 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
215 | $reader
216 | ->expects($this->any())
217 | ->method('getPropertyAnnotation')
218 | ->will($this->returnCallback(function() use ($uploadableField) {
219 | $args = func_get_args();
220 | if ('file' === $args[0]->getName()) {
221 | return null;
222 | }
223 | return null;
224 | }));
225 |
226 | $driver = new AnnotationDriver($reader);
227 | $this->assertEquals ($driver->readUploadableField($class, 'file'), null);
228 | }
229 |
230 |
231 |
232 |
233 |
234 | /**
235 | * Test that the driver correctly reads two UploadableField
236 | * properties.
237 | */
238 | public function testReadTwoUploadableFields()
239 | {
240 | $fileField = Mocks::getUploadableFieldMock($this);
241 | $fileField->expects($this->once())->method('setFileUploadPropertyName');
242 |
243 | $imageField = Mocks::getUploadableFieldMock($this);
244 | $imageField->expects($this->once())->method('setFileUploadPropertyName');
245 |
246 | $entity = new TwoFieldsDummyEntity();
247 | $class = new \ReflectionClass($entity);
248 |
249 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
250 | $reader
251 | ->expects($this->any())
252 | ->method('getPropertyAnnotation')
253 | ->will($this->returnCallback(function() use ($fileField, $imageField) {
254 | $args = func_get_args();
255 | if ('file' === $args[0]->getName()) {
256 | return $fileField;
257 | } elseif ('image' === $args[0]->getName()) {
258 | return $imageField;
259 | }
260 |
261 | return null;
262 | }));
263 |
264 | $driver = new AnnotationDriver($reader);
265 | $fields = $driver->readUploadableFields($class);
266 |
267 | $this->assertEquals(2, count($fields));
268 | }
269 |
270 | /**
271 | * Test that the driver reads zero UploadableField
272 | * properties when none exist.
273 | */
274 | public function testReadNoUploadableFieldsWhenNoneExist()
275 | {
276 | $entity = new DummyEntity();
277 | $class = new \ReflectionClass($entity);
278 |
279 | $reader = $this->getMock('Doctrine\Common\Annotations\Reader');
280 | $reader
281 | ->expects($this->any())
282 | ->method('getPropertyAnnotation')
283 | ->will($this->returnValue(null));
284 |
285 | $driver = new AnnotationDriver($reader);
286 | $fields = $driver->readUploadableFields($class);
287 |
288 | $this->assertEquals(0, count($fields));
289 | }
290 |
291 | }
292 |
--------------------------------------------------------------------------------
/Tests/DummyEntity.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class DummyEntity
13 | {
14 |
15 | protected $id;
16 |
17 | /**
18 | * @FileStore\UploadableField(mapping="dummy_file")
19 | */
20 | protected $file;
21 |
22 |
23 | protected $title;
24 |
25 | protected $createdAt;
26 |
27 | /**
28 | * @var array Default iphpfilestore bundle configuration for DummyEntity
29 | */
30 | public $defaultFileStoreConfig= array(
31 | 'dummy_file' => array(
32 | 'upload_dir' => '/www/web/images',
33 | 'upload_path' => '/images',
34 | 'namer' => array('translit' => array('service' => 'iphp.filestore.namer.default'))
35 | )
36 | );
37 |
38 |
39 | public function setId($id)
40 | {
41 | $this->id = $id;
42 | return $this;
43 | }
44 |
45 | public function getId()
46 | {
47 | return $this->id;
48 | }
49 |
50 |
51 | public function getFile()
52 | {
53 | return $this->file;
54 | }
55 |
56 | public function setFile($file)
57 | {
58 | $this->file = $file;
59 | }
60 |
61 | public function setTitle($title)
62 | {
63 | $this->title = $title;
64 | return $this;
65 | }
66 |
67 | public function getTitle()
68 | {
69 | return $this->title;
70 | }
71 |
72 | public function setCreatedAt($createdAt)
73 | {
74 | $this->createdAt = $createdAt;
75 | return $this;
76 | }
77 |
78 | public function getCreatedAt()
79 | {
80 | return $this->createdAt;
81 | }
82 |
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/Tests/DummyEntityProxyORM.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class DummyEntityProxyORM extends DummyEntity implements Proxy
14 | {
15 | public function __load() { }
16 |
17 | public function __isInitialized() { }
18 |
19 | /**
20 | * Sets the callback to be used when cloning the proxy. That initializer should accept
21 | * a single parameter, which is the cloned proxy instance itself.
22 | *
23 | * @param Closure|null $cloner
24 | *
25 | * @return void
26 | */
27 | public function __setCloner(Closure $cloner = null)
28 | {
29 | // TODO: Implement __setCloner() method.
30 | }
31 |
32 | /**
33 | * Retrieves the callback to be used when cloning the proxy.
34 | *
35 | * @see __setCloner
36 | *
37 | * @return Closure|null
38 | */
39 | public function __getCloner()
40 | {
41 | // TODO: Implement __getCloner() method.
42 | }
43 |
44 | /**
45 | * Marks the proxy as initialized or not.
46 | *
47 | * @param boolean $initialized
48 | *
49 | * @return void
50 | */
51 | public function __setInitialized($initialized)
52 | {
53 | // TODO: Implement __setInitialized() method.
54 | }
55 |
56 | /**
57 | * Retrieves the list of lazy loaded properties for a given proxy
58 | *
59 | * @return array Keys are the property names, and values are the default values
60 | * for those properties.
61 | */
62 | public function __getLazyProperties()
63 | {
64 | // TODO: Implement __getLazyProperties() method.
65 | }
66 |
67 | /**
68 | * Retrieves the initializer callback used to initialize the proxy.
69 | *
70 | * @see __setInitializer
71 | *
72 | * @return Closure|null
73 | */
74 | public function __getInitializer()
75 | {
76 | // TODO: Implement __getInitializer() method.
77 | }
78 |
79 | /**
80 | * Sets the initializer callback to be used when initializing the proxy. That
81 | * initializer should accept 3 parameters: $proxy, $method and $params. Those
82 | * are respectively the proxy object that is being initialized, the method name
83 | * that triggered initialization and the parameters passed to that method.
84 | *
85 | * @param Closure|null $initializer
86 | *
87 | * @return void
88 | */
89 | public function __setInitializer(Closure $initializer = null)
90 | {
91 | // TODO: Implement __setInitializer() method.
92 | }
93 |
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Tests/DummyEntitySeparateDataField.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class DummyEntitySeparateDataField
13 | {
14 | /**
15 | * @FileStore\UploadableField(mapping="dummy_file", )
16 | */
17 | protected $file;
18 |
19 |
20 |
21 | protected $file_data;
22 |
23 | /**
24 | * @var array Default iphpfilestore bundle configuration for DummyEntity
25 | */
26 | public $defaultFileStoreConfig= array(
27 | 'dummy_file' => array(
28 | 'upload_dir' => '/www/web/images',
29 | 'upload_path' => '/images',
30 | 'namer' => array('translit' => array('service' => 'iphp.filestore.namer.default'))
31 | )
32 | );
33 |
34 | public function getFile()
35 | {
36 | $this->file;
37 | }
38 |
39 | public function setFile($file)
40 | {
41 | $this->file = $file;
42 | }
43 |
44 | public function setFileData($file_data)
45 | {
46 | $this->file_data = $file_data;
47 | return $this;
48 | }
49 |
50 | public function getFileData()
51 | {
52 | return $this->file_data;
53 | }
54 |
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/File/FileTest.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class FileTest extends \PHPUnit_Framework_TestCase
14 | {
15 |
16 |
17 |
18 |
19 | function testCreateEmpty()
20 | {
21 | $file = File::createEmpty();
22 |
23 | $this->assertTrue ($file instanceof File);
24 | $this->assertSame ($file->isDeleted(), false);
25 | $this->assertSame ($file->getPath(), '');
26 | $this->assertSame ($file->isValid(), true);
27 | }
28 |
29 |
30 |
31 | function testDelete()
32 | {
33 | $file = new File();
34 | $this->assertSame ($file->isDeleted(), false);
35 | $file->delete();
36 | $this->assertSame ($file->isDeleted(), true);
37 | }
38 | }
--------------------------------------------------------------------------------
/Tests/FileStorage/123.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitiko/IphpFileStoreBundle/fd6fd1658f4524f9ca0a05e191cbc5648fcbc935/Tests/FileStorage/123.jpg
--------------------------------------------------------------------------------
/Tests/FileStorage/FileSystemStorageTest.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | class FileSystemStorageTest extends \PHPUnit_Framework_TestCase
20 | {
21 |
22 | /**
23 | * @var \Iphp\FileStoreBundle\FileStorage\FileSystemStorage;
24 | */
25 | protected $storage;
26 |
27 |
28 | protected $uploadedImageFile;
29 |
30 | protected $targetImageFileExistingDir;
31 |
32 |
33 | protected $targetImageFileNewDir;
34 |
35 |
36 | protected $targetImageFileExistingReadonlyDir;
37 |
38 | /**
39 | * Sets up the test.
40 | */
41 | public function setUp()
42 | {
43 | $this->storage = new FileSystemStorage();
44 |
45 | vfsStreamWrapper::register();
46 | vfsStreamWrapper::setRoot(new vfsStreamDirectory('site_root'));
47 | vfsStream::setup('site_root', 0700, array(
48 |
49 | 'uploaded' => array('123.jpg' => file_get_contents(__DIR__ . '/123.jpg')),
50 | 'web' => array(
51 | 'images' => array(),
52 | // 'images_readonly' => array()
53 | )
54 |
55 | ));
56 |
57 | if (version_compare(PHP_VERSION, '5.4.0') >= 0)
58 | {
59 | mkdir (vfsStream::url('site_root/web/images-readonly'),0700);
60 | chown (vfsStream::url('site_root/web/images-readonly'), vfsStream::GROUP_USER_1);
61 | }
62 |
63 | $this->uploadedImageFile = vfsStream::url('site_root/uploaded/123.jpg');
64 | $this->targetImageFileExistingDir = vfsStream::url('site_root/web/images/123.jpg');
65 | $this->targetImageFileExistingReadonlyDir = vfsStream::url('site_root/web/images-readonly/123.jpg');
66 | $this->targetImageFileNewDir = vfsStream::url('site_root/web/images/new/123.jpg');
67 | }
68 |
69 |
70 | protected function createPropertyMapping($resolveFrom, $resolveTo, $targetData = array())
71 | {
72 | $propertyMapping = Mocks::getPropertyMappingMock($this);
73 | $propertyMapping->expects($this->any())
74 | ->method('resolveFileName')
75 | ->with($resolveFrom)
76 | ->will($this->returnValue($resolveTo));
77 |
78 |
79 | if ($targetData) $propertyMapping->expects($this->any())
80 | ->method('prepareFileName')
81 | ->with($resolveFrom, $this->storage)
82 | ->will($this->returnValue($targetData));
83 |
84 | return $propertyMapping;
85 | }
86 |
87 |
88 | public function testFileExists()
89 | {
90 |
91 | $this->assertTrue($this->storage->fileExists( $this->uploadedImageFile));
92 |
93 | }
94 |
95 |
96 | public function testFileNoExists()
97 | {
98 | $this->assertFalse($this->storage->fileExists($this->targetImageFileExistingDir));
99 | }
100 |
101 |
102 | public function testRemoveExistingFile()
103 | {
104 | $this->assertTrue($this->storage->removeFile($this->uploadedImageFile));
105 | }
106 |
107 |
108 | public function testRemoveNonExistingFile()
109 | {
110 |
111 | $this->assertNull($this->storage->removeFile($this->targetImageFileExistingDir));
112 | }
113 |
114 |
115 | public function testUploadImageFileToExistingDir()
116 | {
117 | //where file will be copied
118 | $propertyMapping = $this->createPropertyMapping('123.jpg', $this->targetImageFileExistingDir,
119 | array('123.jpg', '/images/123.jpg'));
120 |
121 | $file = new File(
122 | $this->uploadedImageFile, '123.jpg', 'image/jpeg', null, null, true);
123 |
124 |
125 | $this->storage->setSameFileChecker(function ()
126 | {
127 | return false;
128 | });
129 |
130 | $this->assertFileExists($this->uploadedImageFile);
131 | $this->assertFileNotExists($this->targetImageFileExistingDir);
132 |
133 |
134 | $fileData = $this->storage->upload($propertyMapping, $file);
135 |
136 |
137 | $this->assertFileExists($this->uploadedImageFile);
138 | $this->assertFileExists($this->targetImageFileExistingDir);
139 |
140 | }
141 |
142 |
143 | public function testUploadUploadedImageFileToExistingDir()
144 | {
145 | //test mode
146 | $uploadedFile = new \Symfony\Component\HttpFoundation\File\UploadedFile(
147 | $this->uploadedImageFile, '123.jpg', 'image/jpeg', null, null, true);
148 |
149 | $propertyMapping = $this->createPropertyMapping('123.jpg', $this->targetImageFileExistingDir,
150 | array('123.jpg', '/images/123.jpg'));
151 |
152 | $this->storage->setSameFileChecker(function ()
153 | {
154 | return false;
155 | });
156 |
157 |
158 | $this->assertFileExists($this->uploadedImageFile);
159 | $this->assertFileNotExists($this->targetImageFileExistingDir);
160 | $this->assertFileExists(dirname($this->targetImageFileExistingDir));
161 |
162 | $filesize = filesize($this->uploadedImageFile);
163 | $fileData = $this->storage->upload($propertyMapping, $uploadedFile);
164 | $this->assertFileExists($this->targetImageFileExistingDir);
165 | $this->assertFileNotExists($this->uploadedImageFile);
166 | $this->assertTrue(filesize($this->targetImageFileExistingDir) == $filesize);
167 |
168 |
169 | $testFileData = array
170 | (
171 | 'fileName' => '123.jpg',
172 | 'originalName' => '123.jpg',
173 | 'mimeType' => 'image/jpeg',
174 | 'size' => $filesize,
175 | 'path' => '/images/123.jpg'
176 | );
177 |
178 | if (function_exists('getimagesize')) {
179 | $testFileData['width'] = 660;
180 | $testFileData['height'] = 498;
181 | }
182 |
183 | $this->assertSame($fileData, $testFileData);
184 |
185 |
186 | }
187 |
188 |
189 | public function testUploadImageFileToNewDir()
190 | {
191 | //where file will be copied
192 | $propertyMapping = $this->createPropertyMapping('123.jpg', $this->targetImageFileNewDir,
193 | array('123.jpg', '/images/new/123.jpg'));
194 | $file = new File($this->uploadedImageFile, '123.jpg', 'image/jpeg', null, null, true);
195 |
196 | $this->storage->setSameFileChecker(function ()
197 | {
198 | return false;
199 | });
200 |
201 |
202 | $this->assertFileNotExists($this->targetImageFileNewDir);
203 | $this->assertFileExists($this->uploadedImageFile);
204 |
205 | $this->assertFileNotExists(dirname($this->targetImageFileNewDir));
206 | $filesize = filesize($this->uploadedImageFile);
207 |
208 | $fileData = $this->storage->upload($propertyMapping, $file);
209 |
210 | $this->assertFileExists(dirname($this->targetImageFileNewDir));
211 | $this->assertFileExists($this->targetImageFileNewDir);
212 | $this->assertFileExists($this->uploadedImageFile);
213 |
214 | $testFileData = array
215 | (
216 | 'fileName' => '123.jpg',
217 | 'originalName' => '123.jpg',
218 | 'mimeType' => 'image/jpeg',
219 | 'size' => $filesize,
220 | 'path' => '/images/new/123.jpg'
221 | );
222 |
223 | if (function_exists('getimagesize')) {
224 | $testFileData['width'] = 660;
225 | $testFileData['height'] = 498;
226 | }
227 |
228 | $this->assertSame($fileData, $testFileData);
229 |
230 | }
231 |
232 |
233 |
234 |
235 |
236 | /**
237 | * Test that an exception is thrown when try to move file to readonly dir
238 | * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
239 | */
240 | public function testUploadUploadedImageFileToExistingReadonlyDir()
241 | {
242 |
243 | if (version_compare(PHP_VERSION, '5.4.0','<'))
244 | {
245 | $this->markTestSkipped('vfsStream and chown() works only in PHP 5.4+');
246 | return;
247 | }
248 |
249 |
250 | //test mode
251 | $uploadedFile = new \Symfony\Component\HttpFoundation\File\UploadedFile(
252 | $this->uploadedImageFile, '123.jpg', 'image/jpeg', null, null, true);
253 |
254 | $propertyMapping = $this->createPropertyMapping('123.jpg', $this->targetImageFileExistingReadonlyDir ,
255 | array('123.jpg', '/images-readonly/123.jpg'));
256 |
257 | $this->storage->setSameFileChecker(function ()
258 | {
259 | return false;
260 | });
261 |
262 | $fileData = $this->storage->upload($propertyMapping, $uploadedFile);
263 | }
264 |
265 |
266 | /**
267 | * Test that an exception is thrown when try to move file to readonly dir
268 | * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
269 | */
270 | public function testUploadImageFileToExistingReadonlyDir()
271 | {
272 |
273 | if (version_compare(PHP_VERSION, '5.4.0','<'))
274 | {
275 | $this->markTestSkipped('vfsStream and chown() works only in PHP 5.4+');
276 | return;
277 | }
278 |
279 |
280 | //test mode
281 | $uploadedFile = new File(
282 | $this->uploadedImageFile, '123.jpg', 'image/jpeg', null, null, true);
283 |
284 | $propertyMapping = $this->createPropertyMapping('123.jpg', $this->targetImageFileExistingReadonlyDir ,
285 | array('123.jpg', '/images-readonly/123.jpg'));
286 |
287 | $this->storage->setSameFileChecker(function ()
288 | {
289 | return false;
290 | });
291 |
292 | $fileData = $this->storage->upload($propertyMapping, $uploadedFile);
293 | }
294 |
295 |
296 | function testSetWebDir()
297 | {
298 | $this->storage->setWebDir('/srv/www/web');
299 | $this->assertSame($this->storage->getWebDir(), '/srv/www/web');
300 | }
301 |
302 |
303 | }
304 |
--------------------------------------------------------------------------------
/Tests/Fixtures/files/text.txt:
--------------------------------------------------------------------------------
1 | test file
--------------------------------------------------------------------------------
/Tests/Fixtures/images/front-images-list.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitiko/IphpFileStoreBundle/fd6fd1658f4524f9ca0a05e191cbc5648fcbc935/Tests/Fixtures/images/front-images-list.jpeg
--------------------------------------------------------------------------------
/Tests/Fixtures/images/github1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitiko/IphpFileStoreBundle/fd6fd1658f4524f9ca0a05e191cbc5648fcbc935/Tests/Fixtures/images/github1.png
--------------------------------------------------------------------------------
/Tests/Fixtures/images/php-elephant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitiko/IphpFileStoreBundle/fd6fd1658f4524f9ca0a05e191cbc5648fcbc935/Tests/Fixtures/images/php-elephant.png
--------------------------------------------------------------------------------
/Tests/Fixtures/images/sonata-admin-iphpfile.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitiko/IphpFileStoreBundle/fd6fd1658f4524f9ca0a05e191cbc5648fcbc935/Tests/Fixtures/images/sonata-admin-iphpfile.jpeg
--------------------------------------------------------------------------------
/Tests/Form/DataTransformer/FileDataTransformerTest.php:
--------------------------------------------------------------------------------
1 | fileStorage = Mocks::getFileStorageMock($this);
26 | $this->transformer = new FileDataTransformer($this->fileStorage);
27 | }
28 |
29 |
30 | function testTransform()
31 |
32 | {
33 | $this->assertSame($this->transformer->transform(array(1, 2, 3)), array(1, 2, 3));
34 | }
35 |
36 |
37 | function testReverseTransformDeleteFile()
38 | {
39 | $propertyMapping = Mocks::getPropertyMappingMock($this);
40 | $file = Mocks::getFileMock($this);
41 | $this->transformer->setMapping($propertyMapping, FileDataTransformer::MODE_UPLOAD_FIELD);
42 |
43 |
44 | $propertyMapping->expects($this->once())
45 | ->method('resolveFileName')
46 | ->with('123.jpg')
47 | ->will($this->returnValue('/path/to/123.jpg'));
48 |
49 |
50 | $this->fileStorage
51 | ->expects($this->once())
52 | ->method('removeFile')
53 | ->with('/path/to/123.jpg');
54 |
55 | $this->assertSame(
56 | $this->transformer->reverseTransform(array('delete' => 1, 'file' => $file, 'fileName' => '123.jpg')),
57 | $file);
58 | }
59 |
60 |
61 | function testReverseTransformNoDeleteFile()
62 | {
63 | $propertyMapping = Mocks::getPropertyMappingMock($this);
64 | $file = Mocks::getFileMock($this);
65 | $this->transformer->setMapping($propertyMapping, FileDataTransformer::MODE_UPLOAD_FIELD);
66 |
67 | $this->fileStorage
68 | ->expects($this->never())
69 | ->method('removeFile');
70 |
71 | $this->assertSame(
72 | $this->transformer->reverseTransform(array('delete' => 0, 'file' => $file, 'fileName' => '123.jpg')),
73 | $file);
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/Tests/Form/DataTransformer/FileDataViewTransformerTest.php:
--------------------------------------------------------------------------------
1 | transformer = new FileDataViewTransformer();
22 | }
23 |
24 |
25 | function testTransform()
26 |
27 | {
28 | $this->assertSame($this->transformer->transform(array(1, 2, 3)), array(1, 2, 3));
29 | }
30 |
31 |
32 |
33 | function testReverseTransform()
34 |
35 | {
36 | $this->assertSame($this->transformer->reverseTransform(array(1, 2, 3)), array(1, 2, 3));
37 | }
38 | }
--------------------------------------------------------------------------------
/Tests/Form/Type/FileTypeBindSubscriberTest.php:
--------------------------------------------------------------------------------
1 | dataStorage = Mocks::getDataStorageMock($this);
52 | $this->driver = Mocks::getAnnotationDriverMock($this);
53 | $this->namerServiceInvoker = Mocks::getNamerServiceInvokerMock($this);
54 |
55 |
56 | $this->propertyMappingFactory = Mocks::getPropertyMappingFactoryMock($this,
57 | $this->namerServiceInvoker, $this->driver);
58 |
59 |
60 | $this->transformer = $this->getMockBuilder('Iphp\FileStoreBundle\Form\DataTransformer\FileDataTransformer')
61 | ->disableOriginalConstructor()
62 | ->getMock();
63 |
64 | $this->subscriber = new FileTypeBindSubscriber ($this->propertyMappingFactory, $this->dataStorage, $this->transformer);
65 | }
66 |
67 |
68 | function testSubscribedEvents()
69 | {
70 | $this->assertSame(FileTypeBindSubscriber::getSubscribedEvents(),
71 | array(FormEvents::PRE_SUBMIT => 'preBind',
72 | FormEvents::PRE_SET_DATA => 'preSet'));
73 | }
74 |
75 |
76 | function testPreBind()
77 | {
78 |
79 | $obj = new DummyEntity();
80 | $class = new \ReflectionClass($obj);
81 |
82 | $formEvent = $this->getMockBuilder('Symfony\Component\Form\FormEvent')
83 | ->disableOriginalConstructor()
84 | ->getMock();
85 |
86 | $form = $this->getMockBuilder('Symfony\Component\Form\Form')
87 | ->disableOriginalConstructor()
88 | ->getMock();
89 |
90 | $parentForm = $this->getMockBuilder('Symfony\Component\Form\Form')
91 | ->disableOriginalConstructor()
92 | ->getMock();
93 |
94 |
95 | $formEvent->expects($this->once())
96 | ->method('getForm')
97 | ->will($this->returnValue($form));
98 |
99 |
100 | $form->expects($this->once())
101 | ->method('getParent')
102 | ->will($this->returnValue($parentForm));
103 |
104 | $parentForm->expects($this->once())
105 | ->method('getData')
106 | ->will($this->returnValue($obj));
107 |
108 |
109 | $this->dataStorage->expects($this->once())
110 | ->method('getReflectionClass')
111 | ->with($obj)
112 | ->will($this->returnValue($class));
113 |
114 |
115 | $propertyMapping = Mocks::getPropertyMappingMock($this);
116 |
117 | $this->propertyMappingFactory->expects($this->once())
118 | ->method('getMappingFromField')
119 | ->with($obj, $class, 'file')
120 | ->will($this->returnValue($propertyMapping));
121 |
122 |
123 | $form->expects($this->once())
124 | ->method('getName')
125 | ->will($this->returnValue('file'));
126 |
127 |
128 | $this->transformer->expects($this->once())
129 | ->method('setMapping')
130 | ->with($propertyMapping);
131 |
132 |
133 | $this->subscriber->preBind($formEvent);
134 | }
135 | }
--------------------------------------------------------------------------------
/Tests/Functional/AppKernel.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class AppKernel extends Kernel
12 | {
13 | protected $config;
14 |
15 | protected $testEnv;
16 |
17 | public function __construct($config, $testEnv = 'default')
18 | {
19 | //separate generated container
20 | parent::__construct($testEnv . '_' . substr(md5($config), 0, 3), true);
21 |
22 | $fs = new Filesystem();
23 | if (!$fs->isAbsolutePath($config)) {
24 | $config = __DIR__ . '/config/' . $config;
25 | }
26 |
27 | if (!file_exists($config)) {
28 | throw new \RuntimeException(sprintf('The config file "%s" does not exist.', $config));
29 | }
30 |
31 | $this->config = $config;
32 | $this->testEnv = $testEnv;
33 | }
34 |
35 | public function registerBundles()
36 | {
37 | return array(
38 | new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
39 | new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
40 | new \Symfony\Bundle\TwigBundle\TwigBundle(),
41 |
42 | new \Iphp\FileStoreBundle\IphpFileStoreBundle(),
43 | new \Iphp\FileStoreBundle\Tests\Functional\TestBundle\TestBundle(),
44 | new \Iphp\FileStoreBundle\Tests\Functional\TestXmlConfigBundle\TestXmlConfigBundle()
45 | );
46 | }
47 |
48 | public function registerContainerConfiguration(LoaderInterface $loader)
49 | {
50 | $loader->load($this->config);
51 | }
52 |
53 | public function getCacheDir()
54 | {
55 | return $this->getTestEnvDir() . '/app/cache/' . substr(md5($this->config), 0, 3) . '';
56 | }
57 |
58 | public function getConfig()
59 | {
60 | return $this->config;
61 | }
62 |
63 |
64 | public static function getTestBaseDir()
65 | {
66 | return sys_get_temp_dir() . '/IphpFileStoreTestBundle';
67 | }
68 |
69 |
70 |
71 | public function getTestEnvDir()
72 | {
73 | return self::getTestBaseDir().'/'. $this->testEnv;
74 | }
75 |
76 |
77 | public function makeTestEnvDir()
78 | {
79 | $fs = new Filesystem();
80 | $fs->remove($this->getTestEnvDir());
81 | $fs->mkdir($this->getTestEnvDir());
82 | }
83 |
84 |
85 | protected function getKernelParameters()
86 | {
87 |
88 |
89 | return array_merge(
90 | parent::getKernelParameters(), array(
91 | 'kernel.test_env' => $this->testEnv,
92 | 'kernel.test_env_dir' => $this->getTestEnvDir(),
93 |
94 | )
95 | );
96 | }
97 |
98 | public function getTestEnv()
99 | {
100 | return $this->testEnv;
101 | }
102 |
103 |
104 | }
--------------------------------------------------------------------------------
/Tests/Functional/BaseTestCase.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class BaseTestCase extends WebTestCase
11 | {
12 | protected $testCaseUniqId;
13 |
14 |
15 | static protected function createKernel(array $options = array())
16 | {
17 | return self::$kernel = new AppKernel(
18 | isset($options['config']) ? $options['config'] : 'default.yml', static::getTestEnvFromCalledClass()
19 | );
20 | }
21 |
22 | static function getTestEnvFromCalledClass()
23 | {
24 | $class = explode('\\', get_called_class());
25 | return end($class);
26 |
27 | }
28 | protected function setUp()
29 | {
30 | $dir = AppKernel::getTestBaseDir().'/'.static::getTestEnvFromCalledClass();
31 | $fs = new Filesystem();
32 | $fs->remove($dir);
33 | }
34 |
35 | protected function getKernel()
36 | {
37 | return self::$kernel;
38 | }
39 |
40 | protected function getContainer()
41 | {
42 | return $this->getKernel()->getContainer();
43 | }
44 |
45 | protected function getEntityManager()
46 | {
47 | return $this->getContainer()->get('doctrine.orm.entity_manager');
48 | }
49 |
50 |
51 | protected final function importDatabaseSchema()
52 | {
53 |
54 | $em = $this->getEntityManager();
55 | $metadata = $em->getMetadataFactory()->getAllMetadata();
56 | if (!empty($metadata)) {
57 | $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em);
58 | $schemaTool->dropDatabase();
59 | $schemaTool->createSchema($metadata);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Tests/Functional/FileSaveTest.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | use Iphp\FileStoreBundle\Tests\Functional\TestXmlConfigBundle\Entity\File;
10 | use Symfony\Component\Console\Tester\CommandTester;
11 | use Iphp\FileStoreBundle\Command\RepairFileDataCommand;
12 | use Symfony\Bundle\FrameworkBundle\Console\Application;
13 |
14 | class FileSaveTest extends BaseTestCase
15 | {
16 |
17 |
18 | /**
19 | * test saving entity with file property from parent abstract uploadable class
20 | */
21 | public function testFileSaveUpload()
22 | {
23 | $client = $this->createClient();
24 | $this->importDatabaseSchema();
25 |
26 |
27 | $file = new File();
28 |
29 | $existsFile = new \Symfony\Component\HttpFoundation\File\File(
30 | __DIR__ . '/../Fixtures/files/text.txt');
31 |
32 | $file->setTitle('new file')
33 | ->setDate(new \DateTime('2013-04-04'))
34 | ->setFile($existsFile);
35 |
36 |
37 | $this->getEntityManager()->persist($file);
38 | $this->getEntityManager()->flush();
39 |
40 |
41 |
42 | $this->assertSame($file->getFile(), array(
43 | 'fileName' => '/File/file/2013/1.txt',
44 | 'originalName' => 'text.txt',
45 | 'mimeType' => 'text/plain',
46 | 'size' => 9,
47 | 'path' => '/file/File/file/2013/1.txt',
48 | ));
49 |
50 | unset($file);
51 | $this->getEntityManager()->clear();
52 | $this->getKernel()->shutdown();
53 |
54 |
55 |
56 |
57 | $client = $this->createClient(array('config' => 'default_newfilepath.yml'));
58 | $application = new Application($client->getKernel());
59 | $application->add(new RepairFileDataCommand());
60 |
61 | $command = $application->find('iphp:filestore:repair');
62 |
63 |
64 | $commandTester = new CommandTester($command);
65 |
66 |
67 | //using web directory setted in config/default.yml
68 | $commandTester->execute(array(
69 | 'command' => $command->getName(),
70 | '--entity' => 'TestXmlConfigBundle:File',
71 | '--field' => 'file',
72 | '--force' => 1, // move file to new location
73 | '--webdir' => realpath($this->getContainer()->getParameter('kernel.test_env_dir') . '/web/')
74 | ));
75 |
76 |
77 | $newFile = $this->getEntityManager()->getRepository('TestXmlConfigBundle:File')->findOneByTitle('new file');
78 |
79 |
80 | $this->assertSame($newFile->getFile(), array(
81 | 'fileName' => '/1/new-file.txt',
82 | 'originalName' => 'text.txt',
83 | 'mimeType' => 'text/plain',
84 | 'size' => 9,
85 | 'path' => '/other/uploads/1/new-file.txt',
86 | ));
87 |
88 |
89 | unset($newFile);
90 | unset($commandTester);
91 | unset( $command );
92 | unset($application);
93 | $this->getEntityManager()->clear();
94 | $this->getKernel()->shutdown();
95 |
96 |
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/Tests/Functional/ImageEditTest.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | use Iphp\FileStoreBundle\Tests\Functional\TestBundle\Entity\Photo;
10 |
11 |
12 | class ImageEditTest extends BaseTestCase
13 | {
14 | public function testImageEditAndDelete()
15 | {
16 | $client = $this->createClient();
17 | $this->importDatabaseSchema();
18 |
19 | $this->getEntityManager()->persist($this->createTestPhotoObject());
20 | $this->getEntityManager()->flush();
21 | $this->assertLoadedPhotoParams($this->getEntityManager()->getRepository('TestBundle:Photo')->findOneById(1));
22 |
23 |
24 | //go to edit photo via form
25 | $crawler = $client->request('GET', '/edit/1/');
26 | $this->assertPhotoExistsInForm($crawler);
27 |
28 |
29 |
30 |
31 | //upload new files
32 | $newFileToUpload = new \Symfony\Component\HttpFoundation\File\UploadedFile(
33 | __DIR__ . '/../Fixtures/images/php-elephant.png', 'php-elephant.png');
34 | $alsoNewFileToUpload = new \Symfony\Component\HttpFoundation\File\UploadedFile(
35 | __DIR__ . '/../Fixtures/images/github1.png', 'github1.png');
36 |
37 | $form = $crawler->selectButton('Save')->form();
38 |
39 | //this is combined field [file => [FileType file,Checkbox delete] ]
40 | $form['form[photo][file]']->upload($newFileToUpload);
41 | $form['form[photoUpload]']->upload($alsoNewFileToUpload);
42 | $client->submit($form);
43 | $crawler = $client->followRedirect();
44 | $this->assertPreviousImageGone($crawler);
45 |
46 | //try to delete images
47 | $form = $crawler->selectButton('Save')->form();
48 | $form['form[photo][delete]']->tick();
49 | $form['form[photoInfo][delete]']->tick();
50 | $client->submit($form);
51 | $crawler = $client->followRedirect();
52 | $this->assertNoPhotoOnForm($crawler);
53 |
54 | $this->getEntityManager()->clear();
55 | $photoAfterUpdate = $this->getEntityManager()->getRepository('TestBundle:Photo')->findOneById(1);
56 | $this->assertSame($photoAfterUpdate->getPhoto(), null);
57 | $this->assertSame($photoAfterUpdate->getPhotoInfo(), null);
58 | }
59 |
60 | function createTestPhotoObject()
61 | {
62 | //Creating photo objecy with 2 images
63 | $existsFile = new \Symfony\Component\HttpFoundation\File\File(
64 | __DIR__ . '/../Fixtures/images/front-images-list.jpeg');
65 |
66 | $alsoExistsFile = new \Symfony\Component\HttpFoundation\File\File(
67 | __DIR__ . '/../Fixtures/images/sonata-admin-iphpfile.jpeg');
68 |
69 | $photo = new Photo();
70 | $photo->setTitle('Second photo')
71 | ->setDate(new \DateTime ('2013-04-05 00:00:00'))
72 | ->setPhoto($existsFile)
73 | ->setPhotoUpload($alsoExistsFile);
74 |
75 | return $photo;
76 | }
77 |
78 | function assertLoadedPhotoParams($photoLoaded)
79 | {
80 | //params of fixture files
81 | $this->assertSame($photoLoaded->getPhoto(), [
82 | 'fileName' => '/2013/04/front-images-list.jpeg',
83 | 'originalName' => 'front-images-list.jpeg',
84 | 'mimeType' => 'image/jpeg',
85 | 'size' => 67521,
86 | 'path' => '/photo/2013/04/front-images-list.jpeg',
87 | 'width' => 445,
88 | 'height' => 531
89 | ]);
90 |
91 | $this->assertSame($photoLoaded->getPhotoInfo(), [
92 | 'fileName' => '/2013/04/sonata-admin-iphpfile.jpeg',
93 | 'originalName' => 'sonata-admin-iphpfile.jpeg',
94 | 'mimeType' => 'image/jpeg',
95 | 'size' => 48332,
96 | 'path' => '/photo/2013/04/sonata-admin-iphpfile.jpeg',
97 | 'width' => 671,
98 | 'height' => 487
99 | ]);
100 | }
101 |
102 | function assertPhotoExistsInForm($crawler)
103 | {
104 | //check foto exists in form
105 | $this->assertSame($crawler->filter('input[id="form_title"][value="Second photo"]')->count(), 1);
106 | $this->assertSame($crawler->filter('option[value="2013"][selected="selected"]')->count(), 1);
107 | $this->assertSame($crawler->filter('option[value="4"][selected="selected"]')->count(), 1);
108 |
109 |
110 | //displayed loaded image and checkbox for delete image
111 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/front-images-list.jpeg"]')->count(), 1);
112 | $this->assertSame($crawler->filter('input[type="checkbox"][id="form_photo_delete"]')->count(), 1);
113 |
114 |
115 | //displayed loaded second image and checkbox for delete image
116 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/sonata-admin-iphpfile.jpeg"]')->count(), 1);
117 | $this->assertSame($crawler->filter('input[type="checkbox"][id="form_photoInfo_delete"]')->count(), 1);
118 | }
119 |
120 | function assertNoPhotoOnForm($crawler)
121 | {
122 | //after photo delete NOT displaying loaded image and checkbox for delete image
123 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/front-images-list.jpeg"]')->count(), 0);
124 | $this->assertSame($crawler->filter('input[type="checkbox"][id="form_photo_delete"]')->count(), 0);
125 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/sonata-admin-iphpfile.jpeg"]')->count(), 0);
126 | $this->assertSame($crawler->filter('input[type="checkbox"][id="form_photoInfo_delete"]')->count(), 0);
127 | }
128 |
129 | function assertPreviousImageGone($crawler)
130 | {
131 | //previous image gone
132 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/front-images-list.jpeg"]')->count(), 0);
133 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/sonata-admin-iphpfile.jpeg"]')->count(), 0);
134 | //new exists
135 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/php-elephant.png"]')->count(), 1);
136 | $this->assertSame($crawler->filter('img[src="/photo/2013/04/github1.png"]')->count(), 1);
137 |
138 | }
139 | }
--------------------------------------------------------------------------------
/Tests/Functional/ImageUploadTest.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 |
9 | use Iphp\FileStoreBundle\Tests\Functional\TestBundle\Entity\Photo;
10 |
11 | class ImageUploadTest extends BaseTestCase
12 | {
13 |
14 |
15 | public function testImageUpload()
16 | {
17 | $client = $this->createClient();
18 | $this->importDatabaseSchema();
19 |
20 | $client->enableProfiler();
21 | $crawler = $client->request('GET', '/');
22 |
23 | $this->assertTrue($client->getResponse()->isSuccessful());
24 | //Photos not uploaded yet
25 | $this->assertSame($crawler->filter('div.photo')->count(), 0);
26 |
27 |
28 | $client->enableProfiler();
29 |
30 | $fileToUpload = new \Symfony\Component\HttpFoundation\File\UploadedFile(
31 | __DIR__ . '/../Fixtures/images/sonata-admin-iphpfile.jpeg', 'sonata-admin-iphpfile.jpeg');
32 |
33 | $client->submit($crawler->selectButton('Upload')->form(), array(
34 | 'title' => 'Some title',
35 | 'photo' => $fileToUpload,
36 | 'date[year]' => '2013',
37 | 'date[month]' => '3',
38 | 'date[day]' => '15'
39 | ));
40 |
41 |
42 | $crawler = $client->followRedirect();
43 |
44 | //added 1 photo
45 | $this->assertSame($crawler->filter('div.photo')->count(), 1);
46 |
47 |
48 | $photos = $this->getEntityManager()->getRepository('TestBundle:Photo')->findAll();
49 | $this->assertSame(sizeof($photos), 1);
50 | $photo = $photos[0];
51 | $this->assertSame($photo->getTitle(), 'Some title');
52 |
53 | //path to images dir and directory naming config in Tests/Functional/config/default.yml
54 | $this->assertSame($photo->getPhoto(), array(
55 |
56 | 'fileName' => '/2013/03/sonata-admin-iphpfile.jpeg',
57 | 'originalName' => 'sonata-admin-iphpfile.jpeg',
58 | 'mimeType' => 'application/octet-stream',
59 | 'size' => $fileToUpload->getSize(),
60 | 'path' => '/photo/2013/03/sonata-admin-iphpfile.jpeg',
61 | 'width' => 671,
62 | 'height' => 487
63 | ));
64 | }
65 |
66 |
67 | }
--------------------------------------------------------------------------------
/Tests/Functional/TestBundle/Controller/DefaultController.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class DefaultController extends Controller
19 | {
20 | public function indexAction(Request $request)
21 | {
22 | $em = $this->getDoctrine()->getManager();
23 | $photo = new Photo();
24 |
25 | $uploadForm = $this->get('form.factory')->createNamedBuilder(null, FormType::class, $photo,
26 | array('csrf_protection' => false))
27 | ->add('title', TextType::class)
28 | ->add('date', DateType::class)
29 | //Using standart field type
30 | ->add('photo', FileType::class)
31 | ->add('photoUpload', FileType::class)
32 | ->getForm();
33 |
34 | $uploadForm->handleRequest($request);
35 |
36 | if ($uploadForm->isSubmitted() && $uploadForm->isValid()) {
37 |
38 | $em->persist($photo);
39 | $em->flush();
40 | return $this->redirect($this->generateUrl('photo_index'));
41 |
42 | }
43 |
44 | return $this->render('TestBundle:Photo:index.html.twig', array(
45 | 'uploadForm' => $uploadForm->createView(),
46 | 'photos' => $em->getRepository('TestBundle:Photo')->findAll()
47 | ));
48 | }
49 |
50 |
51 | public function editAction(Request $request, $id)
52 | {
53 | $em = $this->getDoctrine()->getManager();
54 | $photo = $em->getRepository('TestBundle:Photo')->findOneById($id);
55 |
56 | $editForm = $this->createFormBuilder($photo)
57 | ->add('title', TextType::class)
58 | ->add('date', DateType::class)
59 | //Using field type with showing file/image info
60 | ->add('photo', IphpFileType::class)
61 |
62 | ->add('photoUpload', FileType::class)
63 | ->add('photoInfo', IphpFileType::class)
64 |
65 | ->getForm();
66 |
67 |
68 | $editForm->handleRequest($request);
69 |
70 | if ($editForm->isSubmitted() && $editForm->isValid()) {
71 | $em->persist($photo);
72 | $em->flush();
73 | return $this->redirect($this->generateUrl('photo_edit', array('id' => $photo->getId())));
74 |
75 | }
76 |
77 | return $this->render('TestBundle:Photo:edit.html.twig', array(
78 |
79 | 'photo' => $photo,
80 | 'editForm' => $editForm->createView(),
81 | ));
82 |
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/Tests/Functional/TestBundle/Entity/Photo.php:
--------------------------------------------------------------------------------
1 | id;
66 | }
67 |
68 | /**
69 | * @param string $title
70 | * @return Photo
71 | */
72 | public function setTitle($title)
73 | {
74 | $this->title = $title;
75 | return $this;
76 | }
77 |
78 | /**
79 | * @return string
80 | */
81 | public function getTitle()
82 | {
83 | return $this->title;
84 | }
85 |
86 | /**
87 | * @param array $photo
88 | * @return Photo
89 | */
90 | public function setPhoto($photo)
91 | {
92 | $this->photo = $photo;
93 | return $this;
94 | }
95 |
96 | /**
97 | * @return array
98 | */
99 | public function getPhoto()
100 | {
101 | return $this->photo;
102 | }
103 |
104 | /**
105 | * @param \Datetime $date
106 | */
107 | public function setDate($date)
108 | {
109 | $this->date = $date;
110 | return $this;
111 | }
112 |
113 | /**
114 | * @return \Datetime
115 | */
116 | public function getDate()
117 | {
118 | return $this->date;
119 | }
120 |
121 | /**
122 | * @return mixed
123 | */
124 | public function getPhotoUpload()
125 | {
126 | return $this->photoUpload;
127 | }
128 |
129 | /**
130 | * @param mixed $photoUpload
131 | */
132 | public function setPhotoUpload($photoUpload)
133 | {
134 | $this->photoUpload = $photoUpload;
135 | }
136 |
137 | /**
138 | * @return mixed
139 | */
140 | public function getPhotoInfo()
141 | {
142 | return $this->photoInfo;
143 | }
144 |
145 | /**
146 | * @param mixed $photoInfo
147 | */
148 | public function setPhotoInfo($photoInfo)
149 | {
150 | $this->photoInfo = $photoInfo;
151 | }
152 |
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/Tests/Functional/TestBundle/Resources/views/Photo/edit.html.twig:
--------------------------------------------------------------------------------
1 | Edit photo
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/Tests/Functional/TestBundle/Resources/views/Photo/index.html.twig:
--------------------------------------------------------------------------------
1 | {# src/Iphpsandbox/PhotoBundle/Resources/views/Photo/index.html.twig #}
2 | Photos
3 |
4 | Uploaded photos
5 |
6 | {% for photo in photos %}
7 |
15 | {% endfor%}
16 |
17 | Upload new photo
18 |
19 |
--------------------------------------------------------------------------------
/Tests/Functional/TestBundle/TestBundle.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class TestBundle extends Bundle
11 | {
12 | }
--------------------------------------------------------------------------------
/Tests/Functional/TestXmlConfigBundle/Entity/File.php:
--------------------------------------------------------------------------------
1 | id;
45 | }
46 |
47 | /**
48 | * @param string $title
49 | * @return Photo
50 | */
51 | public function setTitle($title)
52 | {
53 | $this->title = $title;
54 | return $this;
55 | }
56 |
57 | /**
58 | * @return string
59 | */
60 | public function getTitle()
61 | {
62 | return $this->title;
63 | }
64 |
65 |
66 | /**
67 | * @param \Datetime $date
68 | */
69 | public function setDate($date)
70 | {
71 | $this->date = $date;
72 | return $this;
73 | }
74 |
75 | /**
76 | * @return \Datetime
77 | */
78 | public function getDate()
79 | {
80 | return $this->date;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Tests/Functional/TestXmlConfigBundle/Entity/UploadableEntity.php:
--------------------------------------------------------------------------------
1 |
11 | * @FileStore\Uploadable
12 | */
13 |
14 | abstract class UploadableEntity
15 | {
16 |
17 |
18 | /**
19 | * @Assert\Image( maxSize="20M")
20 | * @FileStore\UploadableField(mapping="file")
21 | **/
22 | protected $file;
23 |
24 | public function setFile($file)
25 | {
26 | $this->file = $file;
27 | return $this;
28 | }
29 |
30 | public function getFile()
31 | {
32 | return $this->file;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/Functional/TestXmlConfigBundle/Resources/config/doctrine/File.orm.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Tests/Functional/TestXmlConfigBundle/TestXmlConfigBundle.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | class TestXmlConfigBundle extends Bundle
11 | {
12 | }
--------------------------------------------------------------------------------
/Tests/Functional/config/database.php:
--------------------------------------------------------------------------------
1 | getParameter ('kernel.test_env_dir').'/db.sqllite';
4 |
5 |
6 | $container->loadFromExtension('doctrine', array(
7 | 'dbal' => array(
8 | 'driver' => 'pdo_sqlite',
9 | 'path' => $path,
10 | ),
11 | ));
--------------------------------------------------------------------------------
/Tests/Functional/config/default.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: framework.yml }
3 | - { resource: doctrine.yml }
4 | - { resource: twig.yml }
5 | - { resource: database.php }
6 |
7 |
8 | iphp_file_store:
9 | mappings:
10 | photo:
11 | upload_dir: %kernel.test_env_dir%/web/photo
12 | upload_path: /photo
13 | directory_namer:
14 | date:
15 | params: { field : date, depth : month }
16 | namer: ~
17 |
18 |
19 | file:
20 | upload_dir: %kernel.test_env_dir%/web/file
21 | upload_path: /file
22 |
23 | #chain of directory namers
24 | directory_namer:
25 | entityName: ~
26 | property:
27 | params: { use_field_name : true }
28 | date:
29 | params: { field : date, depth : year }
30 | namer:
31 | #naming file by id
32 | property: ~
33 |
34 |
--------------------------------------------------------------------------------
/Tests/Functional/config/default_newfilepath.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: framework.yml }
3 | - { resource: doctrine.yml }
4 | - { resource: twig.yml }
5 | - { resource: database.php }
6 |
7 |
8 | iphp_file_store:
9 | mappings:
10 | file:
11 | upload_dir: %kernel.test_env_dir%/web/other/uploads
12 | upload_path: /other/uploads
13 |
14 | #directory naming by id property
15 | directory_namer:
16 | property: ~
17 |
18 | #file naming by translited title property
19 | namer:
20 | property:
21 | params: { field: title}
22 | translit:
23 |
24 |
--------------------------------------------------------------------------------
/Tests/Functional/config/doctrine.yml:
--------------------------------------------------------------------------------
1 |
2 |
3 | doctrine:
4 | orm:
5 | auto_generate_proxy_classes: "%kernel.debug%"
6 | entity_managers:
7 | default:
8 | auto_mapping: true
9 | services:
10 | em: "@doctrine.orm.entity_manager"
--------------------------------------------------------------------------------
/Tests/Functional/config/framework.yml:
--------------------------------------------------------------------------------
1 | framework:
2 | secret: test
3 | test: ~
4 | session:
5 | storage_id: session.storage.mock_file
6 | form: true
7 | csrf_protection: true
8 | validation:
9 | enabled: true
10 | enable_annotations: true
11 | router:
12 | resource: "%kernel.root_dir%/config/routing.yml"
--------------------------------------------------------------------------------
/Tests/Functional/config/routing.yml:
--------------------------------------------------------------------------------
1 | photo_index:
2 | path: /
3 | defaults: { _controller: TestBundle:Default:index }
4 |
5 | photo_edit:
6 | path: /edit/{id}/
7 | defaults: { _controller: TestBundle:Default:edit }
--------------------------------------------------------------------------------
/Tests/Functional/config/twig.yml:
--------------------------------------------------------------------------------
1 | framework:
2 | templating:
3 | engines: [twig, php]
4 |
5 | twig:
6 | debug: %kernel.debug%
7 | strict_variables: %kernel.debug%
--------------------------------------------------------------------------------
/Tests/Mapping/Annotation/UploadableFieldTest.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class UploadableFieldTest extends \PHPUnit_Framework_TestCase
13 | {
14 | /**
15 | * @var \Iphp\FileStoreBundle\Mapping\Annotation\UploadableField;
16 | */
17 | protected $uploadableField;
18 |
19 | public function setUp()
20 | {
21 | $this->uploadableField = new UploadableField(array('mapping' => 'dummy_file'));
22 | }
23 |
24 | /**
25 | * @expectedException \InvalidArgumentException
26 | */
27 | public function testExceptionThrownWhenNoMappingAttribute()
28 | {
29 | new UploadableField(array(
30 | 'fileNameProperty' => 'fileName'
31 | ));
32 | }
33 |
34 |
35 | public function testGetMapping()
36 | {
37 |
38 | $this->assertSame($this->uploadableField->getMapping(), 'dummy_file');
39 | }
40 |
41 |
42 | public function testSetMapping()
43 | {
44 | $this->uploadableField->setMapping('dummy_file');
45 | $this->assertSame($this->uploadableField->getMapping(), 'dummy_file');
46 | }
47 |
48 |
49 | public function testGetSetFileUploadPropertyName()
50 | {
51 | $this->uploadableField->setFileUploadPropertyName('file');
52 | $this->assertSame($this->uploadableField->getFileUploadPropertyName(), 'file');
53 | $this->assertSame($this->uploadableField->getFileDataPropertyName(), 'file');
54 |
55 | }
56 |
57 |
58 |
59 |
60 | public function testGetSetFileDataPropertyName()
61 | {
62 | $this->uploadableField->setFileUploadPropertyName('file');
63 | $this->uploadableField->setFileDataPropertyName('file_data');
64 |
65 | $this->assertSame($this->uploadableField->getFileUploadPropertyName(), 'file');
66 | $this->assertSame($this->uploadableField->getFileDataPropertyName(), 'file_data');
67 | }
68 |
69 |
70 |
71 |
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/Tests/Mapping/PropertyMappingFactoryTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class PropertyMappingFactoryTest extends \PHPUnit_Framework_TestCase
15 | {
16 |
17 | /**
18 | * @var \Iphp\FileStoreBundle\Naming\NamerServiceInvoker
19 | */
20 | protected $namerServiceInvoker;
21 |
22 |
23 | /**
24 | * @var \Iphp\FileStoreBundle\Driver\AnnotationDriver $driver
25 | */
26 | protected $driver;
27 |
28 |
29 | /**
30 | * Sets up the test.
31 | */
32 | public function setUp()
33 | {
34 | $this->driver = Mocks::getAnnotationDriverMock($this);
35 | $this->namerServiceInvoker = Mocks::getNamerServiceInvokerMock($this);
36 | }
37 |
38 | /**
39 | * if a non uploadable object is passed in - return array()
40 | *
41 | */
42 | public function testFromObjectThrowsExceptionIfNotUploadable()
43 | {
44 | $obj = new \StdClass();
45 | $class = new \ReflectionClass($obj);
46 |
47 | $this->driver
48 | ->expects($this->once())
49 | ->method('readUploadable')
50 | ->will($this->returnValue(null));
51 |
52 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, array());
53 | $this->assertEquals($factory->getMappingsFromObject($obj, $class), array());
54 | }
55 |
56 | /**
57 | * Test the fromObject method with one uploadable
58 | * field.
59 | */
60 | public function testFromObjectOneField()
61 | {
62 | $obj = new DummyEntity();
63 | $class = new \ReflectionClass($obj);
64 |
65 | $mappingsConfig = array(
66 | 'dummy_file' => array(
67 | 'upload_dir' => '/www/web/images',
68 | 'upload_path' => '/images'
69 | )
70 | );
71 |
72 | $uploadable = Mocks::getUploadableMock($this);
73 | $fileField = Mocks::getUploadableFieldMock($this);
74 |
75 | $fileField
76 | ->expects($this->any())
77 | ->method('getMapping')
78 | ->will($this->returnValue('dummy_file'));
79 |
80 | $fileField
81 | ->expects($this->once())
82 | ->method('getFileUploadPropertyName')
83 | ->will($this->returnValue('file'));
84 |
85 | $fileField
86 | ->expects($this->once())
87 | ->method('getFileDataPropertyName')
88 | ->will($this->returnValue('file'));
89 |
90 |
91 | $this->driver
92 | ->expects($this->once())
93 | ->method('readUploadable')
94 | ->with($class)
95 | ->will($this->returnValue($uploadable));
96 |
97 | $this->driver
98 | ->expects($this->once())
99 | ->method('readUploadableFields')
100 | ->with($class)
101 | ->will($this->returnValue(array($fileField)));
102 |
103 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, $mappingsConfig);
104 | $mappings = $factory->getMappingsFromObject($obj, $class);
105 |
106 | $this->assertEquals(1, count($mappings));
107 |
108 | if (count($mappings) > 0) {
109 | $mapping = $mappings[0];
110 |
111 | $this->assertEquals('dummy_file', $mapping->getMappingName());
112 | $this->assertEquals('/www/web/images', $mapping->getUploadDir());
113 | $this->assertEquals('/images', $mapping->getUploadPath());
114 | $this->assertTrue($mapping->getDeleteOnRemove());
115 | $this->assertFalse($mapping->hasNamer());
116 | $this->assertFalse($mapping->hasDirectoryNamer());
117 | $this->assertFalse($mapping->isStoreFullDir());
118 | $this->assertFalse($mapping->isOverwriteDuplicates());
119 |
120 | $this->assertSame($mapping->getObj(), $obj);
121 |
122 | }
123 | }
124 |
125 | /**
126 | * Test that an exception is thrown when an invalid mapping name
127 | * is specified.
128 | *
129 | * @expectedException \InvalidArgumentException
130 | */
131 | public function testThrowsExceptionOnInvalidMappingName()
132 | {
133 | $obj = new DummyEntity();
134 | $class = new \ReflectionClass($obj);
135 |
136 | $mappingsConfig = array(
137 | 'bad_name' => array()
138 | );
139 |
140 | $uploadable = Mocks::getUploadableMock($this);
141 | $fileField = Mocks::getUploadableFieldMock($this);
142 |
143 | $fileField
144 | ->expects($this->any())
145 | ->method('getMapping')
146 | ->will($this->returnValue('dummy_file'));
147 |
148 |
149 | $this->driver
150 | ->expects($this->once())
151 | ->method('readUploadable')
152 | ->with($class)
153 | ->will($this->returnValue($uploadable));
154 |
155 | $this->driver
156 | ->expects($this->once())
157 | ->method('readUploadableFields')
158 | ->with($class)
159 | ->will($this->returnValue(array($fileField)));
160 |
161 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, $mappingsConfig);
162 | $mappings = $factory->getMappingsFromObject($obj, $class);
163 | }
164 |
165 | /**
166 | * Test that the fromField method returns null when an invalid
167 | * field name is specified.
168 | */
169 | public function testFromFieldReturnsNullOnInvalidFieldName()
170 | {
171 | $obj = new DummyEntity();
172 | $class = new \ReflectionClass($obj);
173 |
174 | $uploadable = Mocks::getUploadableMock($this);
175 |
176 | $this->driver
177 | ->expects($this->once())
178 | ->method('readUploadable')
179 | ->with($class)
180 | ->will($this->returnValue($uploadable));
181 |
182 | $this->driver
183 | ->expects($this->once())
184 | ->method('readUploadableField')
185 | ->with($class)
186 | ->will($this->returnValue(null));
187 |
188 | $this->driver
189 | ->expects($this->once())
190 | ->method('readUploadableFields')
191 | ->with($class)
192 | ->will($this->returnValue(array()));
193 |
194 |
195 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, array());
196 | $mapping = $factory->getMappingFromField($obj, $class, 'oops');
197 |
198 | $this->assertNull($mapping);
199 | }
200 |
201 |
202 | /**
203 | */
204 | public function testGetMappingFromField()
205 | {
206 | $obj = new DummyEntity();
207 | $class = new \ReflectionClass($obj);
208 |
209 | $uploadable = Mocks::getUploadableMock($this);
210 | $fileField = Mocks::getUploadableFieldMock($this);
211 |
212 |
213 | $mappingsConfig = array(
214 | 'dummy_file' => array(
215 | 'upload_dir' => '/www/web/images',
216 | 'upload_path' => '/images'
217 | )
218 | );
219 |
220 |
221 | $fileField
222 | ->expects($this->any())
223 | ->method('getMapping')
224 | ->will($this->returnValue('dummy_file'));
225 |
226 | $fileField
227 | ->expects($this->once())
228 | ->method('getFileUploadPropertyName')
229 | ->will($this->returnValue('file'));
230 |
231 | $fileField
232 | ->expects($this->once())
233 | ->method('getFileDataPropertyName')
234 | ->will($this->returnValue('file'));
235 |
236 | $this->driver
237 | ->expects($this->once())
238 | ->method('readUploadable')
239 | ->with($class)
240 | ->will($this->returnValue($uploadable));
241 |
242 | $this->driver
243 | ->expects($this->once())
244 | ->method('readUploadableField')
245 | ->with($class)
246 | ->will($this->returnValue($fileField));
247 |
248 |
249 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, $mappingsConfig);
250 | $mapping = $factory->getMappingFromField($obj, $class, 'file');
251 |
252 | $this->assertSame($mapping->getObj(), $obj);
253 | $this->assertEquals('dummy_file', $mapping->getMappingName());
254 | $this->assertEquals('/www/web/images', $mapping->getUploadDir());
255 | $this->assertEquals('/images', $mapping->getUploadPath());
256 | $this->assertTrue($mapping->getDeleteOnRemove());
257 | $this->assertFalse($mapping->hasNamer());
258 | $this->assertFalse($mapping->hasDirectoryNamer());
259 | $this->assertFalse($mapping->isStoreFullDir());
260 | $this->assertFalse($mapping->isOverwriteDuplicates());
261 | }
262 |
263 |
264 | public function testConfiguredNamerRetrievedFromInvoker()
265 | {
266 | $obj = new DummyEntity();
267 | $class = new \ReflectionClass($obj);
268 |
269 | $mappingsConfig = array(
270 | 'dummy_file' => array(
271 | 'upload_dir' => '/www/web/images',
272 | 'upload_path' => '/images',
273 | 'namer' => array('translit' => array('service' => 'iphp.filestore.namer.default'))
274 | )
275 |
276 | );
277 |
278 | $uploadable = Mocks::getUploadableMock($this);
279 | $fileField = Mocks::getUploadableFieldMock($this);
280 |
281 | $fileField
282 | ->expects($this->any())
283 | ->method('getMapping')
284 | ->will($this->returnValue('dummy_file'));
285 |
286 | $fileField
287 | ->expects($this->once())
288 | ->method('getFileUploadPropertyName')
289 | ->will($this->returnValue('file'));
290 |
291 | $fileField
292 | ->expects($this->once())
293 | ->method('getFileDataPropertyName')
294 | ->will($this->returnValue('file'));
295 |
296 |
297 | $this->driver
298 | ->expects($this->once())
299 | ->method('readUploadable')
300 | ->with($class)
301 | ->will($this->returnValue($uploadable));
302 |
303 | $this->driver
304 | ->expects($this->once())
305 | ->method('readUploadableFields')
306 | ->with($class)
307 | ->will($this->returnValue(array($fileField)));
308 |
309 | $factory = new PropertyMappingFactory($this->namerServiceInvoker, $this->driver, $mappingsConfig);
310 | $mappings = $factory->getMappingsFromObject($obj, $class);
311 |
312 | $this->assertEquals(1, count($mappings));
313 |
314 | if (count($mappings) > 0) {
315 | $mapping = $mappings[0];
316 |
317 | $this->namerServiceInvoker
318 | ->expects($this->once())
319 | ->method('rename')
320 | ->with('iphp.filestore.namer.default', 'translit', $mapping, 'ab cde', array())
321 | ->will($this->returnValue('ab-cde'));
322 |
323 |
324 | $this->assertEquals($mapping->useFileNamer('ab cde'), 'ab-cde');
325 | $this->assertTrue($mapping->hasNamer());
326 | }
327 | }
328 |
329 |
330 | }
331 |
--------------------------------------------------------------------------------
/Tests/Mapping/PropertyMappingTest.php:
--------------------------------------------------------------------------------
1 | namerServiceInvoker = Mocks::getNamerServiceInvokerMock($this);
29 | $this->fileStorage = Mocks::getFileStorageMock($this);
30 | }
31 |
32 |
33 | /**
34 | * @param $object
35 | * @param array $mergeConfig
36 | * @return PropertyMapping
37 | */
38 | public function getPropertyMapping($object, $mergeConfig = array())
39 | {
40 | $config = $object->defaultFileStoreConfig['dummy_file'];
41 | if ($mergeConfig) $config = array_merge($config, $mergeConfig);
42 |
43 | return new PropertyMapping($object, $config, $this->namerServiceInvoker);
44 | }
45 |
46 | public function testUseDirectoryNamerNoExists()
47 | {
48 | $propertyMapping = $this->getPropertyMapping(new DummyEntity());
49 |
50 | $this->assertFalse($propertyMapping->hasDirectoryNamer());
51 | $this->assertSame($propertyMapping->useDirectoryNamer('file name', 'file name'), '');
52 | }
53 |
54 |
55 | public function testUseDirectoryNamerExists()
56 | {
57 | $propertyMapping = $this->getPropertyMapping(new DummyEntity(),
58 | array('directory_namer' =>
59 | array('entityName' => array('service' => 'iphp.filestore.directory_namer.default'))));
60 |
61 |
62 | $this->assertTrue($propertyMapping->hasDirectoryNamer());
63 |
64 | $this->namerServiceInvoker
65 | ->expects($this->once())
66 | ->method('rename')
67 | ->with('iphp.filestore.directory_namer.default', 'entityName', $propertyMapping, 'file name', array())
68 | ->will($this->returnValue('DummyFile'));
69 |
70 | $this->assertSame($propertyMapping->useDirectoryNamer('file name', 'file name'), '/DummyFile');
71 | }
72 |
73 |
74 | public function testNeedResolveCollisionFileNoExists()
75 | {
76 | $propertyMapping = $this->getPropertyMapping(new DummyEntity());
77 |
78 | $this->fileStorage
79 | ->expects($this->once())
80 | ->method('fileExists')
81 | ->with( '/www/web/images/123')
82 | ->will($this->returnValue(false));
83 |
84 |
85 |
86 |
87 |
88 |
89 | //by default overwriteDuplicates = false
90 | $this->assertFalse($propertyMapping->needResolveCollision('123', $this->fileStorage));
91 | }
92 |
93 |
94 | public function testNeedResolveCollisionOverwriteDuplicates()
95 | {
96 | $propertyMapping = $this->getPropertyMapping(new DummyEntity(), array('overwrite_duplicates' => true));
97 | $this->fileStorage->expects($this->never())->method('fileExists');
98 |
99 | //by default overwriteDuplicates = false
100 | $this->assertFalse($propertyMapping->needResolveCollision('123', $this->fileStorage));
101 | }
102 |
103 |
104 | public function testPrepareFileName()
105 | {
106 | $propertyMapping = $this->getPropertyMapping(new DummyEntity());
107 |
108 | $this->namerServiceInvoker
109 | ->expects($this->once())
110 | ->method('rename')
111 | ->with('iphp.filestore.namer.default', 'translit', $propertyMapping, 'ab cd', array())
112 | ->will($this->returnValue('ab-cd'));
113 |
114 |
115 | $this->fileStorage
116 | ->expects($this->once())
117 | ->method('fileExists')
118 | ->with('/www/web/images/ab-cd')
119 | ->will($this->returnValue(false));
120 |
121 |
122 | //Default false
123 | $this->assertFalse($propertyMapping->isStoreFullDir());
124 | //From config
125 | $this->assertEquals($propertyMapping->getUploadPath(), '/images');
126 |
127 | $this->assertSame($propertyMapping->prepareFileName('ab cd', $this->fileStorage),
128 | array('/ab-cd', '/images/ab-cd'));
129 | }
130 |
131 |
132 | /**
133 | * Once resolved collision
134 | */
135 | public function testPrepareFileNameWithResolveCollision()
136 | {
137 | $propertyMapping = $this->getPropertyMapping(new DummyEntity());
138 |
139 |
140 | //service iphp.filestore.namer.default, method translit will invoke once,
141 | //"ab cd" will be translited to "ab-cd"
142 | $this->namerServiceInvoker
143 | ->expects($this->once())
144 | ->method('rename')
145 | ->with('iphp.filestore.namer.default', 'translit', $propertyMapping, 'ab cd', array())
146 | ->will($this->returnValue('ab-cd'));
147 |
148 |
149 | $this->namerServiceInvoker
150 | ->expects($this->once())
151 | ->method('resolveCollision')
152 | ->with('iphp.filestore.namer.default', 'ab-cd', 1)
153 | ->will($this->returnValue('ab-cd_1'));
154 |
155 |
156 | $fileExistsCalls = 0;
157 |
158 | $this->fileStorage
159 | ->expects($this->any())
160 | ->method('fileExists')
161 |
162 | ->will($this->returnCallback(function() use (&$fileExistsCalls)
163 | {
164 | $args = func_get_args();
165 | $fileExistsCalls++;
166 | return $args[0] == '/www/web/images/ab-cd' ? true : false;
167 | }));
168 |
169 |
170 | $this->assertSame($propertyMapping->prepareFileName('ab cd', $this->fileStorage),
171 | array('/ab-cd_1', '/images/ab-cd_1'));
172 |
173 | $this->assertSame($fileExistsCalls, 2);
174 |
175 | }
176 |
177 |
178 | /**
179 | * Not resolved collision
180 | * @expectedException \Exception
181 | */
182 | public function testPrepareFileNameWithoutResolveCollision()
183 | {
184 | $propertyMapping = $this->getPropertyMapping(new DummyEntity());
185 |
186 | //service iphp.filestore.namer.default, method translit will invoke once,
187 | //"ab cd" will be translited to "ab-cd"
188 | $this->namerServiceInvoker
189 | ->expects($this->once())
190 | ->method('rename')
191 | ->with('iphp.filestore.namer.default', 'translit', $propertyMapping, 'ab cd', array())
192 | ->will($this->returnValue('ab-cd'));
193 |
194 |
195 | //always return same name - no resolve
196 | $this->namerServiceInvoker
197 | ->expects($this->any())
198 | ->method('resolveCollision')
199 | ->will($this->returnValue('ab-cd'));
200 |
201 | $this->fileStorage
202 | ->expects($this->any())
203 | ->method('fileExists')
204 | ->will($this->returnCallback(function()
205 | {
206 | $args = func_get_args();
207 |
208 | return $args[0] == '/www/web/images/ab-cd' ? true : false;
209 | }));
210 |
211 |
212 | $propertyMapping->prepareFileName('ab cd', $this->fileStorage);
213 |
214 | }
215 |
216 |
217 | /**
218 | * Exception about need namer for resolving collisions
219 | * @expectedException \Exception
220 | */
221 | function testResolveFileCollisionNoNamer()
222 | {
223 | $propertyMapping = $this->getPropertyMapping(new DummyEntity(), array('namer' => null));
224 |
225 | $this->namerServiceInvoker->expects($this->never())->method('rename');
226 | $this->fileStorage
227 | ->expects($this->once())
228 | ->method('fileExists')
229 | //No translited
230 | ->with('/www/web/images/ab cd')
231 | ->will($this->returnValue(true));
232 |
233 |
234 | $propertyMapping->prepareFileName('ab cd', $this->fileStorage);
235 | }
236 |
237 |
238 | public function testGetPropertyName()
239 | {
240 |
241 | $obj = new DummyEntitySeparateDataField();
242 | $class = new \ReflectionClass($obj);
243 | $file = Mocks::getFileMock($this);
244 |
245 |
246 | $propertyMapping = $this->getPropertyMapping($obj);
247 |
248 | $propertyMapping->setFileUploadProperty($class->getProperty('file'));
249 | $this->assertSame($propertyMapping->getFileUploadPropertyName(), 'file');
250 |
251 | $propertyMapping->setFileDataProperty($class->getProperty('file_data'));
252 | $this->assertSame($propertyMapping->getFileDataPropertyName(), 'file_data');
253 |
254 |
255 | $propertyMapping->setFileUploadPropertyValue($file);
256 | $this->assertSame( $propertyMapping->getFileUploadPropertyValue(), $file);
257 |
258 |
259 | $propertyMapping->setFileDataPropertyValue(array(1));
260 | $this->assertSame( $propertyMapping->getFileDataPropertyValue(), array(1));
261 | }
262 |
263 |
264 |
265 | public function testResolveFileName()
266 | {
267 | $obj = new DummyEntitySeparateDataField();
268 | $class = new \ReflectionClass($obj);
269 |
270 |
271 |
272 |
273 | $propertyMapping = $this->getPropertyMapping($obj);
274 |
275 | $propertyMapping->setFileDataProperty($class->getProperty('file_data'));
276 | $propertyMapping->setFileDataPropertyValue(array('fileName' => 123));
277 |
278 |
279 | $this->assertSame ($propertyMapping->resolveFileName(), $propertyMapping->getUploadDir() . '/123');
280 |
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/Tests/Mocks.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | use Iphp\FileStoreBundle\Naming\NamerServiceInvoker;
9 | use Iphp\FileStoreBundle\Driver\AnnotationDriver;
10 |
11 | class Mocks
12 | {
13 | /**
14 | * Creates a mock args
15 | *
16 | * @return \Doctrine\Common\EventArgs EventArgs
17 | */
18 | static function getEventArgsMock(\PHPUnit_Framework_TestCase $testCase)
19 | {
20 | return $testCase->getMockBuilder('Doctrine\Common\EventArgs')
21 | ->disableOriginalConstructor()
22 | ->getMock();
23 | }
24 |
25 |
26 | static function getContainerMock(\PHPUnit_Framework_TestCase $testCase)
27 | {
28 | return $testCase->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')
29 | ->disableOriginalConstructor()
30 | ->getMock();
31 | }
32 |
33 |
34 | /**
35 | * Creates a mock uploadable
36 | *
37 | * @return \Iphp\FileStoreBundle\Mapping\Annotation\Uploadable Uploadable
38 | */
39 | static function getUploadableMock(\PHPUnit_Framework_TestCase $testCase)
40 | {
41 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Mapping\Annotation\Uploadable')
42 | ->disableOriginalConstructor()
43 | ->getMock();
44 | }
45 |
46 |
47 | /**
48 | * Creates a mock uploadable field
49 | *
50 | * @return \Iphp\FileStoreBundle\Mapping\Annotation\UploadableField UploadableField
51 | */
52 | static function getUploadableFieldMock(\PHPUnit_Framework_TestCase $testCase)
53 | {
54 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Mapping\Annotation\UploadableField')
55 | ->disableOriginalConstructor()
56 | ->getMock();
57 | }
58 |
59 | /**
60 | * Creates a mock property mapping
61 | *
62 | * @return \Iphp\FileStoreBundle\Mapping\PropertyMapping UploadableField
63 | */
64 | static function getPropertyMappingMock(\PHPUnit_Framework_TestCase $testCase)
65 | {
66 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Mapping\PropertyMapping')
67 | ->disableOriginalConstructor()
68 | ->getMock();
69 | }
70 |
71 |
72 | /**
73 | * Creates a mock annotation driver.
74 | *
75 | * @return \Iphp\FileStoreBundle\Driver\AnnotationDriver The driver.
76 | */
77 | static function getAnnotationDriverMock(\PHPUnit_Framework_TestCase $testCase)
78 | {
79 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Driver\AnnotationDriver')
80 | ->disableOriginalConstructor()
81 | ->getMock();
82 | }
83 |
84 |
85 | /**
86 | * Creates a mock for namer service invoker.
87 | *
88 | * @return \Iphp\FileStoreBundle\Naming\NamerServiceInvoker mock namer service invoker
89 | */
90 | static function getNamerServiceInvokerMock(\PHPUnit_Framework_TestCase $testCase)
91 | {
92 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Naming\NamerServiceInvoker')
93 | ->disableOriginalConstructor()
94 | ->getMock();
95 | }
96 |
97 | /**
98 | * Creates a mock data storage
99 | *
100 | * @return \Iphp\FileStoreBundle\DataStorage\DataStorageInterface The mock data storage
101 | */
102 | static function getDataStorageMock(\PHPUnit_Framework_TestCase $testCase)
103 | {
104 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\DataStorage\DataStorageInterface')
105 | ->disableOriginalConstructor()
106 | ->getMock();
107 | }
108 |
109 |
110 | /**
111 | * Creates a mock storage.
112 | *
113 | * @return \Iphp\FileStoreBundle\FileStorage\FileStorageInterface mock file storage
114 | */
115 | static function getFileStorageMock(\PHPUnit_Framework_TestCase $testCase)
116 | {
117 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\FileStorage\FileStorageInterface')
118 | ->disableOriginalConstructor()
119 | ->getMock();
120 | }
121 |
122 |
123 | /**
124 | * Creates a mock mapping factory.
125 | *
126 | * @return \Iphp\FileStoreBundle\Mapping\PropertyMappingFactory The factory.
127 | */
128 | static function getPropertyMappingFactoryMock(\PHPUnit_Framework_TestCase $testCase,
129 | NamerServiceInvoker $namerServiceInvoker,
130 | AnnotationDriver $driver,
131 | $mappingsConfig = array())
132 | {
133 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\Mapping\PropertyMappingFactory')
134 | ->setConstructorArgs(array($namerServiceInvoker, $driver, $mappingsConfig))
135 | ->getMock();
136 |
137 | }
138 |
139 | /**
140 | * Creates a mock file
141 | *
142 | * @return \Symfony\Component\HttpFoundation\File\File The file.
143 | */
144 | static function getFileMock(\PHPUnit_Framework_TestCase $testCase)
145 | {
146 | return $testCase->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
147 | ->setConstructorArgs(array(null, false))
148 | ->getMock();
149 | }
150 |
151 |
152 | /**
153 | * Creates a mock of uploadedFile
154 | *
155 | * @return \Symfony\Component\HttpFoundation\File\UploadedFile uploaded file
156 | */
157 | static function getUploadedFileMock(\PHPUnit_Framework_TestCase $testCase)
158 | {
159 | return $testCase->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile')
160 | ->disableOriginalConstructor()
161 | ->getMock();
162 | }
163 |
164 |
165 | /**
166 | * Creates a mock file
167 | *
168 | * @return \Iphp\FileStoreBundle\File\File The file.
169 | */
170 | static function getIphpFileMock(\PHPUnit_Framework_TestCase $testCase)
171 | {
172 | return $testCase->getMockBuilder('Iphp\FileStoreBundle\File\File')
173 | ->setConstructorArgs(array(null, false))
174 | ->getMock();
175 | }
176 |
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/Tests/Naming/DefaultDirectoryNamerTest.php:
--------------------------------------------------------------------------------
1 | namer = new DefaultDirectoryNamer;
18 | }
19 |
20 |
21 | protected function getMappingWithObject($object)
22 | {
23 | $propertyMapping = Mocks::getPropertyMappingMock($this);
24 | $propertyMapping->expects($this->any())
25 | ->method('getObj')
26 | ->will($this->returnValue($object));
27 |
28 | return $propertyMapping;
29 | }
30 |
31 |
32 | public function testPropertyRenameById()
33 | {
34 |
35 | $obj = new DummyEntity();
36 | $obj->setId(12345);
37 | $propertyMapping = $this->getMappingWithObject($obj);
38 |
39 | $this->assertSame($this->namer->propertyRename($propertyMapping, 'some-name.jpg', array()), '12345');
40 |
41 | }
42 |
43 |
44 | public function testPropertyRenameByMultiple()
45 | {
46 | $obj = new DummyEntity();
47 | $obj->setId(12345);
48 | $file = Mocks::getFileMock($this);
49 | $file->expects($this->once())
50 | ->method('getSize')
51 | ->will($this->returnValue('100'));
52 |
53 | $obj->setFile($file);
54 | $propertyMapping = $this->getMappingWithObject($obj);
55 |
56 | $this->assertSame($this->namer->propertyRename($propertyMapping, 'some-name.jpg', array(
57 | 'field' => 'id/file.size'
58 | )), '12345/100');
59 |
60 | }
61 |
62 |
63 |
64 |
65 | public function testEntityNameRename()
66 | {
67 | $propertyMapping = $this->getMappingWithObject(new DummyEntity());
68 | $this->assertSame($this->namer->entityNameRename($propertyMapping, 'some-name.jpg', array()), 'DummyEntity');
69 | }
70 |
71 |
72 | public function testReplaceRename()
73 | {
74 | $propertyMapping = $this->getMappingWithObject(new DummyEntity());
75 | $this->assertSame($this->namer->replaceRename($propertyMapping, 'some-dir',
76 | array('some' => 'new')), 'new-dir');
77 | }
78 |
79 |
80 | public function testDateRename()
81 | {
82 | $obj = new DummyEntity();
83 | $obj->setCreatedAt(new \DateTime ('2013-05-21 12:12:12'));
84 | $propertyMapping = $this->getMappingWithObject($obj);
85 |
86 |
87 | $this->assertSame($this->namer->dateRename($propertyMapping, 'some-dir',
88 | array('field' => 'createdAt')), '2013/05/21');
89 | }
90 |
91 |
92 | public function testDateRenameByYear()
93 | {
94 | $obj = new DummyEntity();
95 | $obj->setCreatedAt(new \DateTime ('2013-05-21 12:12:12'));
96 | $propertyMapping = $this->getMappingWithObject($obj);
97 |
98 |
99 | $this->assertSame($this->namer->dateRename($propertyMapping, 'some-dir',
100 | array('field' => 'createdAt', 'depth' => 'year')), '2013');
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Tests/Naming/DefaultNamerTest.php:
--------------------------------------------------------------------------------
1 | namer = new DefaultNamer;
22 | }
23 |
24 |
25 | protected function getMappingWithObject($object)
26 | {
27 | $propertyMapping = Mocks::getPropertyMappingMock($this);
28 | $propertyMapping->expects($this->any())
29 | ->method('getObj')
30 | ->will($this->returnValue($object));
31 |
32 | return $propertyMapping;
33 | }
34 |
35 |
36 | public function testTranslitRename()
37 | {
38 | $propertyMapping = Mocks::getPropertyMappingMock($this);
39 | $this->assertSame($this->namer->translitRename($propertyMapping,
40 | 'АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя'),
41 | 'aabbvvggddeeeezzzziijjkkllmmnnoopprrssttuuffhhccccssssyyeeuuaa');
42 |
43 |
44 | $this->assertSame($this->namer->translitRename($propertyMapping,
45 | 'En Français on a des caractères accentués !'),
46 | 'en-francais-on-a-des-caracteres-accentues');
47 |
48 |
49 | $this->assertSame($this->namer->translitRename($propertyMapping, ' ! , Давай-Давай .'),
50 | 'davaj-davaj-.');
51 | }
52 |
53 |
54 | public function testPropertyRename()
55 | {
56 | $obj = new DummyEntity();
57 | $obj->setId(12345);
58 |
59 | $propertyMapping = $this->getMappingWithObject($obj);
60 | $this->assertSame($this->namer->propertyRename($propertyMapping, 'some-name.jpg', array()), '12345.jpg');
61 | }
62 |
63 |
64 | public function testPropertyRenameByField()
65 | {
66 | $obj = new DummyEntity();
67 | $obj->setTitle('some-title');
68 |
69 | $propertyMapping = $this->getMappingWithObject($obj);
70 | $this->assertSame(
71 | $this->namer->propertyRename($propertyMapping, 'some-name.jpg', array('field' => 'title')),
72 | 'some-title.jpg');
73 | }
74 |
75 |
76 | public function testPropertyPrefixRename()
77 | {
78 | $obj = new DummyEntity();
79 | $obj->setId(12345);
80 |
81 | $propertyMapping = $this->getMappingWithObject($obj);
82 | $this->assertSame(
83 | $this->namer->propertyPrefixRename($propertyMapping, 'some-name.jpg', array()),
84 | '12345-some-name.jpg');
85 | }
86 |
87 | public function testPropertyPrefixRenameWithParams()
88 | {
89 | $obj = new DummyEntity();
90 |
91 | $propertyMapping = $this->getMappingWithObject($obj);
92 | $propertyMapping->expects($this->once())
93 | ->method('getFileDataPropertyName')
94 | ->will($this->returnValue('file'));
95 |
96 | $this->assertSame($this->namer->propertyPrefixRename($propertyMapping, 'some-name.jpg',
97 | array('use_field_name' => true, 'delimiter' => '_')), 'file_some-name.jpg');
98 | }
99 |
100 |
101 | public function testPropertyPostfixRename()
102 | {
103 | $obj = new DummyEntity();
104 | $obj->setTitle('some-title');
105 |
106 | $propertyMapping = $this->getMappingWithObject($obj);
107 |
108 | $this->assertSame($this->namer->propertyPostfixRename($propertyMapping, 'some-name.jpg',
109 | array('field' => 'title')), 'some-name-some-title.jpg');
110 | }
111 |
112 |
113 | public function testReplaceRename()
114 | {
115 | $obj = new DummyEntity();
116 | $propertyMapping = $this->getMappingWithObject($obj);
117 |
118 |
119 | $this->assertSame($this->namer->replaceRename($propertyMapping, 'some-name.jpg',
120 | array('name' => 'staff')), 'some-staff.jpg');
121 | }
122 |
123 |
124 | public function testResolveCollision()
125 | {
126 | $this->assertSame($this->namer->resolveCollision('some-name.jpg', 5), 'some-name_5.jpg');
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Tests/Naming/NamerServiceInvokerTest.php:
--------------------------------------------------------------------------------
1 | container = Mocks::getContainerMock($this);
23 | $this->invoker = new NamerServiceInvoker ($this->container);
24 | }
25 |
26 |
27 | public function testRename()
28 | {
29 | $propertyMapping = Mocks::getPropertyMappingMock($this);
30 |
31 | $namer = $this->getMockBuilder('Iphp\FileStoreBundle\Naming\DefaultNamer')
32 | ->disableOriginalConstructor()
33 | ->getMock();
34 |
35 |
36 | $this->container
37 | ->expects($this->once())
38 | ->method('get')
39 | ->with('test.service')
40 | ->will($this->returnValue($namer));
41 |
42 |
43 | $namer->expects($this->once())
44 | ->method('translitRename')
45 | ->with($propertyMapping, 'sample file name', array('param' => 1))
46 | ->will($this->returnValue('sample-file-name'));
47 |
48 | $this->assertSame(
49 | $this->invoker->rename('test.service', 'translit', $propertyMapping, 'sample file name', array('param' => 1)),
50 | 'sample-file-name'
51 | );
52 |
53 |
54 | }
55 |
56 |
57 | public function testResolveCollision()
58 | {
59 | $namer = $this->getMockBuilder('Iphp\FileStoreBundle\Naming\DefaultNamer')
60 | ->disableOriginalConstructor()
61 | ->getMock();
62 |
63 |
64 | $this->container
65 | ->expects($this->once())
66 | ->method('get')
67 | ->with('test.service')
68 | ->will($this->returnValue($namer));
69 |
70 |
71 | $namer->expects($this->once())
72 | ->method('resolveCollision')
73 | ->with('sample-file-name', 1)
74 | ->will($this->returnValue('sample-file-name_1'));
75 |
76 |
77 | $this->assertSame($this->invoker->resolveCollision('test.service', 'sample-file-name', 1),
78 | 'sample-file-name_1');
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Tests/TwoFieldsDummyEntity.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class TwoFieldsDummyEntity
13 | {
14 | /**
15 | * @FileStore\UploadableField(mapping="dummy_file")
16 | */
17 | protected $file;
18 |
19 | /**
20 | * @FileStore\UploadableField(mapping="dummy_image")
21 | */
22 | protected $image;
23 |
24 | public function getFile()
25 | {
26 | $this->file;
27 | }
28 |
29 | public function setFile($file)
30 | {
31 | $this->file = $file;
32 | }
33 |
34 | public function getImage()
35 | {
36 | $this->image;
37 | }
38 |
39 | public function setImage($image)
40 | {
41 | $this->image = $image;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ./Tests
8 |
9 |
10 |
11 |
12 |
13 | ./
14 |
15 | ./Resources
16 | ./Tests
17 | ./vendor
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------