I am using http://milesj.me/code/cakephp/uploader#configuration to upload images. I got it to work fine (in terms of uploading images) but I can't get it to save title/description in my db.
so I have Image.php model that has the following code
<?php
class Image extends AppModel {
var $name = 'Image';
public $actsAs = array('Uploader.Attachment', 'Uploader.FileValidation');
public $validate = array(
'title' => array( 'rule' => 'notEmpty')
);
}
In my view I have bunch of fields such as
echo $this->Form->input('title');
My ImagesController.php add function looks like this
function add($number_of_images = 1){
if (!empty($this->data)) {
var_export($this->data);
exit();
$count = 1;
foreach($this->data['Images'] as $entry){
$file_name = "file" . $count;
if ($data_s = $this->Uploader->upload($file_name)) {
$this->Image->saveAll($data_s);
}
$count++;
}
$this->Session->setFlash("Your image(s) has been saved");
$this->redirect(array('action'=>'index'));
}else{
// make sure 10 is max amount of images a user can upload
if($number_of_images <= 10 ){
$this->set('number_of_images', $number_of_images);
}else{
// set to default 1
$this->set('number_of_images', '1');
}
}
}
When I click save, the image gets uploaded but title/description doesnt get uploaded or validated. This is how my var_export($this->data) looks like
array ( 'selectImages' => '1', 'Images' => array ( 'title' => 'adsafdas', 'description' => 'asdfasd', 'tags' => '', 'file1' => array ( 'name' => '308462_926071922398_11704522_41424436_637322498_n.jpg', 'type' => 'image/jpeg', 'tmp_name' => '/tmp/php7tycbu', 'error' => 0, 'size' => 81638, ), ), )
How can I fix this?
According with the link, $this->Uploader->upload() returns only data of file uploaded. So, you need merge this array with the other fields of your form $this->data before saveAll.
However, if you need validate form data before upload the file, use $this->Image->validates($this->request->data).
Related
I'm developing a PHP application where I have a form in which the user should upload a text file and an other page works with this data. I'm also creating controller tests with CakePHP and PHPUnit. My question is, how can I make the test automatically upload a file for the tests of this action?
Thanks in advance.
update 1:
The question in more details: Basically I have a 'submit' action which has the form in its view which submits the parameters (and the uploaded file) to the 'curl' action. Here this curl action processes the uploaded text file and this is actually what I'd like to test. But to test this, I should have an uploaded file with its content, so a more specific question: how could I mock this uploaded file to 'submit' it to my curl controller?
Code snippet for the View:
...
echo '<fieldset><table>';
echo $this->Form->create('Job', array('type' => 'post','action' => 'curl', 'enctype' => 'multipart/form-data'));
echo $this->Form->input('user.name', array('type' => 'hidden', 'value' => $this->User->getCode1(),'name' => 'user.name'));
echo $this->Form->input('class', array('type' => 'hidden', 'value' => $this->Hadoop->getClass(), 'name' => 'class'));
...
update 2:
The test I've already written:
public function testCurl() {
$data = array(
'user_name' => ...,
'release' => ...,
'analysis_period_start' => ...,
'uploaded_file' => ???
);
$Jobs = $this->generate('Jobs', array('components' => array('Session')));
$Jobs->Session->expects($this->any())->method('setFlash');
$this->testAction('/jobs/curl', array('data' => $data, 'method' => 'post'));
}
So basically I'm trying to test my curl action with a POST method with the data provided in the $data variable. But I don't know how to mock/imitate an uploaded file into that array.
update 3:
The relevant cod snippet from my controller's given action:
public function curl() {
/* This action accepts only POST request */
if (!$this->isPOSTRequest())
return $this->redirect(array('controller' => 'jobs', 'action' => 'submit'));
/* Create a new entry in the database and get its ID */
$id = $this->createNewEntryInTheDatabase();
/* Inserts the new patterns into the DB and looks up the already existing
** patterns in the DB. Returns an array with the IDs of the submitted patterns */
$patternIds = $this->lookupPatternIDsAndInsertNewPatterns($_FILES['patterns']);
$curl = $this->initCURL();
...
$this->closeCURL($curl);
return $this->redirect(array('controller' => 'jobs', 'action' => 'submit'));
}
...
private function lookupPatternIDsAndInsertNewPatterns($patternsFile) {
$patternIDs = null;
/* One element for each row */
$patternsArray = $this->convertUploadedCSVFileIntoArray(
$patternsFile, $this->CSV->getDelimiter(),
$this->CSV->getEnclosure(), $this->CSV->getEscape());
...
return $patternIDs;
}
/* Returns the patterns from the CSV file in an array */
private function convertUploadedCSVFileIntoArray(
$patternsFile, $delimiter, $enclosure, $escape) {
$patternsArray = file($patternsFile['tmp_name']);
$patterns = null;
foreach ($patternsArray as $pattern)
$patterns[] = str_getcsv(
$pattern,
$delimiter,
$enclosure,
$escape);
return $patterns;
}
I'm trying to perform validation with cake 2.3.8 on a file upload to make sure that only PDF's can be uploaded. I'm loosly basing this off of this tutorial.
My form is displaying the asterisk next to the input, and when I remove the validation from my model the asterisk goes away. I'm assuming this means it "sees" the input for validation, but I just can't figure out why even the custom validation isn't being triggered.
Here's the form
echo $this->Form->create('Upload', array('type' => 'file'));
echo $this->Form->input('file_upload', array('type' => 'file'));
echo $this->Form->input('file_title');
echo $this->Form->end(__('Upload File!', true));
Here's the code in my Upload model
public function checkUpload(){
echo "test"; //check to see if it reaches this...not displaying
return false; //the error message should be set just for testing, it's not displaying though
}
public $validate = array(
'file_upload' => array(
'extension' => array(
'rule' => array('extension', array('pdf')),
'message' => 'Only pdf files',
),
'upload-file' => array(
'rule' => array('checkUpload'),
'message' => 'Error uploading file'
)
)
);
Here is my answer (albeit for cakephp 1.3):
In your model add the following validation to your $validate variable.
$this->validate = array(...
// PDF File
'pdf_file' => array(
'extension' => array(
'rule' => array('extension', array('pdf')),
'message' => 'Only pdf files',
),
'upload-file' => array(
'rule' => array('uploadFile'), // Is a function below
'message' => 'Error uploading file'
)
)
); // End $validate
/**
* Used when validating a file upload in CakePHP
*
* #param Array $check Passed from $validate to this function containing our filename
* #return boolean True or False is passed or failed validation
*/
public function uploadFile($check)
{
// Shift the array to easily acces $_POST
$uploadData = array_shift($check);
// Basic checks
if ($uploadData['size'] == 0 || $uploadData['error'] !== 0)
{
return false;
}
// Upload folder and path
$uploadFolder = 'files'. DS .'charitylogos';
$fileName = time() . '.pdf';
$uploadPath = $uploadFolder . DS . $fileName;
// Make the dir if does not exist
if(!file_exists($uploadFolder)){ mkdir($uploadFolder); }
// Finally move from tmp to final location
if (move_uploaded_file($uploadData['tmp_name'], $uploadPath))
{
$this->set('logo', $fileName);
return true;
}
// Return false by default, should return true on success
return false;
}
You may have to display the error validation messages yourself, you can do this using:
<!-- The classes are for twitter bootstrap 3 - replace with your own -->
<?= $form->error('pdf_file', null, array('class' => 'text-danger help-block'));?>
if you try to debug sth in Cake, always use debug(sth) // sth could be variable could be string could be anything, cuz in Cake debug means
echo "<pre>";
print_r(sth);
echo "</pre>";`
it's already formatted very well.
then after that you have to put die() otherwise after echo sth it will load the view that's why you can't see it even there was an output.
My custom module code:
<?php
function my_module_menu() {
$items = array();
$items['form-example'] = array(
'title' => 'My Module Form',
'description' => 'A form to mess around with.',
'page callback' => 'drupal_get_form',
'page arguments' => array('my_module_form'),
'access callback' => TRUE
);
return $items;
}
function my_module_form($form, &$form_state, $no_js_use = FALSE) {
$form['file'] = array(
'#type' => 'file',
'#title' => t('Image'),
'#description' => t('Upload an image'),
);
$form['menu'] = array(
'#markup' => '<b>Add More:</b>'
);
$form['#tree'] = TRUE;
$form['names_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Add more images'),
'#prefix' => '<div id="names-fieldset-wrapper">',
'#suffix' => '</div>',
);
if (empty($form_state['num_names'])) {
$form_state['num_names'] = 1;
}
for ($i = 0; $i < $form_state['num_names']; $i++) {
$form['names_fieldset']['name'][$i][0]= array(
'#title' => t('Image'),
'#type' => 'file',
'#weight' => '5',
'#description' => t('Upload an image'),
);
}
$form['names_fieldset']['add_name'] = array(
'#type' => 'submit',
'#value' => t('Add one more'),
'#submit' => array('my_module_add_more_add_one'),
'#ajax' => array(
'callback' => 'my_module_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
if ($form_state['num_names'] > 1) {
$form['names_fieldset']['remove_name'] = array(
'#type' => 'submit',
'#value' => t('Remove one'),
'#submit' => array('my_module_add_more_remove_one'),
'#ajax' => array(
'callback' => 'my_module_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
$form['#submit'][] = 'my_module_add_more_submit';
if ($no_js_use) {
if (!empty($form['names_fieldset']['remove_name']['#ajax'])) {
unset($form['names_fieldset']['remove_name']['#ajax']);
}
unset($form['names_fieldset']['add_name']['#ajax']);
}
return $form;
}
function my_module_add_more_callback($form, $form_state) {
return $form['names_fieldset'];
}
function my_module_add_more_add_one($form, &$form_state) {
$form_state['num_names']++;
$form_state['rebuild'] = TRUE;
//$form_state['no_redirect'] = TRUE;
}
function my_module_add_more_remove_one($form, &$form_state) {
if ($form_state['num_names'] > 1) {
$form_state['num_names']--;
}
$form_state['rebuild'] = TRUE;
}
function my_module_add_more_submit($form, &$form_state) {
$file = $form_state['values']['file']."<br \>";
$validators = array();
$file = file_save_upload('file', $validators, 'public://uploads');
print_r($file);
exit();
}
When i submit the form, i am trying to get the details of images, that are added through Add More option. But i am not able to get them. However i am only able to get the details of the first image (and able to upload it).
I want to know two things here:
How can i retrieve the details of the images that are added with Add More option (fieldset), and how can i upload them ?
When i browse and select an image in the fieldset, it is not retained in the form, after adding another image field. How to retain the selected images in fieldset ?
Look at this article -- http://evolvingweb.ca/story/poutine-maker-introduction-field-api-drupal-7-part-1 -- as it has some information on something missing in your code. $delta. $delta is the id assigned to values of fields, even if your field only has 1 item.
What do you see when you var_dump the file field you created? If you get all the information along with those added using 'Add one more' button, you can learn the correct structure of the values using: echo "<pre>"; print_R($form_state['values']); echo "</pre>":
There are a couple of issues with your code.
The first issue is that your submit function only deals with the first upload field which is indeed called "file". But does absolutely nothing to handle the other fields.
A second issue is that it will upload and save the first field every time you click on "Add one more" which will duplicate your upload. You would not experience this issue without AJAX, but if you want to add that, you will.
I would make the following changes:
Remove the $form['#tree'] = TRUE and add it to the fieldset instead. $form['names_fieldsets']['#tree'] = TRUE; after you declare the fieldset of course.
Change the way you declare the file fields in the fieldset (inside the for loop) to this:
for ($i = 0; $i < $form_state['num_names']; $i++) {
$form['names_fieldset'][$i]['name']= array(
'#title' => t('Image'),
'#type' => 'file',
'#weight' => '5',
'#description' => t('Upload an image'),
// We need this to know which file element this is.
// By default drupal would name all as files[names_fieldset]
'#name' => 'files[names_fieldset_' . $i . '_name]',
);
}
I would change the submit function like this (note that I assume you also do my above suggested changes):
function my_module_add_more_submit($form, &$form_state) {
if ($form_state['values']['op'] == 'Submit') {
$validators = array();
$files = array();
if (!empty($_FILES['files']['name']['file'])) {
$files[] = file_save_upload('file', $validators, file_default_scheme() . '://uploads');
}
foreach ($form_state['values']['names_fieldset'] as $name => $field) {
if ($name != 'add_name') {
$file_name = implode('_', $form['names_fieldset'][$name]['name']['#parents']);
if (!empty($_FILES['files']['name'][$file_name])) {
$files[] = file_save_upload($file_name, $validators, file_default_scheme() . '://uploads');
}
}
}
}
}
With this changes we set a form field name aware that's inside a tree. We only trigger uploads when the "Submit" button is clicked and only for form fields that actually had a file added to them. Also we upload using the default scheme and don't use always the public one.
Of course the code need some messages for the user to know how many files did upload, names, or any other deemed worthy info.
How can I get the filename of the file uploaded in codeigniter. We get the values of the input fields as $this->input->post('name'), but how can i get the name of the file uploaded from the form??
public function create(){
//getting all the POST data in $data array
$data = array('title' => $this->input->post('title') ,
'content' => $this->input->post('content'),
'filename' => ?? HOW ??
);
}
1) make sure that you form is multipart. With helper its form_open_multipart()
2) use upload library for receiving the file
3) then with $this->upload->data() you get array with file info
Here is full how to and official documentation
public function create(){
//getting all the POST data in $data array.
$data = array('title' => $this->input->post('title') ,
'content' => $this->input->post('content'),
'filename' => $_FILES["file"]["name"]
);
}
im using Drupal 7 and I want to add a new filter in views.
I have a custom table "clicks" with two fields; nid and clicks_left.
The filter should just contain a checkbox "Only display nodes with clicks left". So the filter should join node and clicks on nid..
I have read like thousands of pages of custom filters but can't get it to work =)
Please, could someone show me a working example so I understand?
I have come so far that the filter is displayed under filters but what do I need to add to do the join and get the checkbox? The relevant code below:
FILE clicks_views.inc:
function clicks_views_data() {
$data = array();
$data['clicks']['clicks_filter'] = array(
'group' => t('Clicks'),
'title' => t('Clicks left'),
'help' => t('Filter any Views based on clicks left'),
'filter' => array(
'field' => 'clicks_left',
'handler' => 'clicks_handler_filter',
),
);
return $data;
}
FILE clicks_handler_filter.inc:
<?php
class clicks_handler_filter extends views_handler_filter {
???
};
I know both functions are wrong ;)
Ok, I've found a solution. For anyone who needs it:
In clicks.module
function clicks_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'clicks') . '/includes'
);
}
In clicks.views.inc
function clicks_views_handlers() {
return array(
'info' => array(
'path' => drupal_get_path('module', 'clicks') . '/includes', // path to view files
),
'handlers' => array(
// register our custom filter, with the class/file name and parent class
'clicks_handler_filter' => array(
'parent' => 'views_handler_filter',
)
),
);
}
function clicks_views_data() {
$data = array();
if(module_exists('clicks')) {
$data['node']['clicks'] = array(
'group' => t('Clicks'),
'title' => t('Clicks left'),
'help' => t('Filter any Views based on clicks left'),
'filter' => array(
'handler' => 'clicks_handler_filter',
),
);
}
return $data;
}
In clicks_handler_filter.inc
class clicks_handler_filter extends views_handler_filter {
function admin_summary() { }
function operator_form() { }
function query() {
$table = $this->ensure_my_table();
$join = new views_join();
$join->construct('clicks', $this->table_alias, 'nid', 'nid');
$this->query->ensure_table('clicks', $this->relationship, $join);
$this->query->add_where($this->options['group'], "clicks.clicks_left", 0, ">");
}
}
This gives me a possibility to add a filter "clicks" that if enabled hides all results that doesn't have clicks left (clicks_left > 0)
Actually, if your values in your tables clicks are numeric you don't need to create your own handler, you can use the default from Views views_handler_filter_numeric.
You can see all handlers that already exists in the Views handlers.