I'm trying to merge 3 models to create a fourth one. I have model1, model2 and model3 and I want to merge them into modelMaster. I've also created controllers for all of them. When I call modelMaster/create action, I render the modelMaster/create view which renders the modelMaster/_form view. Inside this _form view, I also want to render model1/_form, model2/_form and a CHtml::dropDownList(), wich takes datas from model3. However, this doesn't work. How can I combine these three different views into one another?
If you try to skip the form generate from the _form views and use unique model names, I think you can use this manual: single form with more models
So the generate of the form definition handles always the parent view and the _form's only the inputs
The other way to use single model in views, create a form model by extend CFormModel, and handle the data binding between this model and the wrapped submodels
If you want to nest several forms into one master form you have to adjust the form templates accordingly. All of your modelMaster/create, model1/_form, model2/_form-views create and render a new CActiveForm (and thus several <form> tags).
Since you cannot nest form-elements in html (how should html know which action to pass the data to) you have to avoid this by doing the following:
Extract the inputs of the form you want to nest into a new view, e.g. model1/_formInputs would look like
...
<?php echo $form->labelEx($model,'name'); ?>
<?php echo $form->textField($model,'name');
<?php echo $form->error($model,'name');
...
alter the model1/create and the other views and get a reference to the form created there, by assigning the return of $this->beginWidget to the variable $form (if not already done):
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'foo',
)); ?>
replace the former input fields with
<?php $this->renderPartial('model1/_formInputs', array('form' => $form); ?>
Now, for example the old model1/create-view should work as expected
To get your multi-model-form working you just have to get the reference to the form created in modelMaster/create and use it to renderPartial all */_formInputs you require. Please also remember to include the models for the inputs into the renderPartial-call. So modelMaster/create would look something like:
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'foo',
)); ?>
/* Master Inputs here */
// Rendering other models' inputs
<?php $this->renderPartial('model1/_formInputs', array('form' => $form, 'model' => $model1); ?>
<?php $this->renderPartial('model2/_formInputs', array('form' => $form, 'model' => $model2); ?>
/* Render Form Buttons here */
<?php $this->endWidget(); ?>
Submit with Ajax, in Yii it is easy to do and it will keep things easy to understand in the controllers, each controller will have a save and respond with json to confirm the save. There is already ajax validation.
/**
* Performs the AJAX validation.
* #param CModel the model to be validated
*/
protected function performAjaxValidation($model)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='employee-form')
{
$valid = CActiveForm::validate($model);
if(strlen($valid) > 2) {
echo $valid;
Yii::app()->end();
}
}
}
As you can see I have modified it to return the error if there is one (validate returns [] if it is valid, I should probably check for that instead of strlen >2 ), otherwise let the script continue, in this case it will go to the save function.
Related
I'm new to yii and I don't understand the extensions much
but I used this extension called jmultiselect2side because I'm trying to make a site where users could reserve stuff like apparatuses in the lab
Anyway, I need a code that would get the Selected Items and then display them in another page for viewing purposes
I haven't put anything in the controller but the name of my controller and model is Apparatus
Here is my view:
<?php
$model= Apparatus::model()->findByAttributes(array('ApparatusCode'=>'1'));
// complete user list to be shown at multiselect order by ApparatusCode
$Apparatus= Apparatus::model()->findAll(
array('order' => 'ApparatusCode'));
?>
<center>
<?php
$this- >widget('application.extensions.jmultiselect2side.Jmultiselect2side',array(
'model'=>$model,
'attribute'=>'ApparatusName', //selected items
'labelsx'=>'Available',
'labeldx'=>'Selected',
'moveOptions'=>false,
'autoSort'=>'true',
'search'=>'Search:',
'list'=>CHtml::listData( // available items
$Apparatus,
'ApparatusCode',
'ApparatusName'),
));
?>
please help as soon as possible :/
put all above elements in a form. Set action for the form. Submit the form then the action in which you are handling this submit request, you can write there
if(isset($_POST))
{
foreach($_POST['Apparatus']['ApparatusName'] as $name)
{
do what ever you want
}
}
$name will represent the each selected item
I am a bit new to the Yii Framework. I am making a product selling website, which has 3 basic models
1. Users model containing the primary key id
2. Products model containing the primary key id
3. Orders model which is basically a mapping between the products and orders. It contains the fields product_id and user_id as foreign keys.
I have made a page where all the products are populated and the logged in user can click on a button on product box to order a particular product.
the code of the link is like this
<?php echo CHtml::link('Order Now',array('order',
'product_id'=>$model->id,
'user_id'=>Yii::app()->user->id)); ?>
(Q1) This is sending a GET request but I want to sent the details as post request. How to do this?
My default controller is the site controller. I have made an actionOrder method in this controller.
The code is:
if(Yii::app()->user->isGuest){
$this->redirect('login');
}else{
$model=new Orders;
if(isset($_POST['products_id']))
{
$model->attributes->products_id=$_POST['product_id'];
$model->attributes->users_id=Yii::app()->user->id;
if($model->save())
$this->redirect(array('index'));
}
$this->render('index');
}
But this code is showing bunch of errors. Also, (Q2) how can I put both products_id and users_id in a single array Orders so that I just have to write $_POST['orders']
Also, (Q3) how can I display a flash message after the save is successful?
Kindly help me to solve my 3 problems and sorry if you feel that the questions are too stupid.
Q1: If you want to use POST request, you're going to have to use a form of sorts, in this case the CActiveForm.
Controller:
public function actionOrder()
{
if(Yii::app()->user->isGuest)
$this->redirect('login');
else
{
$model=new Orders;
if(isset($_POST['Orders']))
{
$model->product_id=$_POST['Orders']['products_id'];
$model->users_id = Yii::app()->user->id;
if($model->save())
{
// Q3: set the flashmessage
Yii::app()->user->setFlash('ordered','The product has been ordered!');
$this->redirect(array('index'));
}
}
$this->render('index', array('model'=>$model)); //send the orders model to the view
}
}
View:
<!-- Q3: show the flash message if it's set -->
<?php if (Yii::app()->user->hasFlash('ordered')): ?>
<?php echo Yii::app()->user->getFlash('ordered'); ?>
<?php endif ?>
...
<?php $form=$this->beginWidget('CActiveForm', array('id'=>'order-form')); ?>
<?php echo $form->hiddenField($model,'products_id',array('value'=>$product->id)); ?> // please note the change of variable name
<?php echo CHtml::submitButton('Order Now'); ?>
<?php $this->endWidget(); ?>
Please note that I have changed the name of the product model variable $model to $product, because we will be using $model for the Orders model for the form.
Q2: In this case I set the users_id value in the controller, so $_POST['Orders'] only contains the value for products_id. In yii you can also mass assign your attributes with:
$model->attributes = $_POST['Orders']
Which basicly means $_POST['Orders'] is already an associative array containing the attribute names and values that are in your form.
Q3: The code shows you how to set and show a flash message after an order is succesfull.
First you have to declare forms send method, if you're using bootsrap it'll be like mine:
<?php $form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'action' => Yii::app()->createUrl($this->route),
'method' => 'post',
'id' => 'activity_timeRpt',
));
?>
Second if you want to send custom inputs, you have to specify, otherwise it'll be like
i'll be back to finish this
For your questions 1 and 2 I'd recomend you to use a CActiveForm class. For example
<?php $form = $this->beginWidget('CActiveForm', array(
'action' => 'you_action_here'
'method'=>'post' // this is optinal parameter, as 'post' is default value
)); ?>
<?php echo $form->textField($model,'product_id'); ?>
<?php echo $form->hiddenField($model,'user_id', array('value'=>Yii::app()->user->id)); ?>
<?php $this->endWidget(); ?>
where $model is instance of Orders class, passed by variables thru controller, or set in view file. After that you can use it in way you wanted $model->attributes = $_POST['orders'] in your action method.
For flash message you can use Yii->app()->user->setFlash('orderStatus', 'Successful'), before redirect( or render ) in your actionOrder. To show it:
<?php if(Yii::app()->user->hasFlash('orderStatus')):?>
<div class="info">
<?php echo Yii::app()->user->getFlash('orderStatus'); ?>
</div>
<?php endif; ?>
been looking for a solution to add a feature for "Custom Columns"... Meaning, I present a list of columns that I can show the user and he selects the ones he wants to see and after the selection the table is updated and add/removes the needed columns.
Didn't find anything on Google (perhaps it has a different name than what I was looking for...)
Anyone has an Idea on how it can be accomplished?
Thanks in advance!
This is not a complete sample, but can give you some clues on how to implement it. You've to define some kind of form to collect the data about how your grid has to be rendered. I recommend you to create a CFormModel class if there are more than 3 input fields. Create a view file with the form and a div or renderPartial of a file containing a grid:
$form = $this->beginWidget('CActiveFormExt');
echo $form->errorSummary($model);
echo $form->labelEx($model,'column1');
echo $form->dropDownList($model
echo $form->error($model,'column1');
echo CHtml::ajaxSubmitButton('UpdateGrid',array('controller/grid'),
array('update'=>'#grid'),
$this->endWidget();
// you can render the 'default options' before any ajax update
$this->renderPartial('_grid',array($customColumns=>array('id','name'),'dataProvider'=>$dataProvider));
In the _grid.php view file:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'grid',
'dataProvider'=>$dataProvider,
'columns' => $customColumns;
));
In the controller:
function actionGrid(){
// recover the form data, and build the custom columns array
$customColumns = array();
$customColumns[] = '.....';
$dataProvider = ...;
$this->renderPartial('_formTrabajo', array('customColumns' => $idSiniestro, 'dataProvider' => $dataProvider'), false);
}
When you click the ajaxSubmitButton, the form is sent to the url specified through ajax, and the reply from the controller must contain the renderPartial of the view containing the grid, so the jQuery call can replace the html correctly. You must pass an array from your controller to the partial view of the grid, with the custom list of columns you want to display.
Situation
I used the wiki article on Yii's site, Collecting Tabular Input, to follow as an example.
I don't believe I need to validate tabular input in a traditional sense against multiple models. I only have one model, but I'm dynamically creating the number of fields in the form. Here's a bit more background.
I'm importing CSV files where its headers vary in order among the different files. Before correctly parsing the files, the user needs to map which header would map to what table/column.
I have a single model, ImportParseForm extended from CFormModel. It really only has one rule:
public function rules()
{
return array(
array('header', 'required'),
);
}
Here's a snippet of my view:
<?php foreach($headers as $h => $hItem): ?>
<div class="row">
<?php echo CHtml::label(CHtml::encode($hItem), "[$h]header"); ?> maps to
<?php echo $fParse->textField($mForm, "[$h]header"); ?>
<?php echo $fParse->error($mForm, "[$h]header"); ?>
</div>
<?php endforeach; ?>
Here's a snippet of my controller:
$mForm = new ImportParseForm;
$valid = true;
if (isset($_POST['ImportParseForm'])){
foreach ($headers as $h => $hVal){
if (isset($_POST['ImportParseForm'][$h])){
$mForm->attributes = $_POST['ImportParseForm'][$h];
$valid = $mForm->validate() && $valid;
}
}
if ($valid){
// Process CSV
}
}
If all fields are valid, then it passes as expected. The problem is if one of the fields are invalid (or in this case, empty), then all fields are flagged as invalid.
In Yii 1.1.10, they added CActiveForm::validateTabular(), but it looks like it's for multiple models. Not quite what I have here. But for kicks, I added the following to my controller (removed the other type of validation, of course):
CActiveForm::validateTabular($mForm, array('header'));
The form itself is only valid if the first element is populated. If the first element is populated, it will set all the other elements with that same value (and passes validation).
Question
Basically, can I use CActiveForm to do validation against fields that are dynamically generated (similar to tabular input, but with only one model)?
I was trying to do similar thing, and this is my solution in case of model update form. Here I exploited model validation for update model attributes on value change event, which don't need submit button, and looks simple and fancy. Here is code snippet...
view code:
<?php foreach($modelArray as $model): ?>
<div class="row">
<?php echo $form->textField($model, "[$model->id]attributeName"); ?>
<?php echo $form->label($model, "[$model->id]attributeName"); ?>
<?php echo $form->error($model, "[$model->id]attributeName"); ?>
</div>
<?php endforeach; ?>
controller code:
objArray = array();
foreach($_REQUEST['ModelName'] as $id => $attributes){
$obj = ModelName::model()->findByPk($id);
$obj->attributes = $attributes;
$obj->save();
$objArray[$id] = $obj;
}
echo CActiveForm::ValidateTabular($objArray);
Yii::app()->end();
After reading Collecting Tabular Input a bit closer, I am using "multiple" models. I misunderstood that multiple models would mean multiple different structured models and not just multiple of the same structured model in an array. For example, in the wiki there's a piece that shows what items (array of models) to update: $items=$this->getItemsToUpdate();. My corrected assumption is that that particular method grabs multiple of the same structured model but with different primary keys... or different records. Understanding that, the rest of the article makes more sense ;)
But here's my model solution on how to create a CSV header mapping form.
class ImportParseForm extends CFormModel{
// Model really only has one attribute to check against, the header
var $header;
// New attributeLabels collected and stored on class instantiation
protected $attributeLabels;
// Modify construct so we can pass in custom attribute labels
public function __construct($attributeLabels = '', $scenario = '')
{
if (! is_array($attributeLabels)){
$this->attributeLabels = array($attributeLabels);
}
else{
$this->attributeLabels = $attributeLabels;
}
parent::__construct($scenario);
}
public function rules()
{
return array(
array('header', 'required'),
);
}
public function attributeLabels()
{
// Default mapping
$arr = array(
'header' => 'Header Mapping',
);
// Merge mapping where custom labels overwrite default
return array_merge($arr, $this->attributeLabels);
}
}
Here is a snippet in my controller on what my equivalent to $items=$this->getItemsToUpdate(); (again, the goal is to collect an array of models) would look like
// Get the first row of CSV, assume it's the headers
$tmpCsvRow = explode("\n", $mTmp->data);
$headers = explode(',', $tmpCsvRow[0]);
foreach ($headers as $header){
if (! empty($header)){ // Blank headers are lame, skip them
// Add a new model for each CSV header found into $mForm array
// You can also add in a custom attributeLabel, $header is an actual header name like 'First Name',
// so the new label for the header attribute in ImportParseForm would be 'First Name header' and
// it will show up properly in your CActiveForm view
$mForm[] = new ImportParseForm(array('header' => $header.' header'));
}
}
Push $mForm to your view. Now in your view, iterate through $mForm for your form like so (similar to the wiki article, but I'm using a CActiveForm widget here):
<?php foreach($mForm as $m => $mItem): ?>
<div class="row">
<?php echo $fParse->labelEx($mItem,"[$m]header"); ?> maps to
<?php echo $fParse->textField($mItem, "[$m]header"); ?>
<?php echo $fParse->error($mItem, "[$m]header"); ?>
</div>
<?php endforeach; ?>
Validation works as expected.
If you want to use AJAX validation, use CActiveForm::validateTabular() in your controller (instead of the normal validate()).
Hope this helps other Yii beginners! :)
I have a simple Zend Framework that has a view script to add records to the database. This is a silly question IMHO, but how can I use the add view script to edit the record as well??
I have played around with a few scenarios to no avail.
Thanks,
Steve
Per Matt S' comment, the method you're looking for is Zend_Form::populate(). There are some notes about it in the documentation: Populating and Retrieving Values.
Basically, you use it like this in the controller:
$form = new Form_Person();
// get the data from somewhere
if($id = $this->getRequest()->getParam('id') && $model->find($id)) {
// really, use data from the model here
// but the populate() -method can take any array as an argument
$form->populate(array(
'name' => 'Dolph',
'age' => '52'
));
}
$this->view->form = $form;
and in your view, as usual:
<?= $this->form ?>
So the array could be for example the result of Zend_Db_Table_Row_Abstract::toArray() with column names matching to the names you gave the form elements.