Yii - ratings percentage in GridView - php

So, I finally have a progress bar in my GridView, thanks to the BootProgressColumn. However, I have a rating system, and I'd like to show the percentage inside the progress bar. I can set it the hard way in the columns array, but that'll be for all the rows.
$rawData = Item::model()->findAllByAttributes(array('special_id' => $specialId));
$dataProvider = new CArrayDataProvider($rawData, array());
The GridView:
'dataProvider' => $dataProvider,
'columns' => array(
array(
'class' => 'application.components.BootProgressColumn',
'name' => 'Rating',
'animated' => true,
'striped' => true,
'percent' => '44',
),
)
Now, how do I get the sum of all columns where each respective $data->id matches rows in the Rating model?
$countRatings = count(Rating::model()->findAllByAttributes(array('item_id' => $data->id));
But what to do for sum? And how to divide sum by count? That all having done, how to get it in each column? Or would it be best to just create a table by myself?

If I understand your question well, you want to get the average rates for each item, here is what you can do:
in the Item model create the relation avgRating:
public function relations()
{
return array(
'avgRating' => array(self::STAT, 'Item', 'item_id', 'group' => 'item_id', 'select' => 'ROUND(AVG(rate),1)'),
);
}
and then you can call this for each item to get the rating in your table:
'dataProvider' => $dataProvider,
'columns' => array(
array(
'class' => 'application.components.BootProgressColumn',
'name' => 'Rating',
'animated' => true,
'striped' => true,
'percent' => '$data->avgRating',
),
)

Related

YII2 gridview dont display the correct data

I have few tables. position(to holds all positions), applicants(to hold all applicants) and assign_applicant(to say which position is held by what applicant - many to many reln).
So data is like for assign_applicant table
pos applicant
2 1
2 3
3 4
3 5
I want to display the records like this. but instead it shows :
pos applicant
2 1,3 (array)
3 4,5 (array)
The reason is (many) relations:
public function getAssign()
{
return $this->hasMany(\admin\models\AssignApplicant::className(), ["job_position_id" => "id"]);
}
public function getApplicant()
{
return $this->hasOne(\admin\models\Applicant::className(), ["id" => "applicant_id"]) ->via('assign');
}
Here is the grid view:
<?=
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'layout' => "{pager}\n{summary}\n{items}\n{pager}",
'filterPosition' => \yii\grid\GridView::FILTER_POS_HEADER,
'responsive' => true,
'hover' => true,
'resizableColumns' => true,
'floatHeader' => true,
'autoXlFormat' => true,
'export' => [
'fontAwesome' => true,
'showConfirmAlert' => true,
'target' => GridView::TARGET_BLANK
],
'panel' => [
'type' => 'primary',
'heading' => ''
],
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'attribute' => 'position',
'format' => 'raw',
'value' => function ($model)
{
return Html::a($model->positionName, yii\helpers\Url::toRoute(['position/update', 'id' => $model->position_id]));
}
],
[
'attribute' => 'applicantName',
'value' => 'applicantName'
],
],
]);
?>
My query (raw form):
SELECT `job_positions` . * , applicant.first_name, assign_applicant.id
FROM `job_positions`
LEFT JOIN `position` ON `job_positions`.`position_id` = `position`.`id`
INNER JOIN `assign_applicant` ON `job_positions`.`id` = `assign_applicant`.`job_position_id`
INNER JOIN `applicant` ON `assign_applicant`.`applicant_id` = `applicant`.`id`
WHERE (
`client_id` = '1'
)
AND (
`shift_id` = '1'
)
AND (
`date` = '2016-12-08'
)
ORDER BY `position`.`name`
LIMIT 20
Any way to show the data like I want
yii2 intentionally removes duplicated models when processing results.
your can either use applicants (or assign_applicant) as primary model as #Bizley suggested
or process the resulting models beforehand (using clone and populateRelation to obtain the desired structure)

sort column by date TbJSONGridView

I want to sort a column in TbJSONGridView by date , I have this code
$this->widget(
'bootstrap.widgets.TbJsonGridView', array(
'dataProvider' => $model->searchPending(),
'type' => 'striped bordered condensed',
'summaryText' => false,
'cacheTTL' => 10, // cache will be stored 10 seconds (see cacheTTLType)
'cacheTTLType' => 's', // type can be of seconds, minutes or hours
'enablePagination' => true,
'columns' => array(
array(
'name' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
))
but the column I get it is from a related model, and the sorting is not working, when I clicked on the column header it does nothing.
what's wrong with the code???
Gabriel
$data->carShippeds[0]->
This shows us that the current model has many cars shipped. You are only showing the first one in there. While that works, Yii has no way of knowing that you are only showing the first value so your sorting has no chance of working. Create a view that only selects your first pickup date, make a join in your criteria with the main model, show the field without [0]-> and it will work.
Change the below section:
array(
'name' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
To this:
array(
'name' => 'pickup_date',
'header' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
Then in your 'form' when you want to return DataProvider write the specific sorting code for this association like below:
return new DataProvider($query, [
'pagination' => [
'pagesize' => 20,
],
'sort' => [
'sortVar' => 'sort',
'defaultOrder' => ['t.carShippeds.pickup_date asc'],
'attributes' => [
'pickup_date' => [
'asc' => 't.carShippeds.pickup_date',
'desc' => 't.carShippeds.pickup_date DESC',
],
'*',
],
],
]);
Please notice that I did not know the join alias you have for your t.carShippeds or even whether it is t or another entity. replace it or give me more information about your DataProvider and I can help more.

how to display array to CGridView (yii framework)

I have next variables:
$type_model = ProductTypeModel::model()->findByPk($id);
$prod = $type_model->product;
Now in $prod:
array
(
0 => ProductModel#1
(
[CActiveRecord:_new] => false
[CActiveRecord:_attributes] => array
(
'product_id' => '6'
'product_type_id' => '5'
)
...
)
1 => ProductModel#2
(
'product_id' => '8'
'product_type_id' => '5'
)
...
How i can display my products in CGridView?
Thx.
I Suppose you are using CarrayDataProvider. So in your controller
$dataProvider = new CArrayDataProvider($prod);
Here $product could be any array you want to display in CgridView. Now
In you view write this.
$gridColumns = array(
array(
'header' => 'First Name',
'value' => 'ProductTypeModel::model()->findByPk($data->product_id)->key',
'htmlOptions' => array('style' => 'text-align:center;')
),
$this->widget('zii.widgets.grid.CGridView',array('dataProvider' => $dataProvider,));
As in CarrayDataprovider array is obtained so we cant use relations in it. Thats why u have to write 'ProductTypeModel::model()->findByPk($data->product_id)->key'
Here you can display anything attribute of ProductTypeModel so u can replace above mentioned key with your desired attribute
Try this ...
it automatic convert to array data provider.
$dataProvider=new CArrayDataProvider($type_model->product);
Thanks all. By means of answers "naveen goyal" and "jailed abroad" i did like this:
$dataProvider=new CArrayDataProvider($type_model->product);
$dataProvider->keyField = 'product_id';
$this->widget('bootstrap.widgets.TbGridView', array(
'dataProvider' => $dataProvider,
'columns' => array(
array(
'header' => 'Title',
'value' => 'CHtml::encode($data["product_title"])',
),
)));
Nice work for me.

Yii passing data back into a model

I have been working with Yii for the past few months, but I hit a bit of a block:
In my base controller I have a property public $currentUserOrganisations = NULL; which on load populates it with all the logged in users organizations.
Now I have a page where I get all of the organizations and the user can connect to them, but the ones I already have must say "Connected" rather then have the ability to add. I am using the bootstrap TbGridView widget and in my Organisation model created a function getConnectionAction which either returns the anchor OR a label depending if the organization is already connected to the user.
Here is my problem: I can't find a way to access the already loaded user organizations in the Organisation model, because it is a property on my model class.
See below for code:
Action in controller
public function actionNew()
{
$connectionModel = Connection::model();
$organizationModel = Organisation::model();
$this->selectedSubnav = "Add";
$this->render('new', array("connectionModel" => $connectionModel, "organizationModel" => $organizationModel));
}
Here is the TbGridView in the view:
<?php
$this->widget(
'bootstrap.widgets.TbGridView',
array(
'type'=>'striped',
'enableSorting'=>true,
'id' => 'connection-rest-data',
'dataProvider' => $organizationModel->getConnectionsByOrganization($this->currentOrganisation->id),
'ajaxUpdate' => true,
'template'=>"{pager}<br>\n{items}\n{pager}",
'rowHtmlOptionsExpression' => '',
'emptyText' => Yii::t("site", "no_restults_found") . '.',
"itemsCssClass" => "table table-first-column-number data-table display full dataTable transaction_tbl",
'columns' => array(
array(
'header' => Yii::t("site", "id"),
'value' => '$data->id',
'type' => 'raw',
'name' => 'id'
),
array(
'header' => Yii::t("site", "from_unit"),
'type' => 'raw',
'value' => '$data->fromUnit["name"]',
'name' => 'fromUnit'
),
array(
'header' => Yii::t("site", "to_unit"),
'type' => 'raw',
'value' => '$data->toUnit["name"]',
'name' => 'type'
),
array(
'header' => Yii::t("site", "connection_type"),
'type' => 'raw',
'value' => '$data->type["name"]',
'name' => 'type'
),
array(
'header' => Yii::t("site", "ended"),
'type' => 'raw',
'value' => '$data->ended',
'name' => 'ended'
),
array(
'header' => Yii::t("site", "fees"),
'type' => 'raw',
'value' => 'empty($data->fees) ? "none" : $data->fees["fee"]',
'name' => 'fees'
),
array(
'header' => Yii::t("site", "actions"),
'type' => 'raw',
'value' => 'Organisation::model()->getConnectionAction($data->id, \'' . serialize($this->currentUserOrganisations) . '\'")',
'name' => 'fees'
)
),
)
);
?>
And here is the function in the Organisations Model:
public function getConnectionAction ($connectionId)
{
$currentOrganizations = null; //This I need to get the public $currentUserOrganisations = NULL; value
foreach ($currentOrganizations as $org)
{
if($org->id == $connectionId)
{
return CHtml::label(Yii::t("site", "connected"), "", array("style" => "color:green;"));
}
}
return CHtml::link("Add", "/connection/manage/addConnection?id=$connectionId");
}
Much appreciated guys and give me a shout if anything is unclear!
Ok so I managed a work around which is very close to what Martin said.
In the view for the specific field I do:
'value' => 'Organisation::model()->getConnectionAction($data->id, ' . $this->currentOrganisation->id . ')',
The id is the id of the listed organization and the currentOrganization->id is the id of the users current organization.
Then in my model I get all the organizations for the user:
$currentOrganizations = $this->findAllForUserByOrganizationId($currentOrgId);
Foreach through them and check if they match, full function below.
public function getConnectionAction ($organizationId, $currentOrgId)
{
$currentOrganizations = $this->findAllForUserByOrganizationId($currentOrgId);
foreach ($currentOrganizations as $org)
{
if(in_array($organizationId, array($org->fromUnit['id'], $org->toUnit['id'])))
{
return CHtml::label(Yii::t("site", "connected"), "", array("style" => "color:green;"));
}
}
return CHtml::link(Yii::t("site", "add_connection"), "/connection/manage/addConnection?id=$connectionId", array("class" => "createConnection"));
}
I think you are going to want to avoid Active Record for this one. You can use a CSqlDataProvider to load your TbGridView. Use SQL that looks something like this
select
org.organization_name,
user.name
from organization org
left outer join connection x on org.id = x.organization_id
join user on user.id = x.user_id
where user.id = ? or user.id is null
That ? is a parameter marker to allow you to pass in a user_id in a parameter array rather than just concatenating it. Concatenation provides an opening for SQL injection.
Then in your column list for the gid view test the user.name for null and if null display the add button. If not null, display connected.

jqgrid update two tables from one grid

I have two tables in one grid displayed (via LEFT JOIN) and I wonder is there a way to implement an inline edit functionality for both table from the same grid?
I.e:
when the main grid table updated:
onSuccess { table_name, {column1=value1,column2=value2,..}}
not necessarily above scenario - I'm seeking here for ideas and logic to it...
I know there is setAfterCrudAction function in jqGridPHP suit, but from the whole suit I only need this functionality... so I'm seeking to try to implement it myself with some help from you
any ideas appreciated, thanks
Example shows how to update fields when join is used (check 'PHP Grid' tab, btw jqgrid-php is free):
<?php
class jqOperBasic extends jqGrid
{
protected function init()
{
$this->table = 'tbl_order_item';
$this->query = "
SELECT {fields}
FROM tbl_order_item i
JOIN tbl_books b ON (i.book_id=b.id)
WHERE {where}
";
#Set columns
$this->cols = array(
'item_id' => array('label' => 'ID',
'db' => 'i.id',
'width' => 10,
'align' => 'center',
'formatter' => 'integer',
),
'order_id' => array('label' => 'Order id',
'db' => 'i.order_id',
'width' => 15,
'align' => 'center',
'formatter' => 'integer',
),
'name' => array('label' => 'Book name',
'db' => 'b.name',
'width' => 30,
'editable' => true,
'editrules' => array('required' => true),
),
'price' => array('label' => 'Price',
'db' => 'i.price',
'width' => 15,
'align' => 'center',
'formatter' => 'integer',
'editable' => true,
'editrules' => array('required' => true,
'integer' => true,
'minValue' => 1,
'maxValue' => 3000
),
),
);
#Set nav
$this->nav = array('edit' => true, 'edittext' => 'Edit');
}
#Save columns to different tables
protected function opEdit($id, $upd)
{
#Server-side validation
if(strlen($upd['name']) < 5)
{
#Just throw the exception anywhere inside the oper functions to stop execution and display error
throw new jqGrid_Exception('The book name is too short!');
}
#Get editing row
$result = $this->DB->query('SELECT * FROM tbl_order_item WHERE id=' . intval($id));
$row = $this->DB->fetch($result);
#Save book name to books table
$this->DB->update('tbl_books', array('name' => $upd['name']), array('id' => $row['book_id']));
unset($upd['name']);
#Save other vars to items table
$this->DB->update('tbl_order_item', $upd, array('id' => $id));
}
}

Categories