How to handle multiple forms in the same view? - forms

I have a default form for my specific view.
Through an element I (dinamically) include another view (using view extension) in order to provide an upload form.
My problem is that the second form seems to submit the first one.
Default form
<div class="content-box-content">
<?php
echo $this->Form->create("WebSubject", array(
'inputDefaults' => array(
'error' => array(
'attributes' => array(
'wrap' => 'span',
'class' => 'input-notification error png_bg'
)
)
)
));
?>
<?=$this->Form->input('id', array('type' => 'hidden'))?>
<?=$this->Form->input('title', array('class' => "text-input small-input", 'label' => 'Denumire'))?>
<?=$this->Form->input('description', array('type' => 'textarea', 'label' => 'Descriere', 'id' => 'description'))?>
<?=$this->Form->input('description_long', array('type' => 'textarea', 'label' => 'Continut', 'id' => 'description_long'))?>
<?=$this->Form->submit('Salveaza', array('class' => "button"))?>
</div>
This way I include the element
<div class="tab-content default-tab" id="fotoUploadTab">
<?php
echo $this->element('file_upload_form', array(
'view' => 'upload_admin',
'webFileType' => 'image',
'redirect' => $SHT['here']
)
);
?>
<div class="tab-content default-tab">
Lista imagini
</div>
</div>
Element code
<?php
$view = (isset($view)) ? $view : "upload_admin";
$webFileType = (isset($webFileType)) ? $webFileType : "image";
$redirect = (isset($redirect)) ? $redirect : "/";
?>
<?php
$this->extend("/WebFiles/".$view);
?>
Extended View code
<div class="tab-content default-tab">
<?php echo $this->Form->create("WebFile", array('action' => '/', 'type' => 'file')); ?>
<input type="hidden" name="redirect" value="" />
<?php echo $this->Form->input('entity_id', array('type' => 'hidden')); ?>
<?php echo $this->Form->input('entity_table_name', array('type' => 'hidden')); ?>
<?php echo $this->Form->input('type', array('type' => 'hidden')); ?>
<?php echo $this->Form->input('title', array('class' => "text-input small-input", 'label' => 'Denumire')); ?>
<?php echo $this->Form->input('description', array('class' => "text-input small-input", 'label' => 'Descriere')); ?>
<?php echo $this->Form->submit('Upload', array('class' => 'button')); ?>
</div>
As seen in the last snippet, I tried to force the last form by providing an action url, but on submiting it, it sends data as the first one does.
How should I handle this ?
Thank you!

If you just want to have both forms, the one from parent and the other from the child view/element make sure you call $this->Form->end() in both templates and that you are not nesting a form inside the other. Probably, in your case, just by adding end() to both forms will solve your issue.
As a side note, you cannot have a parent view opening a Form with $this->Form->create() and inject fields into it using the child view, basically because you need create() to be called before any input is rendered and parent views are rendered after the child is executed.

Related

In Cakephp 3.6 How do I get the size and type of an image to send it by form?

How do I get the size and type of an image to send it by form?
Well that, I want to save in the table of images the size and type of an image that is uploaded through a form. With Ajax, I can recover those data, but to pass them to PHP I can only do when sending the form, also directly with PHP:
if ($this->request->is('post')) {
$isData = $this->request->getdata();
$imagene->imagen = $isData['image_path'];
$imagene->tipo = $isData['type']
$imagene->tamano = $isData['size'];
...
But I want to do it before sending the form, which is when the insertion is done in the database.
Form:
<?= $this->Form->create($imagene, ['novalidate', 'id' => 'addimageform', 'class' => 'form addimageform']); ?>
<?= $this->Form->control('imagen', ['type' => 'file', 'class' => 'imagen-addimage']); ?>
<?= $this->Form->hidden('tipo', ['value' => $tipo, 'class' => 'tipo-addimage']); ?>
<?= $this->Form->hidden('$tipo', ['value' => $size, 'class' => 'tamano-addimage']); ?>
<?= $this->Form->button('Subir imagen', ['id' => 'submit', 'class' => 'submit-addimage']); ?>
<?= $this->Form->button('Omitir', ['id' => 'omitir', 'class' => 'omitir-addimage', 'redirect' => ['controller' => 'administracion', 'action' => 'index']]); ?>
<?= $this->Form->end(); ?>
Now I see that if I do a debug of $isData, the field: "imagen" does not appear:
'tabla' => 'users',
'id_tabla' => '22',
'tipo' => '',
'tamano' => ''
UPDATING
I've changed things in the form and the controller:
form:
<?= $this->Form->create($imagene, ['enctype' => 'multipart/form-data', 'novalidate', 'id' => 'addimageform', 'class' => 'form addimageform']); ?>
<?= $this->Form->control('imagen', ['type' => 'file', 'class' => 'imagen-addimage']); ?>
<div class="centrar-submit">
<?= $this->Form->button('Subir imagen', ['id' => 'submit', 'class' => 'submit-addimage']); ?>
<?= $this->Form->button('Omitir', ['id' => 'omitir', 'class' => 'omitir-addimage', 'redirect' => ['controller' => 'administracion', 'action' => 'index']]); ?>
</div>
<?= $this->Form->end(); ?>
Controller:
public function add($table, $idTable) {
$imagene = $this->Imagenes->newEntity();
if ($this->request->is('post')) {
$isData = $this->request->getdata();
debug($this->request->getData('imagen')); // <---- Is null
debug($isData); // <---- Is empty
...
Why? I don't know.
You should mentioned what type of form you used. That is file type you should use
echo $this->Form->create($article, ['type' => 'file']);
Hope this help you.

put submit button in Tabs::widget in yii2

I have a Tabs::widget that all settings are located in different tabs in a ActiveForm and admin can set config in each tab and once submit.(multiple forms in one widget )
in setting view :
<?php $form = ActiveForm::begin(); ?>
<?php
echo \yii\jui\Tabs::widget([
'headerOptions' => ['class' => 'tabs'],
'itemOptions' => ['tag' => 'div'],
'items' => [
[
'label' => 'serverSetting',
'content' => $this->render('serverSetting', ['model' => $model, 'form' => $form]),
'active' => true
],
[
'label' => 'emailSetting',
'content' => $this->render('emailSetting', ['model' => $model, 'form' => $form]),
],
[
'label' => 'smsSetting',
'content' => $this->render('smsSetting', ['model' => $model, 'form' => $form]),
],
],
]);
?>
<div class="btnForm">
<?= Html::submitButton(Yii::t('app', 'ثبت', ['class' => 'btn btn-primary', 'name' => ''])) ?>
</div>
<?php ActiveForm::end(); ?>
in view of one of the tabs (smsServer view):
<?php
use yii\helpers\Html;
use app\components\ActiveForm;
?>
<div class="user-form">
<?= $form->field($model, 'login')->textInput(['placeholder' => 'host']) ?>
<?= $form->field($model, 'password1')->textInput(['placeholder' => 'username']) ?>
<?= $form->field($model, 'wsdl')->textInput(['placeholder' => 'password']) ?>
<?= $form->field($model, 'from1')->textInput(['placeholder' => 'port']) ?>
</div>
in controller :
public function actionSetting()
{
$model = new Setting();
$model->setAttributes(Yii::$app->params, false);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
file_put_contents(Yii::getAlias('#app/config') . DIRECTORY_SEPARATOR . 'extra_params.php', base64_encode(serialize($model->attributes)));
}
return $this->render('setting', [
'model' => $model,
]);
}
My form and submit button do not work. where is my problem?
Seems you don't have an ActiveForm::end()
.......
<div class="btnForm">
<?= Html::submitButton(Yii::t('app', 'ثبت', ['class' => 'btn btn-primary'])) ?>
</div>
<?php ActiveForm::end(); ?>
and for debugging don't suppress the name of button
So I found my answer. My validation was false.
in rule():
[['login', 'wsdl', 'password','from1'], 'required',],
But I post form without filled all fields.
I changed to
[['login',], 'required',],
[['login', 'wsdl', 'password',from1'], 'safe',],

CakePHP send form depending on data

Hello I'm fairly new to cakephp, I've just done two tutorials hehehe but now I'm working on it and I have a register form in which I register a user, But depending on the type of the user, more models will be shown and sent to the controller to register it. I was wondering which is the best practice to do this? Do it in separate forms, or sending the information depending on the user type?
Here is my View:
<?php echo $this->Form->create('User', array('type' => 'file')); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php
echo $this->Form->file('User.user_image');
echo $this->Form->input('User.name', array( 'label' => 'Nombre' ));
echo $this->Form->input('User.last_name', array( 'label' => 'Apellidos' ));
echo $this->Form->input('User.email', array( 'label' => 'E-Mail' ));
echo $this->Form->input('User.password', array( 'label' => 'Contraseña' ));
echo $this->Form->input('User.phone', array( 'label' => 'Telefono' ));
//echo $this->Form->input('created_ip_connection');
//echo $this->Form->input('last_ip_connection');
echo $this->Form->input('User.group_id', array('empty' => 'Elige un rol', 'label' => 'Rol de Usuario'));
?>
<div style="display: none;" id="companyAdd">
<legend><?php echo __('Datos de Compañia'); ?></legend>
<?php
echo $this->Form->input('Address.exterior_number', array( 'label' => 'Numero Exterior' ));
echo $this->Form->input('Address.internal_number', array( 'label' => 'Numero Interior' ));
echo $this->Form->input('Address.street', array( 'label' => 'Calle' ));
echo $this->Form->input('Address.suburby', array( 'label' => 'Colonia' ));
echo $this->Form->input('Address.country_id', array('empty' => 'Selecciona País', 'label' => 'Pais'));
echo $this->Form->input('Address.state_id', array('empty' => 'Selecciona País', 'label' => 'Estado'));
echo $this->Form->input('Address.city_id', array('empty' => 'Selecciona Estado', 'label' => 'Municipio'));
echo $this->Form->input('Company.name', array( 'label' => 'Nombre de Compañia' ));
echo $this->Form->input('Company.description', array( 'label' => 'Descripción de Compañia' ));
echo $this->Form->input('Company.bank_acc', array( 'label' => 'Cuenta de Banco' ));
echo $this->Form->input('Company.rfc', array( 'label' => 'RFC' ));
?>
</div>
<div style="display: none;" id="userAdd">
<legend><?php echo __('Datos de Comprador/Proveedor'); ?></legend>
<?php
echo $this->Form->input('Buyer.company_id', array('empty' => 'Elige Comapñia', 'label' => 'Compañia'));
?>
</div>
</fieldset>
<?php echo $this->Form->end(__('Registrar')); ?>
In this if the user has a certain group_id the companyAdd div will be visible for editing, if its another the userAdd div will be shown and if hes admin only the info of the user will be sent. When I try to submit this, the form wont let me cause the inputs for company and user are necessary. How can I control which data is sent depending on the group_id of the user?
The models goes as follow
User hasOne Buyer
User hasOne Provider(NormalUsers)
User hasOne Comapny
By the way Comapny has an address too thats why Address is another model
In the controller I'm thinking over use saveAll or a save per model sent.
Any suggestions or tutorials I can see how this is make ? or Best practices?

Zend Framework 2 formInput or formElement ID tag not rendering

In Zend framework 2, when I use the view's formRow method like so
$this->formRow($form->get('product_name'));
it will generate HTML like this
<label for="product_name">
<span>Name</span>
<input type="text" id="product_name" name="product_name">
</label>
but if I use formInput
<div class="control-group">
<?php echo $this->formLabel($form->get('product_name')->setLabelAttributes(array('class'=>'control-label'))); ?>
<div class="controls">
<?php echo $this->formInput($form->get('product_name')); ?>
</div>
</div>
$this->formInput($form->get('product_name'));
i don't get the id tag
<input type="" name="product_name">
I've tried with formElement with same results.
How can I get it to render just the input with all attributes and values?
This is how my Zend Framework 2 View looks like (simplified)
<?php echo $this->form()->openTag($form); ?>
<div class="control-group">
<?php echo $this->formLabel($form->get('product_name')->setLabelAttributes(array('class'=>'control-label'))); ?>
<div class="controls"><?php echo $this->formInput($form->get('product_name')); ?></div>
</div>
<div class="control-group">
<div class="controls"><?php echo $this->formSubmit($form->get('submit')); ?></div>
</div>
<?php echo $this->form()->closeTag(); ?>
and the Zend Framework 2 Form
<?php
namespace Product\Form;
use Zend\Form\Form;
class ProductForm extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('product');
$this->setAttribute('method', 'post');
$this->setAttribute('class','form-horizontal');
$this->add(array(
'name' => 'product_name',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Name',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Save',
'id' => 'submitbutton',
'class'=>'btn btn-success btn-large'
),
));
}
}
?>
Change:
$this->add(array(
'name' => 'product_name',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Name',
),
));
to:
$this->add(array(
'name' => 'product_name',
'attributes' => array(
'type' => 'text',
'id' => 'product_name',
),
'options' => array(
'label' => 'Name',
),
));
Actually this code:
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'product_name',
'attributes' => array(
'id' => 'product_name',
'class' => 'span3',
),
'options' => array(
'label' => 'Your label',
),
));
could be used correctly by a css renderer like Bootstrap due to fact that the $this->formRow(...) form helper will produce:
<label for="product_name">Your label</label><input type="text" name="product_name" class="span3" id="product_name" value="">
which is more readable than the original formRow output (without id neither class attributes)

How do I use ViewScripts on Zend_Form File Elements?

I am using this ViewScript for my standard form elements:
<div class="field" id="field_<?php echo $this->element->getId(); ?>">
<?php if (0 < strlen($this->element->getLabel())) : ?>
<?php echo $this->formLabel($this->element->getName(), $this->element->getLabel());?>
<?php endif; ?>
<span class="value"><?php echo $this->{$this->element->helper}(
$this->element->getName(),
$this->element->getValue(),
$this->element->getAttribs()
) ?></span>
<?php if (0 < $this->element->getMessages()->length) : ?>
<?php echo $this->formErrors($this->element->getMessages()); ?>
<?php endif; ?>
<?php if (0 < strlen($this->element->getDescription())) : ?>
<span class="hint"><?php echo $this->element->getDescription(); ?></span>
<?php endif; ?>
</div>
Trying to use that ViewScript alone results in an error:
Exception caught by form: No file
decorator found... unable to render
file element
Looking at this FAQ revealed part of my problem, and I updated my form element decorators like this:
'decorators' => array(
array('File'),
array('ViewScript', array('viewScript' => 'form/field.phtml'))
)
Now it's rendering the file element twice, once within my view script, and extra elements with the file element outside my view script:
<input type="hidden" name="MAX_FILE_SIZE" value="8388608" id="MAX_FILE_SIZE" />
<input type="hidden" name="UPLOAD_IDENTIFIER" value="4b5f7335a55ee" id="progress_key" />
<input type="file" name="upload_file" id="upload_file" />
<div class="field" id="field_upload_file">
<label for="upload_file">Upload File</label>
<span class="value"><input type="file" name="upload_file" id="upload_file" /></span>
</div>
Any ideas on how to handle this properly with a ViewScript?
UPDATE: Based on Shaun's solution, here's my final code:
Form Element:
$this->addElement('file', 'upload_file', array(
'disableLoadDefaultDecorators' => true,
'decorators' => array('File', array('ViewScript', array(
'viewScript' => '_form/file.phtml',
'placement' => false,
))),
'label' => 'Upload',
'required' => false,
'filters' => array(),
'validators' => array(array('Count', false, 1),),
));
View Script:
<?php
$class .= 'field ' . strtolower(end(explode('_',$this->element->getType())));
if ($this->element->isRequired()) {
$class .= ' required';
}
if ($this->element->hasErrors()) {
$class .= ' errors';
}
?>
<div class="<?php echo $class; ?>" id="field_<?php echo $this->element->getId(); ?>">
<?php if (0 < strlen($this->element->getLabel())): ?>
<?php echo $this->formLabel($this->element->getFullyQualifiedName(), $this->element->getLabel());?>
<?php endif; ?>
<span class="value"><?php echo $this->content; ?></span>
<?php if ($this->element->hasErrors()): ?>
<?php echo $this->formErrors($this->element->getMessages()); ?>
<?php endif; ?>
<?php if (0 < strlen($this->element->getDescription())): ?>
<p class="hint"><?php echo $this->element->getDescription(); ?></p>
<?php endif; ?>
</div>
The answer is relatively simple as it happens. All you need do is specify the File decorator first, create a specific view script for the file input and specify false for the placement in the viewScript decorator arguments, this will effectively inject the output from the File decorator into the viewScript decorator e.g.
$element->setDecorators(array('File', array('ViewScript', array('viewScript' => 'decorators/file.phtml', 'placement' => false))));
Then in the new file element view script you simply echo $this->content in the script where you'd like the file input markup to be placed. Here's an example from a recent project, so ignore the markup if it looks a little odd, hopefully it will illustrate the point.
<label for="<?php echo $this->element->getName(); ?>" class="element <?php if ($this->element->hasErrors()): ?> error<?php endif; ?>" id="label_<?php echo $this->element->getName(); ?>">
<span><?php echo $this->element->getLabel(); ?></span>
<?php echo $this->content; ?>
<?php if ($this->element->hasErrors()): ?>
<span class="error">
<?php echo $this->formErrors($this->element->getMessages()); ?>
</span>
<?php endif; ?>
</label>
When rendered you will see this html for the element
<label for="photo" class="element" id="label_photo">
<span>Photo</span>
<input type="hidden" name="MAX_FILE_SIZE" value="6291456" id="MAX_FILE_SIZE">
<input type="file" name="photo" id="photo">
</label>
This is not a simple or ideal solution because it requires an extension of the File decorator... but it's rather frustrating that they didn't make the effort to separate the hidden element generation logic from the file input generation logic. I'm not sure if the file view helper handles the issue of an element being an array (that seems to be the reason they did it this way.)
Extension of File Decorator:
(the commented out part is what causes the extra input to be generated.)
<?php
class Sys_Form_Decorator_File extends Zend_Form_Decorator_File {
public function render($content) {
$element = $this->getElement();
if (!$element instanceof Zend_Form_Element) {return $content;}
$view = $element->getView();
if (!$view instanceof Zend_View_Interface) {return $content;}
$name = $element->getName();
$attribs = $this->getAttribs();
if (!array_key_exists('id', $attribs)) {$attribs['id'] = $name;}
$separator = $this->getSeparator();
$placement = $this->getPlacement();
$markup = array();
$size = $element->getMaxFileSize();
if ($size > 0) {
$element->setMaxFileSize(0);
$markup[] = $view->formHidden('MAX_FILE_SIZE', $size);
}
if (Zend_File_Transfer_Adapter_Http::isApcAvailable()) {
$apcAttribs = array('id' => 'progress_key');
$markup[] = $view->formHidden('APC_UPLOAD_PROGRESS', uniqid(), $apcAttribs);
}
else if (Zend_File_Transfer_Adapter_Http::isUploadProgressAvailable()) {
$uploadIdAttribs = array('id' => 'progress_key');
$markup[] = $view->formHidden('UPLOAD_IDENTIFIER', uniqid(), $uploadIdAttribs);
}
/*
if ($element->isArray()) {
$name .= "[]";
$count = $element->getMultiFile();
for ($i = 0; $i < $count; ++$i) {
$htmlAttribs = $attribs;
$htmlAttribs['id'] .= '-' . $i;
$markup[] = $view->formFile($name, $htmlAttribs);
}
}
else {$markup[] = $view->formFile($name, $attribs);}
*/
$markup = implode($separator, $markup);
switch ($placement) {
case self::PREPEND: return $markup . $separator . $content;
case self::APPEND:
default: return $content . $separator . $markup;
}
}
}
?>
Form setup in controller action:
$form = new Zend_Form();
$form->addElement(new Zend_Form_Element_File('file'));
$form->file->setLabel('File');
$form->file->setDescription('Description goes here.');
$decorators = array();
$decorators[] = array('File' => new Sys_Form_Decorator_File());
$decorators[] = array('ViewScript', array('viewScript' => '_formElementFile.phtml'));
$form->file->setDecorators($decorators);
$this->view->form = $form;
In action view:
<?php echo $this->form; ?>
In element script:
<div class="field" id="field_<?php echo $this->element->getId(); ?>">
<?php if (0 < strlen($this->element->getLabel())) : ?>
<?php echo $this->formLabel($this->element->getName(), $this->element->getLabel());?>
<?php endif; ?>
<span class="value">
<?php
echo $this->{$this->element->helper}(
$this->element->getName(),
$this->element->getValue(),
$this->element->getAttribs()
);
?>
</span>
<?php if (0 < $this->element->getMessages()->length) : ?>
<?php echo $this->formErrors($this->element->getMessages()); ?>
<?php endif; ?>
<?php if (0 < strlen($this->element->getDescription())) : ?>
<span class="hint"><?php echo $this->element->getDescription(); ?></span>
<?php endif; ?>
</div>
Output should be:
<form enctype="multipart/form-data" action="" method="post">
<dl class="zend_form">
<input type="hidden" name="MAX_FILE_SIZE" value="134217728" id="MAX_FILE_SIZE" />
<div class="field" id="field_file">
<label for="file">File</label>
<span class="value"><input type="file" name="file" id="file" /></span>
<span class="hint">Description goes here.</span>
</div>
</dl>
</form>
A problem with this solution is that the hidden elements don't render within the viewscript; this might be a problem if you're using the div as a selector in a client-side script...
What I've realized is, a custom decorator class will handle most fields except File fields.
Make sure your class implements the interface like so:
class CC_Form_Decorator_Pattern
extends Zend_Form_Decorator_Abstract
implements Zend_Form_Decorator_Marker_File_Interface
This worked for me.
This helped me fix my problem. I adjusted the code to wrap the file element inside a table. To make it work, simply remove the label from the viewdecorator and add the file element as follows:
$form->addElement('file', 'upload_file', array(
'disableLoadDefaultDecorators' => true,
'decorators' => array(
'Label',
array(array('labelTd' => 'HtmlTag'), array('tag' => 'td', 'class' => 'labelElement')),
array(array('elemTdOpen' => 'HtmlTag'), array('tag' => 'td', 'class' => 'dataElement','openOnly' => true, 'placement' => 'append')),
'File',
array('ViewScript', array(
'viewScript' => 'decorators/file.phtml',
'placement' => false,
)),
array(array('elemTdClose' => 'HtmlTag'), array('tag' => 'td', 'closeOnly' => true, 'placement' => 'append')),
array(array('row' => 'HtmlTag'), array('tag' => 'tr'))
),
'label' => 'Upload',
'required' => false,
'filters' => array(),
'validators' => array(array('Count', false, 1), ),
));
I've found a work-around that avoids the ViewScript altogether.
First, the element definition:
$this->addElement('file', 'upload_file', array(
'disableLoadDefaultDecorators' => true,
'decorators' => array(
'File',
array(array('Value'=>'HtmlTag'), array('tag'=>'span','class'=>'value')),
'Errors',
'Description',
'Label',
array(array('Field'=>'HtmlTag'), array('tag'=>'div','class'=>'field file')),
),
'label' => 'Upload File',
'required' => false,
'filters' => array('StringTrim'),
'validators' => array(),
));
Second, after the form class has been instantiated, I mimic the behavior of my ViewScript:
$field = $form->getElement('upload_file');
$decorator = $field->getDecorator('Field');
$options = $decorator->getOptions();
$options['id'] = 'field_' . $field->getId();
if ($field->hasErrors()) {
$options['class'] .= ' errors';
}
$decorator->setOptions($options);
I guess that I should look into class-based decorators. Maybe there's more flexibility there?
The easiest thing to do is to add no markup at all to the output in your custom File Decorator:
class Custom_Form_Decorator_File extends Zend_Form_Decorator_File {
public function render($content) {
return $content;
}
}
now you can do whatever you want in your viewscript for this file element (output the file input field and all hidden fields you need on your own).
Just in case you have followed #Shaun's answer and you are still getting the error: make sure that you've disabled default decorators for the element in question (take look at line 2):
$this->addElement('file', 'upload_file', array(
'disableLoadDefaultDecorators' => true,
'decorators' => array('File', array('ViewScript', array(
'viewScript' => '_form/file.phtml',
'placement' => false,
))),
'label' => 'Upload',
'required' => false,
'filters' => array(),
'validators' => array(array('Count', false, 1),),
));