So I have a Document Entity that stores file uploads (currently only Images) that is used all over the project which is a blog. Now for the article I want to be able to upload and select an Image that basically has little to no restriction apart from file size, but for a category I want to only be able to use Images that are square or not landscape and not portrait.
The Document Entity looks like this
class Document
{
/**
* #var integer
*/
private $id;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $path;
private $webPath;
private $filename;
/**
* Set name
*
* #param string $name
* #return Document
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set path
*
* #param string $path
* #return Document
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
public function setFullFilename()
{
$this->filename = $this->id . '.' . $this->path;
}
public function getFilename()
{
return $this->filename;
}
public function getAbsolutePath()
{
//return null === $this->path ? null : $this->getUploadRootDir(). '/' . $this->path;
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path;
}
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir() . '/';
}
public function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function getUploadDir()
{
return 'bundles/pgblog/images/uploads';
}
private $file;
private $temp;
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
if(is_file($this->getAbsolutePath()))
{
$this->temp = $this->getAbsolutePath();
}
else {
$this->path = 'initial';
}
}
public function getFile()
{
return $this->file;
}
public function preUpload()
{
if(null !== $this->getFile())
{
$this->path = $this->getFile()->guessExtension();
$this->setMimetype();
$this->setSize();
/*
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename . '.' . $this->getFile()->guessExtension();
*/
}
}
public function upload()
{
if(null === $this->getFile())
{
return;
}
if(isset($this->temp))
{
unlink($this->temp);
$this->temp = null;
}
$this->getFile()->move($this->getUploadRootDir(), $this->id . '.' . $this->getFile()->guessExtension());
$this->file = null;
}
public function storeFilenameForRemove()
{
$this->temp = $this->getAbsolutePath();
}
public function removeUpload()
{
if(isset($this->temp))
{
unlink($this->temp);
}
}
public function __toString()
{
return $this->name;
}
/**
* #var string
*/
private $mimetype;
/**
* Set mimetype
*
* #param string $mimetype
* #return Document
*/
public function setMimetype()
{
$this->mimetype = $this->getFile()->getMimeType();
return $this;
}
/**
* Get mimetype
*
* #return string
*/
public function getMimetype()
{
return $this->mimetype;
}
/**
* #var integer
*/
private $size;
/**
* Set size
*
* #param integer $size
* #return Document
*/
public function setSize()
{
$this->size = $this->getFile()->getSize();
return $this;
}
/**
* Get size
*
* #return integer
*/
public function getSize()
{
return $this->size;
}
}
I use seperate forms for file upload and article/category creation. When creating an article or category, a file can be chosen from a list of currently all Files
Here the form type for Article
class ArticleType extends AbstractType
{
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('text')
->add('tags')
->add('category')
->add('image')
;
}
...
}
and the article entity
class Article
{
...
/**
* Set image
*
* #param \Acme\UtilBundle\Entity\Document $image
* #return Article
*/
public function setImage(\PG\BlogBundle\Entity\Document $image = null)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return \Acme\BlogBundle\Entity\Document
*/
public function getImage()
{
return $this->image;
}
}
Article is related to Document via a unidirectional many to one relationship
manyToOne:
image:
targetEntity: Document
joinColumn:
name: document_id
referencedColumnName: id
So in the form the files can be set by a select. For the article this is just fine, since I want to be able to use any image as an attachment, but for the categories I want only the files that are square as I said earlier.
I thought about using validation for the category image, but since the Image is selected by a select, the actual data is just a string (the file name given on the upload form) and not the image it self so the validation returns the error
Catchable Fatal Error: Argument 1 passed to Acme\BlogBundle\Entity\Category::setImage() must be an instance of Acme\BlogBundle\Entity\Document, string given...
So my question is, how do I restrict the Image options in the category form to only square images and how do I validate this properly?
The reason I only want to use square Images for category is so I can display a nice symmetric list of all the cateogries by the way.
Thanks in advance!
Store the image dimensions in your document entity (length, width).
That way when fetching the document collection for category forms you can filter image documents where length == width, so only display appropriate documents.
For validation, you have a number of options, the best place to start would be here. In your place I'd look into validation groups.
Related
Based on: http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html
I made an Images Model:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Filesystem\Filesystem;
/**
* #ORM\Entity
* #ORM\Table(name="images")
* #ORM\Entity(repositoryClass="AppBundle\Entity\ImagesRepository")
* #ORM\HasLifecycleCallbacks
*/
class Images
{
/**
* #ORM\Column(type="string", length=60)
* #ORM\Id
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $id;
/**
* Filename of the Image
* #ORM\Column(type="string", length=100)
*/
private $name;
/**
* Filename of the Thumbnail
* #ORM\Column(type="string", length=100)
*/
private $name_small;
/**
* ImageGroup og the Image
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ImageGroups", inversedBy="images")
*/
private $group;
/**
* #Assert\File(maxSize="20000000")
*/
private $file;
private $upload_dir='images';
private $temp;
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file,$upload_dir)
{
$this->file = $file;
// check if we have an old image path
if (isset($this->name))
{
// store the old name to delete after the update
$this->temp = $this->name;
$this->name = null;
}
else
{
$this->name = sha1(uniqid(mt_rand(), true)).'.'.$file->guessExtension();
}
$this->name_small="small_".$this->name;
$this->upload_dir=$upload_dir;
return $this;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->getFile())
{
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->name = $filename.'.'.$this->getFile()->guessExtension();
$this->name_small='small_'.$this->name;
}
}
public function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->upload_dir;
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->getFile())
{
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
//-- Make the Thumbnail Here --
$dir=$this->getUploadRootDir();
echo $dir;
$fs = new Filesystem();
if(!$fs->exists($dir))
{
echo "\nCreating\n";
$fs->mkdir($dir,0777,true);
}
$this->getFile()->move($dir, $this->name);
$file=$dir.'/'.$this->name;
// check if we have an old image
if (isset($this->temp))
{
// delete the old image
unlink($this->getUploadRootDir().'/'.$this->temp);
// clear the temp image path
$this->temp = null;
}
$this->file = null;
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
$file = $this->getAbsolutePath();
if ($file)
{
unlink($file);
}
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Get nameSmall
*
* #return string
*/
public function getNameSmall()
{
return $this->name_small;
}
/**
* Set group
*
* #param \AppBundle\Entity\ImageGroups $group
*
* #return Images
*/
public function setGroup(\AppBundle\Entity\ImageGroups $group = null)
{
$this->group = $group;
return $this;
}
/**
* Get group
*
* #return \AppBundle\Entity\ImageGroups
*/
public function getGroup()
{
return $this->group;
}
/**
* Set name
*
* #param string $name
*
* #return Images
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set nameSmall
*
* #param string $nameSmall
*
* #return Images
*/
public function setNameSmall($nameSmall)
{
$this->name_small = $nameSmall;
return $this;
}
}
I also made a Custom Repository In order to do the Uploads:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use AppBundle\Entity\Images;
class ImagesRepository extends EntityRepository
{
public function add($file,$group_id,$user_id)
{
if(empty($group_id)) return -1;
if(empty($user_id)) return -2;
/*Do stuff for uploads*/
/*End of Do stuff for uploads*/
$em = $this->getEntityManager();
$imagesGroups = $em->getRepository('AppBundle:ImageGroups')
->getUserImageGroups($user_id,null,$group_id);
if(empty($imagesGroups) ||(is_int($imagesGroups) && $imagesGroups<0)) return -3; //Null and negative values are false
if(empty($file)) return -4;
$image=new Images();
$image->setFile($file,'images')->setGroup($imagesGroups);
try
{
$em->persist($image);
$em->flush();
return ['id'=>$image->getId(),'image'=>$image->getName(),'thumb'=>$image->getNameSmall()];
}
catch (\Exception $e)
{
echo $e->getMessage();
return false;
}
}
public function delete($user_id,$image_id)
{
if(empty($image_id)) return -1;
if(empty($user_id)) return -2;
$em = $this->getEntityManager();
try
{
$q=$em->createQueryBuilder('i')
->from('AppBundle:Images','i')
->innerJoin('i.group', 'g')
->innerJoin('AppBundle:Users','u')
->select('i')
->where('i.id=:iid')
->andWhere('u.id=:uid')
->setParameter(':uid', $user_id)
->setParameter(':iid', $image_id)
->setMaxResults(1)
->getQuery();
$data=$q->getOneOrNullResult();
if(empty($data)) return -3;
/*Delete Image Stuff*/
/*End Of: Delete Image Stuff*/
$em->remove($data);
$em->flush();
return true;
}
catch (\Exception $e)
{
echo $e->getMessage();
return false;
}
}
}
When I sucessfully do the POST action (I Use curl to test the code above) for some reason I get the following Error:
The file "IMG_20160305_155302.jpg" was not uploaded due to an unknown error.
By echoing the Exception message.
I Originally thought that is a permissions Issues on my filesystem therefore on the Folder /home/pcmagas/Kwdikas/php/apps/symphotest/src/AppBundle/Entity/../../../../web/images
I set the following permissions:
drwxrwxrwx 2 www-data www-data 4096 Μάρ 5 23:02 images
But it does not seem that is the problem. And I wonder what else could it be.
May I have a solution?
Edit 1:
I checked the upload_max_filesize on php.ini and is on 2M and the file that I am Uploading is on 56,0 Kbytes.
The UploadedFile Instance somehow was not setup corectly in controller.
In order to get the file Input I made this class:
<?php
namespace AppBundle\Helpers;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class MyUploadedFile
{
/**
*Storing Uploaded Files
*/
private $files=null;
function __construct($string)
{
if(isset($_FILES[$string]))
{
$files=$_FILES[$string];
if(is_array($files['name']))
{
$this->files=array();
$tmp_files=array();
/**
*Sanitizing When Multiple Files
*/
foreach($files as $key=>$val)
{
foreach($val as $key2=>$val2)
{
$tmp_files[$key2][$key]=$val2;
}
}
foreach($tmp_files as $val)
{
$this->files[]=new UploadedFile($val['tmp_name'],$val['name'],$val['type'],$val['size'],$val['error']);
}
}
elseif(is_string($files['name']))
{
$this->files= new UploadedFile($files['tmp_name'],$files['name'],$files['type'],$files['size'],$files['error']);
}
}
}
/**
*#return {UploadedFile} Or {Array of UploadedFile} or null
*/
public function getFiles()
{
return $this->files;
}
}
?>
Especially in these lines
$this->files= new UploadedFile($files['tmp_name'],$files['name'],$files['type'],$files['size'],$files['error']);
$this->files[]=new UploadedFile($val['tmp_name'],$val['name'],$val['type'],$val['size'],$val['error']);
So when ytou manually make an uploaded file the size comes before the error
I have one application with a class user and document, every user have a picture profile (document) it works fine.
But now I'm having problems, for example on the header of the page I show the picture:
{% if app.user.picture %}
<img src="{{ asset('uploads/' ~ app.user.picture.Id) }}" alt="" class="img-circle">
{% else %}
<img src="{{ asset('images/default.png') }}" alt="" class="img-circle">
{% endif %}
and I got the html code:
<img src="/Project/web/uploads/30" alt="" class="img-circle">
this code works perfect on my localhost, but now I uploaded this to the server and the picture is not showing.
But if I add the extension on the browser It show me exactly what I want.
Here is my Document entity:
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Document {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
public $name;
/**
* #ORM\Column(type="datetime")
*/
protected $createdAt;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
private $temp;
public function __construct() {
$this->setCreatedAt(new \DateTime());
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($n) {
$this->name = $n;
}
public function getCreatedAt() {
return $this->createdAt;
}
public function setCreatedAt($d) {
$this->createdAt = $d;
}
public function setPath() {
$this->path = $this->id;
}
public function getPath() {
return $this->path;
}
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null) {
$this->file = $file;
// check if we have an old image path
if (isset($this->path)) {
// store the old name to delete after the update
$this->temp = $this->path;
$this->path = null;
} else {
$this->path = 'initial';
}
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile() {
return $this->file;
}
public function getAbsolutePath() {
return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->id . '.' . $this->path;
}
public function getWebPath() {
return null === $this->path ? null : $this->getUploadDir() . '/' . $this->path;
}
protected function getUploadRootDir() {
// the absolute directory path where uploaded
// documents should be saved
return __DIR__ . '/../../../../../web/' . $this->getUploadDir();
}
protected function getUploadDir() {
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload() {
if (null !== $this->getFile()) {
$this->path = $this->getFile()->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload() {
if (null === $this->getFile()) {
return;
}
// check if we have an old image
if (isset($this->temp)) {
// delete the old image
unlink($this->temp);
// clear the temp image path
$this->temp = null;
}
// you must throw an exception here if the file cannot be moved
// so that the entity is not persisted to the database
// which the UploadedFile move() method does
$this->getFile()->move(
$this->getUploadRootDir(), $this->id . '.' . $this->getFile()->guessExtension()
);
$this->setFile(null);
}
/**
* #ORM\PreRemove()
*/
public function storeFilenameForRemove() {
$this->temp = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload() {
if (isset($this->temp)) {
unlink($this->temp);
}
}
}
And my Document.orm.yml:
...\Entity\Document:
type: entity
table: null
repositoryClass: ...\Entity\DocumentRepository
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
name:
type: string
length: '255'
path:
type: string
length: '255'
nullable: true
file:
type: string
length: '255'
lifecycleCallbacks: { }
In my account controller:
public function editAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('...Bundle:User')->find($this->getUser()->getId());
$originalpic = $entity->getPicture();
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
$uploadedFile = $request->files->get('form_user');
if ($editForm->isValid()) {
if ($editForm->get('password')->getData()) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($entity);
$entity->setPassword($encoder->encodePassword($editForm->get('password')->getData(), $entity->getSalt()));
}
if ($uploadedFile['picture']) {
foreach ($uploadedFile as $file) {
$pic = new Document();
$pic->setName($entity->getUsername());
$pic->setFile($file);
$pic->setPath();
$entity->setPicture($pic);
$em->persist($pic);
$em->flush();
$pic->upload();
}
} else {
$entity->setPicture($originalpic);
}
$em->flush();
$this->container->get("session")->getFlashBag()->add('success', 'Information updated');
return $this->redirect($this->generateUrl('panel_profile'));
}
return $this->render('...Bundle:Panel\Pages\Account:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView()
));
}
My question is, how can I change my code to show me the picture with the extension
/////
The solution was add another raw to the table and in Document Entity:
public function upload() {
if (null === $this->getFile()) {
return;
}
// check if we have an old image
if (isset($this->temp)) {
// delete the old image
unlink($this->temp);
// clear the temp image path
$this->temp = null;
}
// you must throw an exception here if the file cannot be moved
// so that the entity is not persisted to the database
// which the UploadedFile move() method does
$this->extension = $this->getFile()->guessExtension();
$this->getFile()->move(
$this->getUploadRootDir(), $this->id . '.' . $this->getFile()->guessExtension()
);
$this->setFile(null);
}
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $extension;
//get image extension
public function getImgExtension(){
return $this->getFile()->getClientOriginalExtension();
}
/**
* Set path
*
* #param string $extension
* #return Document
*/
public function setExtension($extension)
{
$this->extension = $extension;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getExtension()
{
return $this->extension;
}
to get image extension, add this property and method in your Document Entity.
Then run this command from your terminal:
php app/console doctrine:schema:update --force
and in your twig file just include:
<img src="{{asset(document.WebPath)}}.{{document.extension}}" alt="" class="img-circle">
I'm creating a ticket system for practice purposes.
Right now I'm having a problem with uploading files.
The idea is that a ticket can have multiple attachments, so I created a many-to-one relationship between the ticket and the upload tables.
class Ticket {
// snip
/**
* #ORM\OneToMany(targetEntity="Ticket", mappedBy="ticket")
*/
protected $uploads;
// snip
}
The upload entity class contains the upload functionality, which I took from this tutorial:
<?php
namespace Sytzeandreae\TicketsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity(repositoryClass="Sytzeandreae\TicketsBundle\Repository\UploadRepository")
* #ORM\Table(name="upload")
* #ORM\HasLifecycleCallbacks
*/
class Upload
{
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
private $temp;
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Ticket", inversedBy="upload")
* #ORM\JoinColumn(name="ticket_id", referencedColumnName="id")
*/
protected $ticket;
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* #ORM\Column(type="string")
*/
protected $src;
public function getAbsolutePath()
{
return null === $this->src
? null
: $this->getUploadRootDir().'/'.$this->src;
}
public function getWebPath()
{
return null === $this->src
? null
: $this->getUploadDir().'/'.$this->src;
}
public function getUploadRootDir()
{
// The absolute directory path where uplaoded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function getUploadDir()
{
// Get rid of the __DIR__ so it doesn/t screw up when displaying uploaded doc/img in the view
return 'uploads/documents';
}
/**
* Sets file
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
if (isset($this->path)) {
// store the old name to delete after the update
$this->temp = $this->path;
$this->path = null;
} else {
$this->path = 'initial';
}
}
/**
* Get file
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set src
*
* #param string $src
*/
public function setSrc($src)
{
$this->src = $src;
}
/**
* Get src
*
* #return string
*/
public function getSrc()
{
return $this->src;
}
/**
* Set ticket
*
* #param Sytzeandreae\TicketsBundle\Entity\Ticket $ticket
*/
public function setTicket(\Sytzeandreae\TicketsBundle\Entity\Ticket $ticket)
{
$this->ticket = $ticket;
}
/**
* Get ticket
*
* #return Sytzeandreae\TicketsBundle\Entity\Ticket
*/
public function getTicket()
{
return $this->ticket;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->getFile()) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->src = $filename.'.'.$this->getFile()->guessExtension();
}
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// move takes the target directory and then the target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->src
);
// check if we have an old image
if (isset($this->temp)) {
// delete the old image
unlink($this->getUploadRootDir().'/'.$this->temp);
// clear the temp image path
$this->temp = null;
}
// clean up the file property as you won't need it anymore
$this->file = null;
}
/**
* #ORM\PostRemove
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
}
The form is build as follows:
class TicketType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('title')
->add('description')
->add('priority')
->add('uploads', new UploadType())
}
Where UploadType looks like this:
class UploadType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options) {
$builder->add('file', 'file', array(
'label' => 'Attachments',
'required' => FALSE,
'attr' => array (
'accept' => 'image/*',
'multiple' => 'multiple'
)
));
}
This part seems to work fine, I do get presented a form which contains a file uploader.
Once I put this line in the Ticket entity's constuctor:
$this->uploads = new \Doctrine\Common\Collections\ArrayCollection();
It throws me the following error:
Neither property "file" nor method "getFile()" nor method "isFile()"
exists in class "Doctrine\Common\Collections\ArrayCollection"
If I leave this line out, and upload a file, it throws me the following error:
"Sytzeandreae\TicketsBundle\Entity\Ticket". Maybe you should create
the method "setUploads()"?
So next thing I did was creating this method, try an upload again and now it throws me:
Class Symfony\Component\HttpFoundation\File\UploadedFile is not a
valid entity or mapped super class.
This is where I am really stuck. I fail to see what, at what stage, I did wrong and am hoping for some help :)
Thanks!
You can either add a collection field-type of UploadType() to your form. Notice that you can't use the multiple option with your current Upload entity ... but it's the quickest solution.
Or adapt your Ticket entity to be able to handle multiple files in an ArrayCollection and looping over them.
I have followed Multiple file upload with Symfony2 and created a entity
/*
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="Repair\StoreBundle\Entity\attachmentsRepository")
*/
class attachments
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #Assert\File(maxSize="6000000")
* #ORM\Column(name="files", type="array", length=255, nullable=true)
*/
private $files=array();
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set files
* #param object $files
*
* #return attachments
*/
public function setFiles($files) {
$this->files = $files;
}
/**
* Get files
*
* #return object
*/
public function getFiles() {
return $this->files;
}
public function __construct() {
$files = array();
}
public function uploadFiles() {
// the files property can be empty if the field is not required
if (null === $this->files) {
return;
}
if (!$this->id) {
$this->files->move($this->getTmpUploadRootDir(), $this->files->getClientOriginalName());
} else {
$this->files->move($this->getUploadRootDir(), $this->files->getClientOriginalName());
}
$this->setFiles($this->files->getClientOriginalName());
}
public function getAbsolutePath() {
return null === $this->path
? null
: $this->getUploadRootDir() . DIRECTORY_SEPARATOR . $this->path;
}
public function getWebPath() {
return null === $this->path
? null
: $this->getUploadDir() . DIRECTORY_SEPARATOR . $this->path;
}
protected function getUploadRootDir() {
return __DIR__ . '/../../../../web/'. $this->getUploadDir();
}
protected function getUploadDir() {
return 'uploads/';
}
}
I have a controller which has the folowing code
class uploadController extends Controller
{
public function uploadAction(Request $request) {
$id= $_GET['id'];
$user = new attachments();
$form = $this->createFormBuilder($user)->add('files','file',array("attr"=>array("multiple" =>"multiple",)))->getForm();
$formView = $form->createView();
$formView->getChild('files')->set('full_name','form[file][]');
if ($request->getMethod() == 'POST') {
$em = $this->getDoctrine()->getManager();
$form->bind($request);
$files = $form["files"]->getFilenames();
$user->uploadFiles(); //--here iam not able to get te file names in order to send to the upload function.upload files is returning null values
}
}
}
the controller is not able to get the filenames that is uploded by the uder from the view.it is returning null values when i send to the upload function in entity
I think you should read the Documentation about File Upload again. You are correctly using the annotation #ORM\HasLifecycleCallbacks but your code is missing the right annotations for the methods. You can use the #ORM\PostPersist() and #ORM\PostUpdate() annotations to call the upload() function automatically after the request was validated and the object was persisted. Also I would suggest using an entity for each file and creating a OneToMany relation. This will make it more easy and logical to store your files correctly.
In my Restful API, i want to upload a file in one call.
In my tests, the form is initialized and binded at the same time, but all my data fields form are empty and the result is an empty record in my database.
If I pass by the form view and then submit it, all is fine but i want to call the Webservice in one call. The webservice is destinated to be consumed by a backbone app.
Thanks for your help.
My Test:
$client = static::createClient();
$photo = new UploadedFile(
'/Userdirectory/test.jpg',
'photo.jpg',
'image/jpeg',
14415
);
$crawler = $client->request('POST', '/ws/upload/mydirectory', array(), array('form[file]' => $photo), array('Content-Type'=>'multipart/formdata'));
There is my controller action:
public function uploadAction(Request $request, $directory, $_format)
{
$document = new Media();
$document->setDirectory($directory);
$form = $this->createFormBuilder($document, array('csrf_protection' => false))
/*->add('directory', 'hidden', array(
'data' => $directory
))*/
->add('file')
->getForm()
;
if ($this->getRequest()->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
if($document->getId() !== '')
return $this->redirect($this->generateUrl('media_show', array('id'=>$document->getId(), 'format'=>$_format)));
}else{
$response = new Response(serialize($form->getErrors()), 406);
return $response;
}
}
return array('form' => $form->createView());
}
My Media Entity:
<?php
namespace MyRestBundle\RestBundle\Entity;
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Media
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $path;
public $directory;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* #see \Symfony\Component\Serializer\Normalizer\NormalizableInterface
*/
function normalize(NormalizerInterface $normalizer, $format= null)
{
return array(
'path' => $this->getPath()
);
}
/**
* #see
*/
function denormalize(NormalizerInterface $normalizer, $data, $format = null)
{
if (isset($data['path']))
{
$this->setPath($data['path']);
}
}
protected function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
protected function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/'.(null === $this->directory ? 'documents' : $this->directory);
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$this->path = $this->getUploadDir().'/'.sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
/**
* Set Directory
*
* #param string $directory
* #return Media
*/
public function setDirectory($directory)
{
$this->directory = $directory;
return $this;
}
/**
* Set Path
*
* #param string $path
* #return Media
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
$request = Request::createFromGlobals();
return $request->getHost().'/'.$this->path;
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
}
My routing:
upload_dir_media:
pattern: /upload/{directory}.{_format}
defaults: { _controller: MyRestBundle:Media:upload, _format: html }
requirements: { _method: POST }
Try breaking this problem down into a simple state. How would you post 'text' or a variable to a web service with one post? Because an image is just a long string. Check out the php function imagecreatefromstring or imgtostring . This is often what goes on behind the scenes of you image transfer protocols.. Once you so solve the simpler problem, you will have proven you can solve your original problem.