I have ActivityFile Entity which should handle files:
class ActivityFile {
// all properties / setters / getters and so on
public function upload()
{
foreach($this->uploadedFiles as $uploadedFile) {
$fileName = md5(uniqid()) . '.' . $uploadedFile->getClientOriginalName();
$uploadedFile->move(
$this->getUploadRootDir(),
$fileName
);
$this->path = $fileName;
$this->name = $uploadedFile->getClientOriginalName();
$this->setRealPath($this->getUploadDir() . '/' . $fileName);
$this->file = null;
}
}
That works fine. I'll get all uploaded files in the desired folder.
Problem is, I don't get the data in Database. Because of my Controller:
class DashboardController extends Controller
{
public function indexAction(Request $request)
{
$activityFile = new ActivityFile();
$activityFile->setUser($this->getUser());
$form = $this->createFormBuilder($activityFile)
->add('uploadedFiles', FileType::class, array(
'multiple' => true,
'data_class' => null,
))
->add('save', SubmitType::class, array('label' => 'Upload'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
// here is PROBLEM
// $activityFile only contains the last file
// from selected upload files
$activityFile->upload();
$em->persist($activityFile);
$em->flush();
return $this->redirect($this->generateUrl('dashboard'));
}
return $this->render('ACMEBundle:Dashboard:index.html.twig', array(
'form' => $form->createView(), 'activityFile' => $activityFile
));
}
}
How can I do the Database Entry for each uploaded file?
You're moving all uploaded files to destination directory but the result of this operation is being stored in a property that is not an array but string. You just simply override it every time.
$this->path = $fileName;
Change the structure of your ActivityFile to store list of files not only one.
Otherwise create multiple ActivityFiles for every uploaded file using collection form type http://symfony.com/doc/current/reference/forms/types/collection.html
Related
I'm using Yii2 basic. It doesn't seems like anything's wrong, no error message displayed, but why did my image didn't upload? The rest (title, content etc) get uploaded through the form,though
This is my model's rule and related method:
public $image;
public function init(){
Yii::$app->params['uploadPath'] = Yii::$app->basePath . '/uploads/batam/';
Yii::$app->params['uploadUrl'] = Yii::$app->urlManager->baseUrl . '/uploads/batam/';
}
public function rules()
{
return [
[['title', 'content'], 'required'],
[['content'], 'string'],
[['created_at', 'updated_at','image'], 'safe'],
[['image'], 'file','extensions'=>'jpg,png,jpeg'],
[['title'], 'string', 'max' => 255],
];
}
public function getImageFile()
{
return isset($this->image) ? Yii::$app->params['uploadPath'].$this->image : null;
}
public function uploadImage() {
$image = UploadedFile::getInstance($this, 'image');
if (empty($image)) {
return false;
}
$this->image = $image->name;
return $image;
}
This is my controller
public function actionCreate()
{
$model = new News();
if ($model->load(Yii::$app->request->post()) )
{
// process uploaded image file instance
$image = $model->uploadImage();
if($model->validate())
{
if($model->save())
{
// upload only if valid uploaded file instance found
if ($image !== false)
{
$path = $model->getImageFile();
$image->saveAs($path);
}
return $this->redirect(['view', 'id'=>$model->id]);
}
}
else{echo " validation is failed";}
}
else{
return $this->render('create', [
'model' => $model,
]);
}
}
This is the form
echo $form->field($model, 'image')->widget(FileInput::classname(), [
'options' => ['accept' => 'image/*'],
'pluginOptions' => [['previewFileType' => 'any']]
]);
I had included the enctype also at the beginning of the form
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]);
At this point inside the if ($image !== false) part of the controller , the $image and $path to be saved-as contains a seemingly correct path.
This is my $path : C:\xampp\htdocs\gbia/uploads/batam/test image 1-01.jpg and my $image also contain the object (not null). This is the var_dump of my $image :
object(yii\web\UploadedFile)#179 (5) { ["name"]=> string(19) "test image 1-01.jpg" ["tempName"]=> string(24) "C:\xampp\tmp\php3199.tmp" ["type"]=> string(10) "image/jpeg" ["size"]=> int(925184) ["error"]=> int(0) }
I think something wrong with the saveAs(), but I can't figure it out. Had googled around, look on stackoverflow and tutorials but I still can't find any answer. Can someone help? Thanks
Check your model, you have declared $image as a public variable of the class, and not as a field in the database, if you want to store the data there, it will never work, as the public property that is temporary will have preference over the database column.
public $image;
So delete this field (If it is also in the db) or generate a new column name (I suggest by the name of path).
[['content', 'path'], 'string'],
Then you need to store the path, I don't see where are you doing that in the controller or class. I suggest you to add a field in the database with the "path" name and then do like this in the controller:
$path = $model->getImageFile();
$image->saveAs($path);
$model->path = $path . $image // You must store the full path plus the file name
$model->save(); // then you save the model again
Any doubt is welcome, I have example projects that I can show you if you are unable to see the light.
I've been following a Symfony tutorial on Udemy, a simple CMS which I'm now trying to expand.
I've added a file upload field to a form, the file is uploaded and the file name is stored in the database.
Adding new records works as does editing records if I select a new file add a new file on the edit form.
But if i try to edit without select a new file to upload, the original file name is removed from the db.
This is what I have so far in the controller
public function editAction(Request $request, Car $car)
{
$deleteForm = $this->createDeleteForm($car);
$editForm = $this->createForm('CarBundle\Form\CarType', $car);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$file = $editForm['brochure']->getData();
if(!empty($file)) {
// Generate a unique name for the file before saving it
$fileName = md5(uniqid()).'.'.$file->guessExtension();
// Move the file to the directory where brochures are stored
$file->move( $this->getParameter('brochures_directory'), $fileName );
$car->setBrochure($fileName);
} else {
$id = $car->getId();
$em = $this->getDoctrine()->getManager();
$car = $em->getRepository('CarBundle:Car')->find($id);
$fileName = $car->getBrochure();
$car->setBrochure($fileName);
}
$em = $this->getDoctrine()->getManager();
$em->merge($car);
$em->flush();
return $this->redirectToRoute('car_edit', array('id' => $car->getId()));
// return $this->redirectToRoute("car_index");
}
If the Symfony form builder I have this
->add('brochure', FileType::class,[
'label' => 'Image',
'data_class' => null,
'required' => false
])
I think the problem is coming from the form builder data_class which I had to add due to the error
The form's view data is expected to be an instance of class >Symfony\Component\HttpFoundation\File\File, but is a(n) string.
But I'm not sure how to fix it, any suggestion or help welcome!
ps. I've read that this should probably be a service, but baby steps first!
So I found a solutions, thanks for the suggestions to all who helped!
I'll post my solution for others to see, but please be aware I'm not a Symfony expert so I can't say if its correct for Symfony or even best practice!
public function editAction(Request $request, Car $car)
{
$deleteForm = $this->createDeleteForm($car);
$editForm = $this->createForm('CarBundle\Form\CarType', $car);
//get the current file name if there is one
$currentFile = $car->getBrochure();
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$file = $editForm['brochure']->getData();
if(!empty($file)) {
//if new file has been posted, use it to update DB
// Generate a unique name for the file before saving it
$fileName = md5(uniqid()).'.'.$file->guessExtension();
// Move the file to the directory where brochures are stored
$file->move(
$this->getParameter('brochures_directory'),
$fileName
);
$car->setBrochure($fileName);
} else {
//if no new file has been posted and there is a current file use that to update the DB
if (!empty($currentFile)) {
$car->setBrochure($currentFile);
}
}
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirectToRoute('car_edit', array('id' => $car->getId()));
// return $this->redirectToRoute("car_index");
}
return array(
'car' => $car,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
At the bottom of this page (ctrl+f for "When creating a form to edit an already persisted item ..") it says if you're editing an item, it's better to just create a new file with the old file's name.
public function editAction(Request $request, Car $car)
{
$editForm = $this->createForm('CarBundle\Form\CarType', $car);
$editForm->handleRequest($request);
$brochureDir = $this->getParameter('brochures_directory');
if (!empty($car->getBrochure()) {
$car->setBrochure(
new File($brochureDir . '/' . $car->getBrochure()
);
}
if ($editForm->isSubmitted() && $editForm->isValid()) {
$file = $car->getBrochure();
if (!empty($file)) {
// Generate a unique name for the file before saving it
$fileName = md5(uniqid()) . '.' . $file->guessExtension();
// Move the file to the directory where brochures are stored
$file->move($brochureDir, $fileName);
$car->setBrochure($fileName);
}
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirectToRoute('car_edit', array('id' => $car->getId()));
}
}
Now can you remove the "'data_class' => null" from your form and just let it either be a new File or null from an old entry.
I also cleaned up some other stuff in there --got rid of your two doctrine calls, got rid of that "merge" (never seen that, not even sure what it does. If this is an edit form the entity already exists in doctrine so just editing the entity and flushing should work.). I also got rid of that else statement because I couldn't see what it was doing besides setting a few variables. If this code doesn't work and you need that else statement (or anything else I've removed) put it back in there and work on it some more. This is not tested code.
Good luck.
While uploading multiple file getting this error:
When I put [['file'], 'file', 'maxFiles' => 4],in model getting following error:
Call to a member function saveAs() on null
But when I put this [['file'], 'file'], in model, its uploading.
Why am I getting error?
View:
<?php echo $form->field($model,'file[]')->label(false)->widget(FileInput::classname(),
[
'options'=>['accept'=>'image/*', 'multiple'=>true],
'pluginOptions'=>['allowedFileExtensions'=>['jpg','gif','png']
]]);
?>
Controller:
public function actionCreate()
{
$model = new RoomTypes();
if ($model->load(Yii::$app->request->post()))
{
$imageName = $model->room_type;
$model->file = UploadedFile::getInstance($model, 'file');
$model->file->saveAs( 'uploads/room_img/'.$imageName.'.'.$model->file->extension);
//save the path in the db column
$model->images = 'uploads/room_img/'.$imageName.'.'.$model->file->extension;
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
}
else
{
return $this->render('create', [
'model' => $model,
]);
}
}
Use getInstances instead of getInstance as according to their respective documentations, the first returns all uploaded files for a given model attribute while the second is designed to return a single one.
Then loop and save them one by one :
if ($model->load(Yii::$app->request->post())) {
$imageName = $model->room_type;
$model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');
$all_files_paths = [];
foreach ($model->imageFiles as $file_instance) {
// this should hold the new path to which your file will be saved
$path = 'uploads/room_img/' . $file_instance->baseName . '.' . $file_instance->extension;
// saveAs() method will simply copy the file
// from its temporary folder (C:\xampp\tmp\php29C.tmp)
// to the new one ($path) then will delete the Temp File
$file_instance->saveAs($path);
// here the file should already exist where specified within $path and
// deleted from C:\xampp\tmp\ just save $path content somewhere or in case you need $model to be
// saved first to have a valid Primary Key to maybe use it to assign
// related models then just hold the $path content in an array or equivalent :
$all_files_pathes []= $path;
}
$model->save();
/*
after $model is saved there should be a valid $model->id or $model->primaryKey
you can do here more stuffs like :
foreach($all_files_pathes as $path) {
$image = new Image();
$image->room_id = $model->id;
$image->path = $path;
$image->save();
}
*/
return $this->redirect(['view', 'id' => $model->id]);
}
See docs for more info.
In User.php (Entity name is User), I have a field in User entity named userPic , type String
In file UserType.php I mention userPic as shown below :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('userFullname')
->add('userName')
->add('userEmail')
->add('userPassword')
->add('userPic', 'file', array ('label'=>'profile Picture'))
->add('gender','choice',array('choices' => array('m' => 'Male', 'f' => 'Female')))
->add('isActive')
;
}
Now in the controller I'm getting the form fields as shown below
/**
* Creates a new User entity.
*
*/
public function createAction(Request $request)
{
$entity = new User();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('user_show', array('id' => $entity->getId())));
}
return $this->render('MWANRegisterBundle:User:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Where do I have to give the path in which I want to save the picture? How can I save the uploaded file in my desired directory and save directory path in database?
Christian's answer is valid, however I'd just like to point out more specificaly how to do what is asked. Simply do :
if ($form->isValid()) {
$file = $form->getData()['file'];
$file->move('/your/path/to/your/file', 'yourFileName');
// Do the rest
...
}
Hope this helps.
You need to create an upload method in your entity. Check this link for more details http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html
public function uploadFile()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move($this->getUploadDir(), $this->getFile()->getClientOriginalName());
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
/**
* Creates a new User entity.
*
*/
public function createAction(Request $request)
{
$entity = new User();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
// Upload file
$entity->uploadFile();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('user_show', array('id' => $entity->getId())));
}
return $this->render('MWANRegisterBundle:User:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
I want to upload images in my cakephp 3.0 app. But I get the error message:
Notice (8): Undefined index: Images [APP/Controller/ImagesController.php, line 55]
Are there already some examples for uploading files (multiple files at once) in cakePHP 3.0? Because I can only find examples for cakePHP 2.x !
I think I need to add a custom validation method in my ImagesTable.php? But I can't get it to work.
ImagesTable
public function initialize(array $config) {
$validator
->requirePresence('image_path', 'create')
->notEmpty('image_path')
->add('processImageUpload', 'custom', [
'rule' => 'processImageUpload'
])
}
public function processImageUpload($check = array()) {
if(!is_uploaded_file($check['image_path']['tmp_name'])){
return FALSE;
}
if (!move_uploaded_file($check['image_path']['tmp_name'], WWW_ROOT . 'img' . DS . 'images' . DS . $check['image_path']['name'])){
return FALSE;
}
$this->data[$this->alias]['image_path'] = 'images' . DS . $check['image_path']['name'];
return TRUE;
}
ImagesController
public function add()
{
$image = $this->Images->newEntity();
if ($this->request->is('post')) {
$image = $this->Images->patchEntity($image, $this->request->data);
$data = $this->request->data['Images'];
//var_dump($this->request->data);
if(!$data['image_path']['name']){
unset($data['image_path']);
}
// var_dump($this->request->data);
if ($this->Images->save($image)) {
$this->Flash->success('The image has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The image could not be saved. Please, try again.');
}
}
$images = $this->Images->Images->find('list', ['limit' => 200]);
$projects = $this->Images->Projects->find('list', ['limit' => 200]);
$this->set(compact('image', 'images', 'projects'));
$this->set('_serialize', ['image']);
}
Image add.ctp
<?php
echo $this->Form->input('image_path', [
'label' => 'Image',
'type' => 'file'
]
);
?>
Image Entity
protected $_accessible = [
'image_path' => true,
];
In your view file, add like this, in my case Users/dashboard.ctp
<div class="ChImg">
<?php
echo $this->Form->create($particularRecord, ['enctype' => 'multipart/form-data']);
echo $this->Form->input('upload', ['type' => 'file']);
echo $this->Form->button('Update Details', ['class' => 'btn btn-lg btn-success1 btn-block padding-t-b-15']);
echo $this->Form->end();
?>
</div>
In your controller add like this, In my case UsersController
if (!empty($this->request->data)) {
if (!empty($this->request->data['upload']['name'])) {
$file = $this->request->data['upload']; //put the data into a var for easy use
$ext = substr(strtolower(strrchr($file['name'], '.')), 1); //get the extension
$arr_ext = array('jpg', 'jpeg', 'gif'); //set allowed extensions
$setNewFileName = time() . "_" . rand(000000, 999999);
//only process if the extension is valid
if (in_array($ext, $arr_ext)) {
//do the actual uploading of the file. First arg is the tmp name, second arg is
//where we are putting it
move_uploaded_file($file['tmp_name'], WWW_ROOT . '/upload/avatar/' . $setNewFileName . '.' . $ext);
//prepare the filename for database entry
$imageFileName = $setNewFileName . '.' . $ext;
}
}
$getFormvalue = $this->Users->patchEntity($particularRecord, $this->request->data);
if (!empty($this->request->data['upload']['name'])) {
$getFormvalue->avatar = $imageFileName;
}
if ($this->Users->save($getFormvalue)) {
$this->Flash->success('Your profile has been sucessfully updated.');
return $this->redirect(['controller' => 'Users', 'action' => 'dashboard']);
} else {
$this->Flash->error('Records not be saved. Please, try again.');
}
}
Before using this, create a folder in webroot named upload/avatar.
Note: The input('Name Here'), is used in
$this->request->data['upload']['name']
you can print it if you want to see the array result.
Its works like a charm in CakePHP 3.x
Now that everyone makes advertisement for his plugins here, let me do this as well. I've checked the Uploadable behavior linked in the other question, it's pretty simple and half done it seems.
If you want a complete solution that is made to scale on enterprise level check FileStorage out. It has some features I haven't seen in any other implementations yet like taking care of ensuring your won't run into file system limitations in the case you get really many files. It works together with Imagine to process the images. You can use each alone or in combination, this follows SoC.
It is completely event based, you can change everything by implementing your own event listeners. It will require some intermediate level of experience with CakePHP.
There is a quick start guide to see how easy it is to implement it. The following code is taken from it, it's a complete example, please see the quick start guide, it's more detailed.
class Products extends Table {
public function initialize() {
parent::initialize();
$this->hasMany('Images', [
'className' => 'ProductImages',
'foreignKey' => 'foreign_key',
'conditions' => [
'Documents.model' => 'ProductImage'
]
]);
$this->hasMany('Documents', [
'className' => 'FileStorage.FileStorage',
'foreignKey' => 'foreign_key',
'conditions' => [
'Documents.model' => 'ProductDocument'
]
]);
}
}
class ProductsController extends ApController {
// Upload an image
public function upload($productId = null) {
if (!$this->request->is('get')) {
if ($this->Products->Images->upload($productId, $this->request->data)) {
$this->Session->set(__('Upload successful!');
}
}
}
}
class ProductImagesTable extends ImageStorageTable {
public function uploadImage($productId, $data) {
$data['adapter'] = 'Local';
$data['model'] = 'ProductImage',
$data['foreign_key'] = $productId;
$entity = $this->newEntity($data);
return $this->save($data);
}
public function uploadDocument($productId, $data) {
$data['adapter'] = 'Local';
$data['model'] = 'ProductDocument',
$data['foreign_key'] = $productId;
$entity = $this->newEntity($data);
return $this->save($data);
}
}
Maybe the following would help. It's a behavior who helps you to upload files very easy!
http://cakemanager.org/docs/utils/1.0/behaviors/uploadable/
Let me know if you struggle.
Greetz
/*Path to Images folder*/
$dir = WWW_ROOT . 'img' .DS. 'thumbnail';
/*Explode the name and ext*/
$f = explode('.',$data['image']['name']);
$ext = '.'.end($f);
/*Generate a Name in my case i use ID & slug*/
$filename = strtolower($id."-".$slug);
/*Associate the name to the extension */
$image = $filename.$ext;
/*Initialize you object and update you table in my case videos*/
$Videos->image = $image;
if ($this->Videos->save($Videos)) {
/*Save image in the thumbnail folders and replace if exist */
move_uploaded_file($data['image']['tmp_name'],$dir.DS.$filename.'_o'.$ext);
unlink($dir.DS.$filename.'_o'.$ext);
}
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use Cake\Network\Exception\InternalErrorException;
use Cake\Utility\Text;
/**
* Upload component
*/
class UploadRegCompanyComponent extends Component
{
public $max_files = 1;
public function send( $data )
{
if ( !empty( $data ) )
{
if ( count( $data ) > $this->max_files )
{
throw new InternalErrorException("Error Processing Request. Max number files accepted is {$this->max_files}", 1);
}
foreach ($data as $file)
{
$filename = $file['name'];
$file_tmp_name = $file['tmp_name'];
$dir = WWW_ROOT.'img'.DS.'uploads/reg_companies';
$allowed = array('png', 'jpg', 'jpeg');
if ( !in_array( substr( strrchr( $filename , '.') , 1 ) , $allowed) )
{
throw new InternalErrorException("Error Processing Request.", 1);
}
elseif( is_uploaded_file( $file_tmp_name ) )
{
move_uploaded_file($file_tmp_name, $dir.DS.Text::uuid().'-'.$filename);
}
}
}
}
}
We're using https://github.com/josegonzalez/cakephp-upload with great success in our production app, and has done so for quite some time.
Has awesome support for using "Flysystem" (https://flysystem.thephpleague.com/) as well - which is abstractions from specific file system(s) - so moving from normal local file system to S3 is a no-brainer, or Dropbox or whatever place you want :-)
You can find related (high quality) plugins on file uploading right here: https://github.com/FriendsOfCake/awesome-cakephp#files - I've used "Proffer" with success as well, and it's by no means "almost done" or anything alike - both has all my recommendations and is in my eyes production ready!