Skip to content

Form file uploads #400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 14, 2011
Merged

Form file uploads #400

merged 4 commits into from
Jun 14, 2011

Conversation

fabpot
Copy link
Member

@fabpot fabpot commented Jun 13, 2011

I've updated the file type reference section to reflect the latest changes of the code and added a new entry in the cookbook to explain how to manage file uploads with Doctrine entities. I'm not really satisfied with the text as it makes you think that this is really complex but in fact, it's not. Hopefully, the magical @weaverryan will fix that.

// 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->file->move($this->getUploadRootDir(), $this->generatePath($this->file));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have an issue here: you generate the path twice so the file will not use the same path than the persisted one. You should use $this->path here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, you also use only the file name instead of the full path.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hehe, I've changed the code just before committing it... without testing it. My bad.

@stof
Copy link
Member

stof commented Jun 13, 2011

The move() method should use the full path

@fabpot
Copy link
Member Author

fabpot commented Jun 13, 2011

The move() methods takes 2 arguments: the directory and the file name.

First, let's create a simple Doctrine Entity to work with::

use Doctrine\ORM\Mapping as ORM;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you need to add
use Symfony\Component\Validator\Constraints as Assert;
since there's an @Assert in used in line 26

@weaverryan
Copy link
Member

If anyone spots anything else on this, please let me know - I'm merging this now so that it'll be included in the next rendering.

weaverryan added a commit that referenced this pull request Jun 14, 2011
@weaverryan weaverryan merged commit 381c68e into master Jun 14, 2011

protected function getUploadRootDir()
{
return '/path/to/uploaded/documents';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a hack - how could we have a hard-coded path included like this? I've been doing something similar to this, but have injected this information (which is quite unfortunate).

Overall. I think this highlights the issue with trying to handle all of this from within an entity. It seems like it might be more appropriate to hand all of this off to some other service - which could handle the unique filename stuff, the moving of the files, setting of the path. In theory - though I don't see exactly how - that service could listen on Doctrine and respond to entities that implement a certain interface (with methods that give information on how to get file information).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we've been dealing with this is by using a service to handle it, as you say. It means however, that we've set our Entity to explicit flush type so that EM flushes wont touch the entity unless our service wants them to.

No doctrine listener, just a coding standard that everything must use its manager service instead of raw doctrine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to draw the line somewhere. This cookbook entry is just a starting point for you own implementation. I've tried to find a good balance between correctness and simplicity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the cookbook entry is very good - it builds the details nicely.

I just think that the cookbook entry highlights a missing feature - be it in a bundle or in core - which removes the need for the boilerplate code and replaces it with some simple configuration (choose target directory, choose filename generation method, specify "file" field, specify "path" field...).

It's not really a concern for the framework, but from a practical standpoint, there's also the issue of managing exactly which files you have where (e.g. Product photo is in /uploads/products, Blog photos in /uploads/blog) and making that information available while handling file uploads and in the view, where you're creating paths to the files.

So, I'm just thinking out loud :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weaverryan Making it configurable will require using the lifecycle event instead of the lifecycle callback. Once the logic is moved to a listener, the service can also be used in the view.
And for the use of different directories, just add some logic in the getUploadRootDir method, based on another property oof the entity.

@tristanbes
Copy link
Contributor

Guys, I may be wrong but, why you don't attach 2 listeners on PreUpload() and Upload()
Right now we only have : @ORM\PrePersist() which is called only when this is a new entry, but what about update ?

I would add @ORM\PreUpdate() on both in case of user edit the file on an already existing object.

Some people forget to remove the call to $document->upload() in the controller, should be mentioned

+1

It's really sad that we can't can't find a way to use %kernel.root_dir% in the model.

@stof
Copy link
Member

stof commented Jul 18, 2011

@tristanbes which events are PreUpload and Upload ?

@tristanbes
Copy link
Contributor

@stof they are functions on which only @ORM\PrePersist() is attached.
So if we update the entity, those functions (to upload the document) are not called and the document is not updated & uploaded.

This reflexions leads to what @poussain told : if only the virtual field is changed, none of the LifeCycle Callbacks are called

UPDATE: after testing, i'm not able to reproduce the virtual field thing with lifeCycle Callbacks that poussain reported. I changed only the avatar image among untouched mapped fields and it called the PreUpdate() event.

@sicarrots
Copy link

I know that it is old discussion, but i`m writing here for confirmation before opening new issue.
Using live cycle callbacks for managing upload does not work with field unamanaged by orm/orm(i've tested only odm).
I've found two possible solutions:

  1. not using life cycle callbacks, do upload by explicit call to upload() in controller
  2. implementing custom change tracking policy as mentioned here: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/change-tracking-policies.html#notify

Can anybody confirm?

fabpot added a commit to symfony/form that referenced this pull request Nov 25, 2013
fabpot added a commit to symfony/framework-bundle that referenced this pull request Nov 11, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants