Now I am workin on complex quiz App on Yii2. Here is MCQ test that contains more than 100 questions. I want to separate this questions into 5 form tabs (so that questions from 1 to 20 in tab1, from 21 to 40 in tab2 etc). Could someone explain what is the way to do this? So, there is only one model and one form submit.
I thought about using the buttflattery\yii2-formwizard. In the documentation, I have found the Single Model Across Steps tutorial, but it is not really suitable for my case because all questions are written in one field as many rows.
For now Answers Model is following:
class Answers extends ActiveRecord
{
public function rules(){
return[
[['id','question_id', 'option_id', 'user_id'], 'required'],
];
}
}
index view:
//start form
<?php $form = ActiveForm::begin([
'id' => 'my-form-id',
'action' => ['answers/save'],
'options' =>['class'=>['t-form']]
]);
?>
//foreach question:
<?php for ($i=0; $i<count($questions); $i++): ?>
<div class="input-title">
<?= Html::encode("{$questions[$i]->title}") ?>
</div>
<?php $options = Options::find()-> where
(['question_id'=>$questions[$i]->id]) ->all();
$options = ArrayHelper::map($options,'id', 'title');?>
//print options:
<div class="radio__wrapper">
<?= $form->field($model, 'option_id')->radioList(
$options,
['name'=>'Questions['.$questions[$i]->id.']',
'separator' => '<br>',
'required'=>true],)->label(false) ?>
</div>
//submit form
<?= Html::submitButton('Save', ['class' => 'submit']) ?>
<?php ActiveForm::end(); }
AnswersController:
public function actionSave(){
$request = \Yii::$app->request;
foreach($request->post('Questions') as $key=>$value) {
$model = new Answers();
$model->load($request->post());
$model->option_id = $value;
$model->question_id = $key;
$model->user_id = \Yii::$app->user->id;
$model->save(false);
}
if( $model->save(false)){
return $this->redirect(['result/index']);
}
}
If FormWizard is not suitable variant please explain me what is the most efficient way?
yii2-formwizard does provide you with a lot of options, which create a form wizard using the ActiveForm and Models.
Prominent Features
You can use a single model across all steps.
Separate model dedicated to every step.
Multiple models to a single step.
Disable/Enable validation.
Customize & order form fields.
Tabular Steps with Add Row button to add fields dynamically like Adress book.
Form Persistence (saves the un-saved form to be restored later using localstorage).
Preview Step ( Previews all the form input with labels as the last step, and navigates to the step when clicked).
Multiple Themes
Demos
You can see the DEMOS with all available variations and for Documentation use the Wiki
SETUP
use composer to install the extension
php composer.phar require buttflattery/yii2-formwizard "#dev"
or add into the composer.json file under require section
"buttflattery/yii2-formwizard":"#dev"
Sample Code
use buttflattery\formwizard\FormWizard;
$shootsModel = new Shoots();
$shootTagModel = new ShootTag();
echo FormWizard::widget([
'steps'=>[
[
'model'=>$shootsModel,
'title'=>'My Shoots',
'description'=>'Add your shoots',
'formInfoText'=>'Fill all fields'
],
[
'model'=> $shootTagModel,
'title'=>'My Shoots',
'description'=>'Add your shoots',
'formInfoText'=>'Fill all fields'
],
]
]);
Related
In Yii2 I'm trying to construct hidden input
echo $form->field($model, 'hidden1')->hiddenInput()->label(false);
But I also need it to have some value option, how can I do that ?
Use the following:
echo $form->field($model, 'hidden1')->hiddenInput(['value'=> $value])->label(false);
Changing the value here doesn't make sense, because it's active field. It means value will be synchronized with the model value.
Just change the value of $model->hidden1 to change it. Or it will be changed after receiving data from user after submitting form.
With using non-active hidden input it will be like that:
use yii\helpers\Html;
...
echo Html::hiddenInput('name', $value);
But the latter is more suitable for using outside of model.
simple you can write:
<?= $form->field($model, 'hidden1')->hiddenInput(['value'=>'abc value'])->label(false); ?>
You can do it with the options
echo $form->field($model, 'hidden1',
['options' => ['value'=> 'your value'] ])->hiddenInput()->label(false);
you can also do this
$model->hidden1 = 'your value';// better put it on controller
$form->field($model, 'hidden1')->hiddenInput()->label(false);
this is a better option if you set value on controller
$model = new SomeModelName();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->group_id]);
} else {
$model->hidden1 = 'your value';
return $this->render('create', [
'model' => $model,
]);
}
Like This:
<?= $form->field($model, 'hidden')->hiddenInput(['class' => 'form-control', 'maxlength' => true,])->label(false) ?>
You can use this code line in view(form)
<?= $form->field($model, 'hidden1')->hiddenInput(['value'=>'your_value'])->label(false) ?>
Please refere this as example
If your need to pass currant date and time as hidden input :
Model attribute is 'created_on' and its value is retrieve from date('Y-m-d H:i:s') ,
just like:"2020-03-10 09:00:00"
<?= $form->field($model, 'created_on')->hiddenInput(['value'=>date('Y-m-d H:i:s')])->label(false) ?>
<?= $form->field($model, 'hidden_Input')->hiddenInput(['id'=>'hidden_Input','class'=>'form-control','value'=>$token_name])->label(false)?>
or
<input type="hidden" name="test" value="1" />
Use This.
You see, the main question while using hidden input is what kind of data you want to pass?
I will assume that you are trying to pass the user ID.
Which is not a really good idea to pass it here because field() method will generate input
and the value will be shown to user as we can't hide html from the users browser. This if you really care about security of your website.
please check this link, and you will see that it's impossible to hide value attribute from users to see.
so what to do then?
See, this is the core of OOP in PHP.
and I quote from Matt Zandstr in his great book PHP Objects, Patterns, and Practice fifth edition
I am still stuck with a great deal of unwanted flexibility, though. I rely on the client coder to change a ShopProduct object’s properties from their default values. This is problematic in two ways. First, it takes five lines to properly initialize a ShopProduct object, and no coder will thank you for that. Second, I have no way of ensuring that any of the properties are set when a ShopProduct object is initialized. What I need is a method that is called automatically when an object is instantiated from a class.
Please check this example of using __construct() method which is mentioned in his book too.
class ShopProduct {
public $title;
public $producerMainName;
public $producerFirstName;
public $price = 0;
public function __construct($title,$firstName,$mainName,$price) {
$this->title = $title;
$this->producerFirstName = $firstName;
$this->producerMainName = $mainName;
$this->price = $price;
}
}
And you can simply do this magic.
$product1 = new ShopProduct("My Antonia","Willa","Cather",5.99 );
print "author: {$product1->getProducer()}\n";
This produces the following:
author: Willa Cather
In your case it will be something semilar to this, every time you create an object just pass the user ID to the user_id property, and save yourself a lot of coding.
Class Car {
private $user_id;
//.. your properties
public function __construct($title,$firstName,$mainName,$price){
$this->user_id = \Yii::$app->user->id;
//..Your magic
}
}
I know it is old post but sometimes HTML is ok :
<input id="model-field" name="Model[field]" type="hidden" value="<?= $model->field ?>">
Please take care
id : lower caps with a - and not a _
name : 1st letter in caps
I am new to Zend Framework 2
I want to add a form to every page (a login box for example) that functions as it does when in its own module ie. so it validates and there is no need to redirect back from the module after the action.
I have looked at various things such as view helpers and action helpers, I have not posted any code as it may just add confusion
I am looking for a guide on how to achieve this as I currently am confused as to how this would be best implemented
This is certainly a use case for a view helper. This provides a piece of logic reusable in multiple views across different modules.
Take for example the login form,. you might want to return a Zend\Form\Form instance when you call the helper. So first, create the helper:
namespace MyLogin\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\Form\Form;
class LoginForm extends AbstractHelper
{
public function __invoke()
{
$form = new Form;
$url = $this->getView()->url('user/login');
$form->setAttribute('action', $url);
$form->add([
'name' => 'username',
]);
$form->add([
'type' => 'password',
'name' => 'password',
]);
return $form;
}
}
Then you register this view helper under the name "loginForm" in your config:
'view_helpers' => [
'invokables' => [
'loginForm' => 'MyLogin\View\Helper\LoginForm',
],
],
Now you can use the helper in your views:
<?php $form = $this->loginForm() ?>
<?= $this->form()->openTag($form)?>
<?= $this->formRow($form->get('username'))?>
<?= $this->formRow($form->get('password'))?>
<button type="submit" value="Login">
<?= $this->form()->closeTag()?>
Of course you can apply any login in your form, whatever you need to be reusable:
Return a form instance so your view can render the form
Return a rendered view already in the helper so your view does not need to render
Set all kind of options to the form
Etc
I recently was introduced into the Yii Framework and am presently developing a web application system for my company. However I noticed that when creating the model in order to give the connection to the respective table, it only allows to choose one relation at a time. However I need to connect two separate tables from the same database with a single form.
Any ideas on how this could be accomplished?
Inside the models you can see the below function,
/**
* #return array relational rules.
*/
public function relations()
{
return array(
);
}
in this you can add relations. like
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'comments' => array(self::HAS_MANY, 'Comments', 'blog_post_id'),
etc.,
If your database engine is in Innodb and tables have foreign key relations , then the relations will be automatically generated while creating the models.
For more info read this
you can use any number of relations.
=============================================
And after second reading, I think you were asking about getting objects of two models into one form? for that you can generate objects of each model in controller and pass those objects to view via render or renderPartial function
e.g.,
$this->render('admin',array(
'model'=>$model,
'model2'=>$model2,
));
and inside the view use model and model2 for respective fields
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'sample-form',
'enableAjaxValidation'=>false,
)); ?>
.....
<?php echo $form->labelEx($model,'column'); ?>
<?php echo $form->textField($model,'column'); ?>
<?php echo $form->error($model,'column'); ?>
<?php echo $form->labelEx($model2,'column'); ?>
<?php echo $form->textField($model2,'column'); ?>
<?php echo $form->error($model2,'column'); ?>
....
inside the controller function use something like below(say for saving data)
$model->attributes=$_POST['ModelOnesName'];
$valid = $model->validate();
$model2->attributes = $_POST['ModelTwosName'];
$valid = $model2->validate() && $valid; //if need validation checks
if($valid)
{
$model->save();
$model2->save();
}
Let's say I have a Zend_Form form that has a few text fields, e.g:
$form = new Zend_Form();
$form->addElement('text', 'name', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
$form->addElement('text', 'surname', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
After rendering it I have following HTML markup (simplified):
<div id="people">
<div class="person">
<input type="text" name="name[]" />
<input type="text" name="surname[]" />
</div>
</div>
Now I want to have the ability to add as many people as I want. I create a "+" button that in Javascript appends next div.person to the container. Before I submit the form, I have for example 5 names and 5 surnames, posted to the server as arrays. Everything is fine unless somebody puts the value in the field that does not validate. Then the whole form validation fails and when I want to display the form again (with errors) I see the PHP Warning:
htmlspecialchars() expects parameter 1 to be string, array given
Which is more or less described in ticket: http://framework.zend.com/issues/browse/ZF-8112
However, I came up with a not-very-elegant solution. What I wanted to achieve:
have all fields and values rendered again in the view
have error messages only next to the fields that contained bad values
Here is my solution (view script):
<div id="people">
<?php
$names = $form->name->getValue(); // will have an array here if the form were submitted
$surnames= $form->surname->getValue();
// only if the form were submitted we need to validate fields' values
// and display errors next to them; otherwise when user enter the page
// and render the form for the first time - he would see Required validator
// errors
$needsValidation = is_array($names) || is_array($surnames);
// print empty fields when the form is displayed the first time
if(!is_array($names))$names= array('');
if(!is_array($surnames))$surnames= array('');
// display all fields!
foreach($names as $index => $name):
$surname = $surnames[$index];
// validate value if needed
if($needsValidation){
$form->name->isValid($name);
$form->surname->isValid($surname);
}
?>
<div class="person">
<?=$form->name->setValue($name); // display field with error if did not pass the validation ?>
<?=$form->surname->setValue($surname);?>
</div>
<?php endforeach; ?>
</div>
The code work, but I want to know if there is an appropriate, more comfortable way to do this? I often hit this problem when there is a need for a more dynamic - multivalue forms and have not find better solution for a long time.
Having no better idea, I have created a view helper that handles the logic presented above. It can be found here.
If the helper is available in the view, it can be used in the following way (with the form from the question):
<?=
$this->formArrayElements(
array($form->name, $form->surname),
'partials/name_surname.phtml'
);
?>
The contents of the application/views/partials/name_surname.phtml partial view are:
<div class="person">
<?= $this->name ?>
<?= $this->surname ?>
</div>
The fields are rendered according to the posted form and validation messages are shown only next to the values that failed validation.
The helper's code is far from perfect (I just rewrote the idea from the question) but is easy to use and can be considered as good starting point.
I have a product search page with the form below. The search result is displayed on the same page with search bar at the top.
echo $this->Form->create('Searches', array('action'=>'products', 'type' => 'get', 'name' => 'textbox1'));
echo $form->input($varName1, array('label' => false));
echo $form->end('Locate');
I also have a little box next to the search result that allows (it doesn't work yet) the user to flag using checkboxes a product and accordingly update its database (table products and using model Product) with a button click. Note that I have a Searches controller for this search page.
<form method="link" action="/myapp/product/test_update_db>
<label><input type="checkbox" name="flag1" <?php echo $preCheckBox1; ?>>Flag 1</input></label>
<label><input type="checkbox" name="flag2" <?php echo $preCheckBox2; ?>>Flag 2</input></label>
<input type="submit" value="Update">
</form>
I'm having difficulty with this approach figuring out how to perform this check-box-and-DB-update routine. I'm getting to the link I'd like to go (/myapp/product/test_update_db), but I don't know how to take variables flag1 and flag2, along with row ID of this result ($results['Product']['id'])) to the new page.
Could someone guide me on how to perform this neatly? Is this general approach correct? If not, what route should I be taking? I'd prefer not to use javascript at this time, if possible.
EDIT: I think I can make this work if I use the URL for passing data.. but I'd still like to know how this could be done "under the hood" or in MVC. I feel like I'm hacking at the CakePHP platform.
UPDATE: So, I ended up using the URL parameters for retrieving information pieces like flag1 and flag2. I'm still looking for an alternative method.
To see where your is-checkbox-checked data is located, do the following in your controller:
// Cake 2.0+
debug($this->request->data);
// previous versions
debug($this->data);
If you want to pass data to your search controller from the current page, you can always add the data to your form:
$this->input
(
'Product.id',
array
(
'type' => 'hidden',
'value' => $yourProductId
)
);
I ended up using information embedded in the URL for getting submission data. Something like below..
In Products controller, when the form with flag1 and flag2 are submitted:
public function test_update_db() {
// Get variables from URL, if any, and save accordingly
$result = $this->Product->updateProduct($this->params['url'], 'url');
if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}
else {
$this->Session->setFlash('Update was unsuccessful!', 'default', array('class' => 'error'));
$this->redirect($this->referer());
}
}
This works for doing what I needed to do. I feel like there's a more proper way to do this though.
if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}