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();
}
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
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'
],
]
]);
I'm working on a Yii project with a database, that contains a table, where almost all it's data is saved in a field as JSON (it's crazy, but it is so as it is):
id INTEGER
user_id INTEGER
data LONGTEXT
This "JSON field" data has following structure and contains inter alia an image:
{
"id":"1",
"foo":"bar",
...
"data":{
"baz":"buz",
...
}
}
Displaying it is no problem, but now I want to make the data ediable. My form looks like this:
<?php
$form = $this->beginWidget('CActiveForm', array(
'id' => 'my-form',
'htmlOptions' => array('enctype' => 'multipart/form-data'),
'enableAjaxValidation'=>false,
));
?>
<div class="row">
<?php echo $form->labelEx($model, 'foo'); ?>
<?php
echo $form->textField($model, 'foo', array(...));
?>
<?php echo $form->error($model, 'foo'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model, 'baz'); ?>
<?php
echo $form->textField($model, 'data[baz]', array(...));
?>
<?php echo $form->error($model, 'data[baz]'); ?>
</div>
It works. But there are multiple problems, that seem to be caused by the same thing -- that he form fields are not referenced to the model attributes/properties:
When I make fields foo and baz required (public function rules() { return array(array('foo, baz', 'required')); } -- the property $foo is defined) foo bahaves as wished, but baz causes an "foo cannot be blank" error. So I cannot set a data[*] as required.
If the form is not valid and gets reloaded, all the data[*] fields are empty.
The data[*] fields are not marked as required.
Is there a to solve this without to change the datase structure? There will not be a correct way for it, but maybe a workaround.
It's impossible to validate fields in such way. First of all if you are using field in model it must be defined or exist in table for active record. So if you want to validate such structure the only right way to do it:
class Model extends CActiveRecord {
// Define public varialble
public $data_baz;
public function rules(){
return array(
// Add it to rules
array( 'data_baz', 'required' )
);
}
public function attributeLabels(){
return array(
// Add it to list of labels
'data_baz' => 'Some field'
);
}
protected function beforeSave(){
if ( !parent::beforeSave() ) {
return false;
}
// Also you may create a list with names to automate append
$this->data['baz'] = $this->data_baz;
// And serialize data before save
$this->data = serialize( $this->data );
return true;
}
}
And your form should looks like
<div class="row">
<?php echo $form->labelEx($model, 'data_baz'); ?>
<?php echo $form->textField($model, 'data_baz'); ?>
<?php echo $form->error($model, 'data_baz'); ?>
</div>
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 have have a view in which there's a form that manages products (either add new product or -if an id passed- editing an existing one). If an id is passed then the form action should be eg 'admin/product/manage/5', if no id passed then it should be like this 'admin/product/manage'.
<?php echo form_open('admin/product/manage/{optional product id}', array('class' => 'ajax-form')); ?>
I have also created and this route:
$route['admin/product/manage'] = "admin/product/manage";
$route['admin/product/manage/(:num)'] = "admin/product/manage/$1";
How can I make my form action work correctly? is it possible to put inside the action the route somehow??
This is my Controller:
public function manage($id = NULL){
//fetch a single product to edit or create a new one
if (isset($id) === true) {
$data['prod'] = $this->product_model->get($id);
$data['vers'] = $this->product_version_model->get_by('product_id',$id);
} else {
$data['prod'] = $this->product_model->make_new();// this returns $product->product_name = ''; in order to be empty the input field and not throughing errors
}
$this->product_model->save_product();
$this->product_version_model->save_version();
// load the view
$this->layout->view('admin/products/manage', $data);
}
This is my view:
<?php echo form_open('admin/product/manage', array('class' => 'ajax-form')); ?>
<p>
<label for="product_name">Product *</label>
<input type="text" name="product_name" value="<?php echo set_value('product_name', $prod->product_name); ?>" />
<?php echo form_error('product_name'); ?>
</p>
<?php echo form_close() . PHP_EOL; ?>
You need to declare both possible routes in order of importance, so:
$route['admin/product'] = "admin/product/manage";
$route['admin/product/(:num)'] = "admin/product/manage/$1";
From the Codeigniter Docs:
Routes will run in the order they are defined. Higher routes will always take precedence over lower ones.
Edit:
According to the changes you have made to your question I can say the following:
First of all isset() returns boolean only, so you don't need the type check "=== true". isset($id) is sufficient.
In order to have your form action set to the id you need to include it either in a hidden field or in the action itself.
So for example:
$action_id = (isset($id) ? '/'.$id : ''); // Using ternary operators here
echo form_open('admin/product/manage'.$action_id, array('class' => 'ajax-form'));
and add the id to the view data in your controller:
$data['id'] = $id;
As a side note: In order to comply with SoC (Separation of Concerns) you'd prepare all data in your controller (with e.g. models all having their own task) and pass the processed data to the view instead of partially generating data in the view itself.