I am trying to learn Yii, and have looked at Yii documentation, but still do not really get it. I still have no idea how to use the CDataProvider on the Controller and View to display all the blog posts available on the view. Can anyone please advise or give an example based on the following:
The actionIndex in my PostController:
public function actionIndex()
{
$posts = Post::model()->findAll();
$this->render('index', array('posts' => $posts));
));
The View, Index.php:
<div>
<?php foreach ($post as $post): ?>
<h2><?php echo $post['title']; ?></h2>
<?php echo CHtml::decode($post['content']); ?>
<?php endforeach; ?>
</div>
Instead of doing the above, can anyone please advise how to use the CDataProvider to generate instead?
Many thanks.
The best that i can suggest is using a CListView in your view, and a CActiveDataProvider in your controller. So your code becomes somewhat like this :
Controller:
public function actionIndex()
{
$dataProvider = new CActiveDataProvider('Post');
$this->render('index', array('dataProvider' => $dataProvider));
}
index.php:
<?php
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_post', // refers to the partial view named '_post'
// 'enablePagination'=>true
)
);
?>
_post.php: this file will display each post, and is passed as an attribute of the widget CListView(namely 'itemView'=>'_post') in your index.php view.
<div class="post_title">
<?php
// echo CHtml::encode($data->getAttributeLabel('title'));
echo CHtml::encode($data->title);
?>
</div>
<br/><hr/>
<div class="post_content">
<?php
// echo CHtml::encode($data->getAttributeLabel('content'));
echo CHtml::encode($data->content);
?>
</div>
Explanation
Basically in the index action of the controller we are creating a new CActiveDataProvider, that provides data of the Post model for our use, and we pass this dataprovider to the index view.In the index view we use a Zii widget CListView, which uses the dataProvider we passed as data to generate a list. Each data item will be rendered as coded in the itemView file we pass as an attribute to the widget. This itemView file will have access to an object of the Post model, in the $data variable.
Suggested Reading: Agile Web Application Development with Yii 1.1 and PHP 5
A very good book for Yii beginners, is listed in the Yii homepage.
Edit:As asked without CListView
index.php
<?php
$dataArray = $dataProvider->getData();
foreach ($dataArray as $data){
echo CHtml::encode($data->title);
echo CHtml::encode($data->content);
}
?>
Related
I am beginner in code igniter php framework. Today I have a task to modify existing code and somehow I am lost in the middle of the line on attempting to located a database table. This is the controller snippet that renders the view
..........
$data["all_types"] = $this->data_model->get_data("all_types",300,"Asc");
$this->load->view("preview_brands",$data);
...........
when the view is loaded I am iterating through the in the view to load data as shown
<div class="decoration"></div>
<?php foreach($all_types as $item): ?>
<div style="display:block;color:#000;">
<a style="font-size:17px;margin:15px 2px;font-family:Cgothic;"
href="<?php echo site_url()."...../..../".$item["id"]; ?>"
class="scale-hover"
title="<?php echo str_replace("_"," ",$item["name"]);?>">
<strong><?php echo str_replace("_"," ",$item["name"]);?></strong>
</a>
</div>
<?php endforeach; ?>
this is the model snippet code as shown
function get_data($db,$i,$order)
{
$this->db->order_by("name",$order);
$this->db->where('consent',"yes");
$query=$this->db->get($db);
return $query->result_array();
}
My challenge is how can I locate or identify the database table the above model is pointing to to fetch data. Hoping someone assists me
all_types is your table name.
$this->db->get($your_table name), get method take table name as a parameter.
Get Method Documentation
If more help needed I'm happy to help.
Happy Coding.
From your model code:
$query=$this->db->get($db);
This means $db is the table name. Looking at your controller code which calls the model function:
$data["all_types"] = $this->data_model->get_data("all_types",300,"Asc");
I can say that all_types is the name of the table.
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; ?>
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.
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! :)
In Cakephp I have a model called Category and I have another model called Page. Now I connected the Page with $belongsTo to the Category model.
Now I have a form where I can create a new Page:
<?php echo $this->Form->create('Page', array('action' => 'create')); ?>
<?php echo $this->Form->input('title'); ?>
<?php echo $this->Form->input('text'); ?>
<?php echo $this->Form->end('Create new Page'); ?>
Now I want to add the possibility to select the category in the form. I think the solution is simple but I didn't found anything helpful so far...
in your form add this code
echo $this->Form->input('category_id');
now go to your Page controller, inside the appropriate action method, you add this code
$categories = $this->Page->Category->find('list');
$this->set(compact('categories'));