jrbasso/MeioUpload

MeioUpload + hasMany + JavaScript-Generated Fields

larry-tx opened this issue · 4 comments

I'm using CakePHP 1.3.3/MeioUpload 2.1.1/PHP 5.3.1. I've got a very standard hasMany/belongsTo set of models. On the many side is a simple table with id, filename, mimetype, and dir using actsAs MeioUpload. I know that I will always have at least one file on the many side so, in my add.ctp for the master side, I've got one set of fields for a single record for the many side (name="data[ItemsFile][0][filename]" and id="ItemsFile0Filename", and so on for the other fields).

When I add a record using that view, it works perfectly, uploading the associated file and making appropriate entries into the database. (My tremendous praise to the developers of MeioUpload!)

Now, comes the problem. I know that I'll need a varying number of records on the many side, so I add a JQuery button to generate an additional set of fields for an additional file upload (name="data[ItemsFile][1][filename]" and id="ItemsFile1Filename" and so on for the other fields). When I try to add a record using the original, hardcoded set of fields and the generated set of fields to upload two files, it uploads the file entered in the original, hardcoded set of fields, but not the file entered in the generated set of fields.

I then use debug() to look at what data is being sent to the controller from add.ctp. Lo and behold, only the data from the first, hardcoded set of upload fields is in the array.

Next step, I look at the source -- after clicking the button to generate a second set of fields -- using Firebug > View Source > View Generated Source. The code looks perfect, but just to make sure, I copy the jQuery-generated HTML from the Firebug Generated Source and paste it into add.ctp below the original hardcoded set of upload fields.

When I test that out, guess what happens! It uploads the two files with no problem at all and makes the appropriate entries in the database! This is the very same HTML code cut-and-pasted into add.ctp that wouldn't work when it was jQuery-generated!

Can anybody give me any idea what I'm missing? This one has me completely stumped.

Are you using the Security component, or anything to sanitize your data? It might be that your form looks okay, but the generated fields are outside the form itself.

Please provide an example application if you continue to have this issue.

I'm not using the Security component. I was trying to avoid pasting in the code because of its length, but here goes.

My crochet_item.php (the belongs to Model):

array( 'notempty' => array( 'rule' => array('notempty'), //'message' => 'Your custom message here', //'allowEmpty' => false, //'required' => false, //'last' => false, // Stop validation after this rule //'on' => 'create', // Limit validation to 'create' or 'update' operations ), ), ); var $actsAs = array( 'MeioUpload.MeioUpload' => array( 'primary_picture_filename' => array( 'dir' => 'uploads/crochet_item/primary_picture_filename', 'create_directory' => true, 'allowed_mime' => array('image/jpeg', 'image/phpeg', 'image/png', 'image/gif'), 'allowed_ext' => array('.jpg.', '.jpeg', '.png', '.gif'), 'thumbsizes' => array( 'thumb' => array('width' => 100, 'height' => 100) ), 'default' => 'default.jpg', ) ) ); //The Associations below have been created with all possible keys, those that are not needed can be removed ``` var $belongsTo = array( 'CrochetShape' => array( 'className' => 'CrochetShape', 'foreignKey' => 'crochet_shape_id', 'conditions' => '', 'fields' => '', 'order' => '' ), 'CrochetSource' => array( 'className' => 'CrochetSource', 'foreignKey' => 'crochet_source_id', 'conditions' => '', 'fields' => '', 'order' => '' ), 'CrochetThread' => array( 'className' => 'CrochetThread', 'foreignKey' => 'crochet_thread_id', 'conditions' => '', 'fields' => '', 'order' => '' ) ); var $hasMany = array( 'CrochetItemsFile' => array( 'className' => 'CrochetItemsFile', 'foreignKey' => 'crochet_item_id', 'order' => 'CrochetItemsFile.filename', 'dependent' => true ) ); var $hasAndBelongsToMany = array( 'CrochetCategory' => array( 'className' => 'CrochetCategory', 'joinTable' => 'crochet_categories_crochet_items', 'foreignKey' => 'crochet_item_id', 'associationForeignKey' => 'crochet_category_id', 'unique' => true, 'conditions' => '', 'fields' => '', 'order' => '', 'limit' => '', 'offset' => '', 'finderQuery' => '', 'deleteQuery' => '', 'insertQuery' => '' ) ); ``` } ?>

And its associated controller:

array('Jquery'), 'Html', 'Ajax', 'Paginator'); function index() { $this->pageTitle = "Crochet Items"; $this->CrochetItem->recursive = 0; $this->set('crochetItems', $this->paginate()); } function view($id = null) { $this->pageTitle = "Crochet Item"; if (!$id) { $this->Session->setFlash(sprintf(__('Invalid %s', true), 'crochet item')); $this->redirect(array('action' => 'index')); } $this->set('crochetItem', $this->CrochetItem->read(null, $id)); } function add() { $this->pageTitle = "Add Crochet Item"; if (!empty($this->data)) { debug($this->data);exit; $this->CrochetItem->create(); unset($this->CrochetItem->CrochetItemsFile->validate['crochet_item_id']); if ($this->CrochetItem->saveAll($this->data)) { $this->Session->setFlash(sprintf(__('The %s has been saved', true), 'crochet item')); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'crochet item')); } } $crochetShapes = $this->CrochetItem->CrochetShape->find('list'); $crochetSources = $this->CrochetItem->CrochetSource->find('list'); $crochetThreads = $this->CrochetItem->CrochetThread->find('list'); $crochetCategories = $this->CrochetItem->CrochetCategory->generatetreelist(null,null,null," - "); $this->set(compact('crochetShapes', 'crochetSources', 'crochetThreads', 'crochetCategories')); } function edit($id = null) { $this->pageTitle = "Edit Crochet Item"; if (!$id && empty($this->data)) { $this->Session->setFlash(sprintf(__('Invalid %s', true), 'crochet item')); $this->redirect(array('action' => 'index')); } if (!empty($this->data)) { if ($this->CrochetItem->save($this->data)) { $this->Session->setFlash(sprintf(__('The %s has been saved', true), 'crochet item')); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'crochet item')); } } if (empty($this->data)) { $this->data = $this->CrochetItem->read(null, $id); } $crochetShapes = $this->CrochetItem->CrochetShape->find('list'); $crochetSources = $this->CrochetItem->CrochetSource->find('list'); $crochetThreads = $this->CrochetItem->CrochetThread->find('list'); $crochetCategories = $this->CrochetItem->CrochetCategory->generatetreelist(null,null,null," - "); $this->set(compact('crochetShapes', 'crochetSources', 'crochetThreads', 'crochetCategories')); } function delete($id = null) { if (!$id) { $this->Session->setFlash(sprintf(__('Invalid id for %s', true), 'crochet item')); $this->redirect(array('action'=>'index')); } if ($this->CrochetItem->delete($id)) { $this->Session->setFlash(sprintf(__('%s deleted', true), 'Crochet item')); $this->redirect(array('action'=>'index')); } $this->Session->setFlash(sprintf(__('%s was not deleted', true), 'Crochet item')); $this->redirect(array('action' => 'index')); } ``` } ?>

And its associated add.ctp:

<style> div.input { display: inline; } </style> <script type="text/javascript" src="/mysite/js/ckeditor/ckeditor.js"></script>

Add Crochet Item

Form->create('CrochetItem', array('type'=>'file', 'action' => 'add')); ?>
Form->label('CrochetItem.name', 'Name: '); ?> Form->input('CrochetItem.name', array('label'=>false, 'size'=>'70')); ?>
Form->label('CrochetItem.crochet_shape_id', 'Shape: '); ?> Form->input('CrochetItem.crochet_shape_id', array('label'=>false)); ?> Form->label('CrochetItem.height', 'Height: '); ?> Form->input('CrochetItem.height', array('label'=>false, 'space'=>'10')); ?> Form->label('CrochetItem.width', 'Width: '); ?> Form->input('CrochetItem.width', array('label'=>false, 'space'=>'10')); ?>
Form->label('CrochetItem.crochet_source_id', 'Source: '); ?> Form->input('CrochetItem.crochet_source_id', array('label'=>false)); ?>
Form->label('CrochetItem.crochet_thread_id', 'Thread Type: '); ?> Form->input('CrochetItem.crochet_thread_id', array('label'=>false)); ?>
Form->label('CrochetItem.description', 'Description: '); ?> Form->input('CrochetItem.description', array('label'=>false, 'cols' => '53', 'rows' => '5', 'class'=>'ckeditor')); ?>
Form->label('CrochetItem.primary_picture_filename', 'Primary Picture:')?> Form->input('CrochetItem.primary_picture_filename', array('label'=>false, 'type'=>'file')); echo $form->input('CrochetItem.dir', array('type' => 'hidden')); echo $form->input('CrochetItem.mimetype', array('type' => 'hidden')); echo $form->input('CrochetItem.filesize', array('type' => 'hidden')); ?>
    </td>
    <th>Category</th>
    <td colspan="2" style="vertical-align: top;"><?php echo $this->Form->input('CrochetCategory', array('label'=>false)); ?></td>
</tr>

Files

Title:
'.$this->Form->input('CrochetItemsFile.0.title', array('label'=>false, 'size'=>'70')).'   '; echo "File: ".$this->Form->input('CrochetItemsFile.0.filename', array('type' => 'file', 'label' => false)); echo $this->Form->input('CrochetItemsFile.0.mimetype', array('type' => 'hidden')); echo $this->Form->input('CrochetItemsFile.0.filesize', array('type' => 'hidden')); echo $this->Form->input('CrochetItemsFile.0.dir', array('type' => 'hidden')); ?>


   

Form->end(__('Submit', true));?>

  • Html->link(sprintf(__('List %s', true), __('Crochet Items', true)), array('action' => 'index'));?>
  • Html->link(sprintf(__('List %s', true), __('Crochet Shapes', true)), array('controller' => 'crochet_shapes', 'action' => 'index')); ?>
  • Html->link(sprintf(__('New %s', true), __('Crochet Shape', true)), array('controller' => 'crochet_shapes', 'action' => 'add')); ?>
  • Html->link(sprintf(__('List %s', true), __('Crochet Sources', true)), array('controller' => 'crochet_sources', 'action' => 'index')); ?>
  • Html->link(sprintf(__('New %s', true), __('Crochet Source', true)), array('controller' => 'crochet_sources', 'action' => 'add')); ?>
  • Html->link(sprintf(__('List %s', true), __('Crochet Threads', true)), array('controller' => 'crochet_threads', 'action' => 'index')); ?>
  • Html->link(sprintf(__('New %s', true), __('Crochet Thread', true)), array('controller' => 'crochet_threads', 'action' => 'add')); ?>
  • Html->link(sprintf(__('List %s', true), __('Crochet Categories', true)), array('controller' => 'crochet_categories', 'action' => 'index')); ?>
  • Html->link(sprintf(__('New %s', true), __('Crochet Category', true)), array('controller' => 'crochet_categories', 'action' => 'add')); ?>
<script type="text/javascript"></script>

The many-side model:

array( 'filename' => array( 'dir' => 'uploads/crochet_item/files', 'create_directory' => true, 'allowedMime' => array('image/jpeg', 'image/png', 'image/gif', 'image/jpg', 'application/msword', 'application/pdf', 'text/richtext', 'text/plain', 'application/excel'), 'allowedExt' => array('.jpg.', '.jpeg', '.png', '.gif', '.doc', '.pdf', '.rtf', '.txt', '.xls'), 'default' => 'default.jpg', 'thumbsizes' => array( 'thumb' => array('width' => 100, 'height' => 100) ) ) ) ); ``` var $validate = array( 'crochet_item_id' => array( 'numeric' => array( 'rule' => array('numeric'), //'message' => 'Your custom message here', //'allowEmpty' => false, //'required' => false, //'last' => false, // Stop validation after this rule //'on' => 'create', // Limit validation to 'create' or 'update' operations ), 'notempty' => array( 'rule' => array('notempty'), //'message' => 'Your custom message here', //'allowEmpty' => false, //'required' => false, //'last' => false, // Stop validation after this rule //'on' => 'create', // Limit validation to 'create' or 'update' operations ), ), ); //The Associations below have been created with all possible keys, those that are not needed can be removed var $belongsTo = array( 'CrochetItem' => array( 'className' => 'CrochetItem', 'foreignKey' => 'crochet_item_id', 'conditions' => '', 'fields' => '', 'order' => '' ) ); ``` } ?>

And its controller:

CrochetItemsFile->recursive = 0; $this->set('crochetItemsFiles', $this->paginate()); } function view($id = null) { if (!$id) { $this->Session->setFlash(sprintf(__('Invalid %s', true), 'crochet items file')); $this->redirect(array('action' => 'index')); } $this->set('crochetItemsFile', $this->CrochetItemsFile->read(null, $id)); } function add() { if (!empty($this->data)) { $this->CrochetItemsFile->create(); if ($this->CrochetItemsFile->save($this->data)) { $this->Session->setFlash(sprintf(__('The %s has been saved', true), 'crochet items file')); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'crochet items file')); } } $crochetItems = $this->CrochetItemsFile->CrochetItem->find('list'); $this->set(compact('crochetItems')); } function edit($id = null) { if (!$id && empty($this->data)) { $this->Session->setFlash(sprintf(__('Invalid %s', true), 'crochet items file')); $this->redirect(array('action' => 'index')); } if (!empty($this->data)) { if ($this->CrochetItemsFile->save($this->data)) { $this->Session->setFlash(sprintf(__('The %s has been saved', true), 'crochet items file')); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'crochet items file')); } } if (empty($this->data)) { $this->data = $this->CrochetItemsFile->read(null, $id); } $crochetItems = $this->CrochetItemsFile->CrochetItem->find('list'); $this->set(compact('crochetItems')); } function delete($id = null) { if (!$id) { $this->Session->setFlash(sprintf(__('Invalid id for %s', true), 'crochet items file')); $this->redirect(array('action'=>'index')); } if ($this->CrochetItemsFile->delete($id)) { $this->Session->setFlash(sprintf(__('%s deleted', true), 'Crochet items file')); $this->redirect(array('action'=>'index')); } $this->Session->setFlash(sprintf(__('%s was not deleted', true), 'Crochet items file')); $this->redirect(array('action' => 'index')); } ``` } ?>

I love CakePHP, but I'm just learning it. I assume that I have made many, multiple mistakes. Thanks for your generous help.

I found the problem! It was, indeed, where Cake was throwing the tag due to the way the div's were constructed. Many thanks for the wonderful MeioUpload.

Please use a pastebin such as http://gist.github.com or http://bin.cakephp.org in the future for pasting code samples online. Hope your application turns out well otherwise.