i generated a multiple upload form with the former generator tool from https://github.com/Anahkiasen/former.
{{ Former::files('file')->accept('image')->required(); }}
that results in
<input multiple="true" accept="image/*" required="true" id="file[]" type="file" name="file[]">
After I've submit the form Ive figured out that Input::hasFile('file') always returns false whilst Input:has('file') returns true.
What i've tried until now:
Log::info(Input::file('file')); <-- empty
foreach(Input::file('file') as $file) <-- Invalid argument supplied for foreach()
Log::info("test");
if(Input::has('file'))
{
if(is_array(Input::get('file')))
foreach ( Input::get('file') as $file)
{
Log::info($file); <-- returns the filename
$validator = Validator::make( array('file' => $file), array('file' => 'required|image'));
if($validator->fails()) {
...
}
}
}
Of course, the validator always fails cause Input::get('file') does not return a file object. How do I have to modify my code to catch the submited files?
Thanks for the help, the answer from Kestutis made it clear. The common way to define a file form in Laravel is
echo Form::open(array('url' => 'foo/bar', 'files' => true))
This Options sets the proper encryption type with enctype='multipart/form-data'.
With the laravel form builder "Former" you have to open the form with
Former::open_for_files()
After that u can validate the form in the common way.
if(Input::hasFile('files')) {
Log::info(Input::File('files'));
$rules = array(
...
);
if(!array(Input::File('files')))
$rules["files"] = 'required|image';
else
for($i=0;$i<count(Input::File('files'));$i++) {
$rules["files.$i"] = 'required|image';
}
$validation = Validator::make(Input::all(), $rules);
if ($validation->fails())
{
return ...
}
else {
// everything is ok ...
}
Related
I have a Customer entity that only has a unique Email field to it. I am trying to edit a customer's email and the validation works fine. However I have this in my controller:
public function updateAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AcmeDemoBundle:Customer')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Customer entity.');
}
$editForm = $this->createForm(new CustomerType(), $entity);
$editForm->bind($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('ticket_result'));
}
var_dump($editForm->getErrors());
return $this->render('AcmeDemoBundle:Customer:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
));
}
The var_dump returns an empty array but the validator sets a unique error and the $editForm->isValid() returns false. Is there a way to check for that specific error in the controller during validation, also can you explain why it returns an empty error array? Basically, I would like to provide the "merge" option if that error comes up.
EDIT: here is the formtype:
namespace Acme\DemoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CustomerType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('email', 'email', array('required'=>true))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Acme\DemoBundle\Entity\Customer',
'cascade_validation' => true,
));
}
public function getName() {
return 'acme_demobundle_customertype';
}
}
And the twig template:
{% extends 'AcmeDemoBundle::layout.html.twig' %}
{% block body -%}
<h1>Customer edit</h1>
<form action="{{ path('customer_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(edit_form) }}>
<input type="hidden" name="_method" value="PUT" />
{{ form_widget(edit_form) }}
<p>
<button type="submit">Edit</button>
</p>
</form>
{% endblock %}
Here is my validation:
Acme\DemoBundle\Entity\Customer:
constraints:
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
fields: email
message: "A customer under that email address already exists"
properties:
email:
- Email: ~
For debug purposes you can use $form->getErrorsAsString() instead of $form->getErrors() if you use Symfony 2.*
Quoted from this answer:
$form->getErrorsAsString() should only be used to debug the form...it
will contain the errors of each child elements which is not the case
of $form->getErrors().
UPDATE 1:
"With more recent Symfony versions, you must use $form->getErrors(true, false); instead. First param corresponds to deep and second to flatten" (see the comment by #Roubi)
Ok, found an answer here:
Symfony2 invalid form without errors
It turns out each form child has it's own separate errors. When doing a var_dump of
$editForm->getChildren()['email']->getErrors()
I get:
array (size=1)
0 =>
object(Symfony\Component\Form\FormError)[531]
private 'message' => string 'A customer under that email address already exists' (length=50)
protected 'messageTemplate' => string 'A customer under that email address already exists' (length=50)
protected 'messageParameters' =>
array (size=0)
empty
protected 'messagePluralization' => null
I am still wondering how to determine that the error is because of a unique conflict without parsing the error message string.
The following solutions works for me:
$form->getErrors(true)
You can use error_bubbling on each field to bubble the error up to your $form.
If not, you can also foreach through the errors
foreach ($children as $child) {
if ($child->hasErrors()) {
$vars = $child->createView()->getVars();
$errors = $child->getErrors();
foreach ($errors as $error) {
$this->allErrors[$vars["name"]][] = $this->convertFormErrorObjToString($error);
}
}
}
In Symfony 2.3, you can use this one :
if ($form->isValid()){
# Code...
} else {
foreach ($form->getIterator() as $key => $child) {
if ($child instanceof Form) {
foreach ($child->getErrors() as $error) {
$errors[$key] = $error->getMessage();
}
}
}
}
This will get you an array ($errors) with the errors from the children.
You could try to use the dump function when the form is submited and not valid. I use it like this
if($form->isSubmited() && $form->isValid()){
//SAVE TO DATABASE AND DO YOUR STUFF
}else if($form->isSubmited()){
//SUBMITED BUT WITH ERRORS
dump($form->getErrors(true));
die();
}
Note this is for debugging purposes only, It will show you your form, the data in it and all the errors any field could have.
In production mode you should return the error to the view and show them to the user.
I have successfully added my own form (from the same module) into my custom template, but now I wish to load the taxonomy add term form (used by ubercart I think for product categories in the catalog vocab) into my template.
I have gotten this far with my module - filename simpleadmin.module
/**
* #file
* A module to simplify the admin by replacing add/edit node pages
*/
function simpleadmin_menu() {
$items['admin/products/categories/add'] = array(
'title' => 'Add Category',
'page callback' => 'simpleadmin_category_add',
'access arguments' => array('access administration pages'),
'menu_name' => 'menu-store',
);
return $items;
}
function simpleadmin_category_add() {
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
$output = drupal_get_form('taxonomy_form_term');
return theme('simpleadmin_category_add', array('categoryform' => $output));
}
function simpleadmin_theme() {
return array(
'simpleadmin_category_add' => array(
'template' => 'simpleadmin-template',
'variables' => array('categoryform' => NULL),
'render element' => 'form',
),
);
}
?>
And as for the theme file itself - filename simpleadmin-template.tpl.php, only very simple at the moment until I get the form to load into it:
<div>
This is the form template ABOVE the form
</div>
<?php
dpm($categoryform);
print drupal_render($categoryform);
?>
<div>
This is the form template BELOW the form
</div>
Its telling me that it is
Trying to get property of non-object in taxonomy_form_term()
and throwing up an error. Should I be using node_add() and passing the nodetype?
To render a taxonomy term form, the function should be able to know the vocabulary to which it belongs to. Otherwise how would it know which form to show? I think this is the proper way to do it.
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
if ($vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary_name')) {
$form = drupal_get_form('taxonomy_form_term', $vocabulary);
return theme('simpleadmin_category_add', array('categoryform' => $form));
}
To redirect your form use hook_form_alter
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
//get your vocabulary id or use print_r or dpm for proper validation
if($form_id == 'taxonomy_form_term' && $form['#vocabulary']['vid'] = '7' ){
$form['#submit'][] = 'onix_sections_form_submit';
}
}
function yourmodule_form_submit($form, &$form_state) {
$form_state['redirect'] = 'user';
}
I am implementing an updatePasswordAction and its not displaying an error with an invalid current password. I could not implement this with a Zend_Validate class to use with a supplied record->password, so i just validated for now in my controller action and if failed then i add the error message to the form element. this is just before i run $form->isValid. In any case, its working. but when the validation fails, its not displaying the error message on this on the element. any help would be greatly appreciated.
FYI: When I submit a blank current password, it shows the validation
class Admin_Form_UserPassword extends Katana_Form
{
public function init()
{
$element = $this->createElement('hidden', 'id');
$this->addElement($element);
$element = $this->createElement('password','password');
$element->setLabel('Current Password:');
$element->setRequired(true);
$this->addElement($element);
$element = $this->createElement('password','new_password');
$element->setLabel('New Password:');
$element->addValidator('StringLength', false, array(6,24));
$element->setRequired(true);
$element->addValidator('NotEmpty');
$this->addElement($element);
$element = $this->createElement('password','new_password_confirm');
$element->setLabel('Confirm:');
$element->addValidator('StringLength', false, array(6,24));
$element->addValidator('IdenticalField', false, array('new_password', 'Confirm Password'));
$element->setRequired(true);
$this->addElement($element);
$this->addElement('submit', 'submit', array('label' => 'Submit'));
}
}
public function updatePasswordAction()
{
$resourceModel = new Core_Model_Resource_User();
$form = new Admin_Form_UserPassword();
$form->setMethod(Katana_Form::METHOD_POST);
$form->setAction($this->getActionUrl('update-password'));
if($this->getRequest()->isPost()){
$id = $this->getRequest()->getParam('id');
$record = $resourceModel->find($id)->current();
$currPassword = $record->password;
$typedPassword = md5($this->getRequest()->getParam('password'));
if($currPassword !== $typedPassword){
$form->getElement('password')->addError('Current password is incorrect.');
}
if($form->isValid($_POST)){
$data = $form->getValues();
$result = $resourceModel->updatePassword($id, $data['new_password']);
if($result){
$this->redirectSimple('list');
}
}
} else {
$id = $this->getRequest()->getParam('id');
$recordData = array(
'id' => $id
);
$form->populate($recordData);
}
$this->getView()->form = $form;
}
Adding an error to the element doesn't cause the form itself to then be invalid.
There are at least 2 methods I use to get around this:
if($currPassword !== $typedPassword){
$form->getElement('password')->addError('Current password is incorrect.');
$form->markAsError();
}
// or
if ($form->isValid($_POST) && 0 == sizeof($form->getMessages()) {
// form was valid, and no errors were set on elements
}
To clarify, when you add the error to the form ELEMENT, there is an error attached to that element, but Zend_Form::isValid only runs the validators and sets appropriate errors, it doesn't check to see if you had set an error on a particular element.
You can however call $form->getMessages() to get all the error messages attached to the form or its child elements. If this is 0 and you have validated your form, then it means there were no errors. If your form passed isValid but you added an error to an element, it will include the error message you added.
I made it work this way.
Controller:
if ($trial->getKind() != 'debt' && $_POST['kind'] == 'debt')
{
$editForm->getElement('kind')->markAsError();
}
if ($editForm->isValid($_POST)) { ... }
Form:
public function isValid($data)
{
$valid = parent::isValid($data);
if ($this->getElement('kind')->hasErrors()) {
$this->getElement('kind')->addError($this->_translate->translate('You can\'t change trial kind to debt.'));
$valid = false;
}
return $valid;
}
And this comment helped me.
As the title says, I tried calling the receive() function either of Form Element and the Adapter Object(not one after another of course). I printed the returned value - was 1 in both cases - which means receive() returned true.
The file was not found on the server though. I tried setting encrypt type of zend form to multipart/form-data - didn't help.
I'm totally clueless so any info is welcomed.
Calling receive() on transfer adapter: file location and upload name are constants.
$this->uploadName = $uploadName;
$this->upload = new Zend_File_Transfer_Adapter_Http();
$this->upload->setDestination($this->fileLocation);
...
$val = $this->upload->receive();
$quoteName = $this->upload->getFileName($this->uploadName);
$size = $this->upload->getFileSize($this->uploadName);
calling receive on form element:
//form creation - my form extends zend form
$staticForm = Srm_Form::getForm(my form,null,null,
my config);
$staticForm->setEnctype('multipart/form-data');
$staticForm->getElement(my file element name)->setDestination(my dest);
//calling receive
$form = Srm_Form::getForm(my form,null,null,my config);
$form->setEnctype('multipart/form-data');
if(!$form->isValid($_POST)){
print_r($form->getMessages());
}
// echo $form->getElement(my file element)->getValue();
$val = $form->getElement(my file element)->receive();
echo "bbbbbb".$val;
I should add that this code works when it is called after the file element is added to the form manually and not through use of a config file.
Okay, the problem was found -
The destination was not set for the file element (it was set manually for the transfer adapter in other place)when handling the submitted form.
I define a simple form with the Zend_Form_Element_File element
<?php
class Form_UploadForm extends Zend_Form
{
public function __construct($options = null)
{
parent::__construct($options);
$this->setMethod('post');
$this->setAttrib('enctype', 'multipart/form-data');
$decors = array(
array('ViewHelper'),
array('HtmlTag'),//array('tag'=>'table')),
array('Label', array('separator' => ' ')), // those unpredictable newlines
array('Errors', array('separator' => ' ')), // in the render output
);
$file = new Zend_Form_Element_File('file');
$file->setDestination('/a/b/c/upload');
$file->setLabel('Document File Path')
->setRequired(true)
->addValidator('NotEmpty');
$this->addElement($file);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Upload File');
$this->addElement($submit);
}
}
?>
My action method in the Controller is
function uploadAction()
{
$this->view->pageTitle = "Zend_Form File Upload Example";
$this->view->bodyCopy = "<p>Please fill out this form.</p>";
$form = new Form_UploadForm();
if ($this->_request->isPost())
{
$formData = $this->_request->getPost();
if ($form->isValid($formData))
{
try
{
$form->file->receive();
}
catch (Zend_File_Transfer_Exception $e)
{
throw new Exception('unable to recieve : '.$e->getMessage());
}
$uploadedData = $form->getValues();
//Zend_Debug::dump($form->file->getFileName(), 'tmp_file');
$this->processFile($form->file->getFileName());
}
else
{
$form->populate($formData);
}
}
$this->view->form = $form;
}
Note - i don't call the Zend_File_Transfer_Adapter_Http directly
The final piece is the view
<?php echo $this->form; ?>
Edit:
I think it is because the action is the same or something. I tried to modify the action using this:
function mytheme_user_profile_form($form) {
global $user;
$uid = $user->uid;
//print '<pre>'; print_r($form); print '</pre>';
$category = $form['_category']['#value'];
switch($category) {
case 'account':
$form['#action'] = '/user/'.$uid.'/edit?destination=user/'.$uid;
break;
case 'education':
$form['#action'] = '/user/'.$uid.'/edit/education?destination=user/'.$uid;
break;
case 'experience':
$form['#action'] = '/user/'.$uid.'/edit/experience?destination=user/'.$uid;
break;
case 'publications':
$form['#action'] = '/user/'.$uid.'/edit/publications?destination=user/'.$uid;
break;
case 'conflicts':
$form['#action'] = '/user/'.$uid.'/edit/conflicts?destination=user/'.$uid;
break;
}
//print '<pre>'; print_r($form); print '</pre>';
//print $form['#action'];
$output .= drupal_render($form);
return $output;
}
But, the form action, when the form is actually rendered is unchanged. They're all /user/%uid
Can I modify the form action?
I am including several different "categories" of the user profile form on one page, and the code will correctly output the forms I'm specifying. Each form is in a separate collapsible div.
My problem is twofold.
(1) The existing values for the fields aren't pre-populated and
(2) Clicking on "Save" for one section will result in a warning: Email field is required, regardless of which form you're actually saving
I am pretty sure that for problem #2, it is because the name of the button is the same in all cases, as is the form id.
print '<h3>– Account Settings</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'account'));
print '</div>';
print '<h3>– My Info</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'Personal'));
print '</div>';
print '<h3>– Experience</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'experience'));
print '</div>';
print '<h3>– Education</h3>';
print '<div class="expand">';
print(drupal_get_form('user_profile_form', $user, 'education'));
print '</div>';
Problem #1: ? Could you post the html source?
For problem #2:
OK, I'll step through the code here:
The validation handler for the user profile form (user_profile_form_validate()) calls
user_module_invoke('validate', $form_state['values'], $form_state['values']['_account'], $form_state['values']['_category']);
Which looks like
<?php
/**
* Invokes hook_user() in every module.
*
* We cannot use module_invoke() for this, because the arguments need to
* be passed by reference.
*/
function user_module_invoke($type, &$array, &$user, $category = NULL) {
foreach (module_list() as $module) {
$function = $module .'_user';
if (function_exists($function)) {
$function($type, $array, $user, $category);
}
}
}
?>
So, the validation handler for this form is going through every module looking for user hook functions and calling them with $type = 'validate'. (Note that 'category' param is optional here - contrib modules are not required to use it)
Let's look at user.module's user hook as an example to see what happens:
function user_user($type, &$edit, &$account, $category = NULL) {
if ($type == 'view') {
$account->content['user_picture'] = array(
'#value' => theme('user_picture', $account),
'#weight' => -10,
);
if (!isset($account->content['summary'])) {
$account->content['summary'] = array();
}
$account->content['summary'] += array(
'#type' => 'user_profile_category',
'#attributes' => array('class' => 'user-member'),
'#weight' => 5,
'#title' => t('History'),
);
$account->content['summary']['member_for'] = array(
'#type' => 'user_profile_item',
'#title' => t('Member for'),
'#value' => format_interval(time() - $account->created),
);
}
if ($type == 'form' && $category == 'account') {
$form_state = array();
return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
}
//<-- LOOK HERE -->
if ($type == 'validate' && $category == 'account') {
return _user_edit_validate((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'submit' && $category == 'account') {
return _user_edit_submit((isset($account->uid) ? $account->uid : FALSE), $edit);
}
if ($type == 'categories') {
return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
}
}
So, it is only supposed to validate if the category == 'account'
In the function _use_edit_validate, we find:
// Validate the e-mail address:
if ($error = user_validate_mail($edit['mail'])) {
form_set_error('mail', $error);
}
There's your error message.
Since that form is only supposed to validate when the category == 'account', and your problem (#2) seems to be that it always validates regardless of the category, maybe your forms are not being rendered as unique form instances? Drupal might be rendering a complete form each time, and just setting a hidden form value to whatever the category is (like in this form's definition function in user_pages.inc $form['_category'] = array('#type' => 'value', '#value' => $category);)
It would be helpful to see the actual html source output.
==EDIT 10-15-09 in response to updated question===
OK, it looks like your method (editing $form['#action'] manually in the theme layer) may not be possible (see this post for reference). If you want to alter the form action you need to write a custom module that implements hook_form_alter() (it won't work in a theme template file). This function allows you to modify how a form is rendered, in your case the user modification form. There are more details on form modification here.
I am not 100% sure that's what you want to do though; (since it looks like you already must create a module) perhaps you want to hook into hook_user() instead; this function "... allows modules to react when operations are performed on user accounts.". You may be able to react to the category in this function and block/allow whichever user changes you like.
However, if it's just email address validation that is the problem, and if you are dealing with existing users, why don't you just make sure the email address is set before you save?