Yii2: How to have sum in footer based on previous pages' sum? - php

Please look the picture below.
As you can see total is 215340 in the summary column. What I want is that in the next page it should start summing those numbers on this number.
Here is a code.
GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
[
'class' => 'kartik\grid\SerialColumn'],
// 'id',
[
'attribute' => 'customer_id',
'value' => function ($data) {
return $data->customerDetail->account_no;
}
],
[
'attribute' => 'naration',
'pageSummary' => 'Total',
],
[
'attribute' => 'transaction_type',
'label' => 'Deposits',
'pageSummary' => true,
'pageSummaryOptions' => ['id' => 'total_sum'],
'value' => function ($data) {
if($data->transaction_type==10)
return $data->amount;
else
return 0;
}
],
[
'attribute' => 'transaction_type',
'label' => 'Withdrawals',
'pageSummary' => true,
'value' => function ($data) {
if($data->transaction_type==11)
return $data->amount;
else
return 0;
}
],
[
'class' => 'backend\components\TotalColumn',
'label' => 'Balance',
'attribute' =>'amount',
],
],
// This line displays sum of the columns.
'showPageSummary' => true,
]);
?>
I was thinking about sending this page's sum to the next page using $_GET but the problem is when user going to previous pages i.e. from page 3 to 2. Do you any widget or something for this problem?

My idea is using mysql to select sum of all records in the current column and add this value with the total value of the previous page, I need to know offset and limit of the current page for this query, I can get those values from data provider, to achieve this I have to override the function \yii\grid\Column::renderFooterCellContent().
Extend \yii\grid\DataColumn class
namespace frontend\components;
use yii\helpers\ArrayHelper;
class DataColumn extends \yii\grid\DataColumn
{
protected function renderFooterCellContent()
{
if ($this->footer instanceof \Closure) {
return call_user_func($this->footer, ArrayHelper::getValue($this, 'grid.dataProvider.pagination'));
} else {
return parent::renderFooterCellContent();
}
}
}
Your view file
use frontend\models\Client;
use yii\db\Expression;
use yii\db\Query;
use yii\grid\GridView;
use yii\data\Pagination;
use frontend\components\DataColumn;
echo GridView::widget([
'dataProvider' => $dataProvider,
'dataColumnClass' => DataColumn::className(),
'showFooter' => true,
'columns' => [
//['class' => 'yii\grid\SerialColumn'],
'id',
[
'attribute' => 'num_users',
'footer' => function ($pagination) {
if ($pagination instanceof Pagination) {
$offset = $pagination->offset;
$limit = $pagination->limit;
$cquery = (new Query())
->select(['sub_users' => new Expression('SUM(num_users)')])
->from([
'cpage' => (new Query())
->select('num_users')
->offset($offset)
->limit($limit)
->from(Client::tableName())
]);
//negative offset is illegal
if ($offset > 0) {
$pquery = (new Query())
->select(['sub_users' => new Expression('SUM(num_users)')])
->from([
'ppage' => (new Query())
->select('num_users')
->offset(0)
->limit($offset)
->from(Client::tableName())
]);
$cquery->union($pquery, true);
}
return (new Query())->from(['subt' => $cquery])->sum('sub_users');
}
}
],
]
]);
Raw SQL query
SELECT SUM(sub_sum_users) FROM (
SELECT SUM(num_users) as sub_sum_users FROM (
SELECT num_users FROM `clients` LIMIT 20 OFFSET 120
) cpage
UNION ALL
SELECT SUM(num_users) as sub_sum_users FROM (
SELECT num_users FROM `clients` LIMIT 20 OFFSET 100
) ppage
) sublt
Note:
You could consider to use ActiveRecord, my girdview has a lot of join queries so it's very heavy for me
$offset = $pagination->offset;
$limit = $pagination->limit;
$cquery = Client::find()
->select(['sub_users' => new Expression('SUM(num_users)')])
->from([
'cpage' => Client::find()->select('num_users')->offset($offset)->limit($limit)
]);
//negative offset is illegal
if ($offset > 0) {
$pquery = $cquery = Client::find()
->select(['sub_users' => new Expression('SUM(num_users)')])
->from([
'ppage' => Client::find()->select('num_users')->offset(0)->limit($offset)
]);
$cquery->union($pquery, true);
}
return (new Query())->from(['subt' => $cquery])->sum('sub_users');

Related

Create new record using 2amigos SelectizeDropDownList in Yii2

I am trying to implement the 2amigos SelectizeDropDownList widget in a form to add new values to a table directly within the dropdown.
I am using the model Book and the Model Author so basically want to be able to add a new author in the book form.
This is the book controller at the update function:
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}
This is the form:
<?=
$form->field($model, 'author_id')->widget(SelectizeDropDownList::className(), [
// calls an action that returns a JSON object with matched
// tags
'loadUrl' => ['author/list'],
'value' => $authors,
'items' => \yii\helpers\ArrayHelper::map(\common\models\author::find()->orderBy('name')->asArray()->all(), 'id', 'name'),
'options' => [
'class' => 'form-control',
'id' => 'id'
],
'clientOptions' => [
'valueField' => 'id',
'labelField' => 'name',
'searchField' => ['name'],
'autosearch' => ['on'],
'create' => true,
'maxItems' => 1,
],
])
?>
And this is the function author controller:
public function actionList($query) {
$models = Author::findAllByName($query);
$items = [];
foreach ($models as $model) {
$items[] = ['id' => $model->id, 'name' => $model->name];
}
Yii::$app->response->format = \Yii::$app->response->format = 'json';
return $items;
}
The form works fine to load, filter, search and add new items.
But it is not inserting the new typed attribute in the author table.
Do I need to add something in the book controller?
How can I check if it is a new value or a change of an existing author?
Thanks a lot
I made it work with the following code, not sure the most elegant because i am checking the if the author_id is a number or a string.
In my case the author won't be a number anyway.
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$x = Yii::$app->request->post('Book');
$new_author = $x['author_id'];
if (!is_numeric($new_author)) {
$author = new Author();
$author->name = $new_author;
$author->save();
$model->author_id = $author->id;
}
if ($model->save()) {
return $this->redirect(['index']);
}
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}

How can I get the selected data/item rows in CheckboxColumn Gridview - Yii2

I have a problem on getting all the selected values/data Yii2 Gridview using checkboxColumn.
I can only get one of the value in the grid using this code:
'class' => 'yii\grid\CheckboxColumn',
'checkboxOptions' => function($model, $key, $index, $widget) {
return ['value' => $model['item_id'] ];
},
Need some suggestions on how can I get all the values in the grid...
Here is my Code Code snippet Controller/View:
Controller:
public function actionBulk(){
$action=Yii::$app->request->post('action');
$selection=(array)Yii::$app->request->post('selection');
print_r($selection);
}
View:
<?=Html::beginForm(['transjournal/bulk'],'post');?>
<?=GridView::widget([
'dataProvider' => $dataProvider,
'bordered'=>true,
'striped'=>true,
'condensed'=>true,
'hover'=>true,
'export' => false,
'showOnEmpty' => false,
'panel'=>[
'after'=>Html::submitButton('<i class="glyphicon glyphicon-plus"></i> Posted', ['class' => 'btn btn-success']),
],
'columns' => [
[
'class' => 'yii\grid\CheckboxColumn',
'checkboxOptions' => function($model, $key, $index, $widget) {
return ['value' => $model['item_id'] ];
},
],
'item_id',
'description',
],
]);
?>
<?= Html::endForm();?>
Here is my attachment:
This is the GridView
This is the Result in the GridView (Selected Data only returns item_id)
How can I return both item_id and description?
Issue with your code 'checkboxOptions' =>, can you remove it?
<?=Html::beginForm(['controller/bulk'],'post');?>
<?=Html::dropDownList('action','',[''=>'Mark selected as: ','c'=>'Confirmed','nc'=>'No Confirmed'],['class'=>'dropdown',])?>
<?=Html::submitButton('Send', ['class' => 'btn btn-info',]);?>
<?=GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
['class' => 'yii\grid\CheckboxColumn'],
...
],
]); ?>
<?= Html::endForm();?>
In Controller:
public function actionBulk(){
$action=Yii::$app->request->post('action');
$selection=(array)Yii::$app->request->post('selection');//typecasting
foreach($selection as $id){
$model = Post::findOne((int)$id);//make a typecasting
//do your stuff
$model->save();
// or delete
}
}
basically, i am using yii's CheckboxColumn:
<?php
namespace common\grid;
class CheckboxColumn extends \yii\grid\CheckboxColumn {
public $headerOptions = ['class' => 'text-center', 'style' => 'width: 5em'];
public $contentOptions = ['class' => 'text-center'];
}
?>
then i wrote a jquery plugin for triggering operations with selected items, plus custom Actions and so on, here the relevant javascript code, where options.grid is the id/selector for your grid, e.g. '#grid':
var selection = [];
$(options.grid + ' input:checkbox[name="selection[]"]:checked').each(function() {
selection.push($(this).val());
});
so var selection contains an array with my item ids. length is:
selection.length

Can't find value in select2 ajax loading in yii2

I use select2 ajax loading. I get the code from this link: http://demos.krajee.com/widget-details/select2. When I enter words into field, it display all data/value, but it can't automatic select data/value according to words that I enter into field. So, my select2 always select the first data/value and display all value. What's the problem? These are the codes:
_form.php
$url = Url::to(['/paket/jsonlist']);
$cityDesc = empty($model->no_induk) ? '' : Penerima::findOne($model->no_induk)->nama;
echo $form->field($model, 'no_induk')->widget(Select2::classname(), [
'initValueText' => $cityDesc, // set the initial display text
'options' => ['placeholder' => 'Search for a city ...'],
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 1,
'language' => [
'errorLoading' => new JsExpression("function () { return 'Waiting for results...'; }"),
],
'ajax' => [
'url' => $url,
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(no_induk) { return no_induk.text; }'),
'templateSelection' => new JsExpression('function (no_induk) { return no_induk.id; }'),
],
]);
my controller:
public function actionJsonlist($q = NULL, $id = NULL)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$out = ['results' => ['id' => '', 'text' => '']];
if(!is_null($q))
{
$query = new \yii\db\Query;
$mainQuery = $query->select('no_induk AS id, nama AS text')
->from('penerima')
->limit(20);
$command = $mainQuery->createCommand();
$data = $command->queryAll();
$out['results'] = array_values($data);
}
elseif ($id > NULL)
{
$out['results'] = ['id' => $id, 'text' => \frontend\models\Penerima::find($id)->nama];
}
return $out;
}
Could be you use the attribute name and not the vars
echo $form->field($model, 'no_induk')->widget(Select2::classname(), [
'initValueText' =>'cityDesc', // set the initial display text
or
echo $form->field($model, 'no_induk')->widget(Select2::classname(), [
'initValueText' =>$model->cityDesc, // set the initial display text

Sorting calculated fields in Yii2 (Grid View)

i am need to sort some fields (asc,desc) in GridView, but same fields are calculated. Look at code below:
SearchModel:
class ObjectSearch extends Object {
use SearchModelTrait;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff'
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Methods in Object model:
public function getLastReportResult()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = new ReportStatistic($lastReport);
$message = $statistic->getPercent();
}
return $message;
}
/**
* #return int
*/
public function getReportPercentDiff()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = $lastReport->getReportDiff();
if (!empty($statistic['diff'])) {
$message = $statistic['diff']['right_answers_percent_diff'];
} elseif (!empty($statistic['message'])) {
$message = $statistic['message'];
}
}
return $message;
}
So, by this methods, i am calculating a values of two fields, which are need's sorting. This way doesn't working, i have a Database Exception, because object table hasn't this fields. exception
How to do sorting of this fields ?
Update: I am the author of this answer and this answer is not accurate. Preferred way is to use database view
Add two public properties to ObjectSearch.php and mark it as safe
class ObjectSearch extends Object {
use SearchModelTrait;
public $lastReportResult, $reportPercentDiff;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
[['lastReportResult', 'reportPercentDiff'], 'safe']
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff' => [
'asc' => ['reportPercentDiff' =>SORT_ASC ],
'desc' => ['reportPercentDiff' => SORT_DESC],
'default' => SORT_ASC
],
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Then in index.php (view file in which you are having grid view) add lastReportResult and reportPercentDiff in array of all attributes (list of all attributes ob Object model)
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// your other attribute here
'lastReportResult',
'reportPercentDiff',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
...
For more info you can visit Kartik's blog at Yii
Though this is an old thread, stumbled upon this and tried to find other method to achieve sorting of purely calculated field to no avail... and this post unfortunately is not an answer as well... It just that I feel the need to post it here to give a heads up to those that still looking for the solution so as not to scratch their heads when trying the solution given and still fail.
The given example from documentation or referred links as far as I have tested only works if you have a column within the database schema (whether in the main table or the related tables). It will not work if the virtual attribute/calculated field you create is based on calculating (as an example multiplication of 2 column on the table)
e.g:
table purchase: | purchase_id | product_id | quantity |
table product: | product_id | unit_price |
then, if we use a virtual attribute 'purchase_total' for model 'purchase' which is the multiplication of quantity and unit_price (from the join table of purchase and product on product_id), eventually you will hit an error saying 'purchase_total' column can not be found when you tried to sort them using the method discussed so far.

how to get merged array list data in GridView Yii2

Here I have two tables
Employeedetails
Claimprocess
In employeedetails I have 8 columns:
Company_name, Employeecode, Employee_name, father_name, mother_name, wife_name, child1_name, and child2_name.
In Claimprocess table I have 5 columns:
Company_name, Employeecode, Employee_name, Healthcard_no, and claim_for.
In Claimprocess I set a dependent dropdown for employeecode. For example, if admin selects a company_name that automatically shows dependent employeecode, then again admin selects an employeecode that automatically shows the employee_name and employee relations name such as father_name, mother_name, wife_name etc...
This is the dependent dropdown code for employee_code by selecting company_name
claimform.php
<?= $form->field($model, 'company_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Company::find()->all(),'id','companyname'),
'language' => 'en',
'options' => [
'placeholder' => 'Select a company ...',
'onchange' => '
$.post( "index.php?r=employeedetails/lists&id='.'"+$(this).val(), function( data ) {
$( "select#claimprocess-employee_id" ).html( data );
});',
],
'pluginOptions' => [
'allowClear' => true
],
]);
?>
employeedetailscontroller.php
public function actionLists($id)
{
$countEmployeedetails = Employeedetails::find()
->where(['company_id' => $id])
->count();
$employeedetails = Employeedetails::find()
->where(['company_id' => $id])
->all();
if($countEmployeedetails>0){
foreach($employeedetails as $employee){
echo "<option value='".$employee->id."'>".$employee->employeecode."</option>";
}
}
else{
echo "<option>-</option>";
}
}
This is dependent dropdown code for employee_name and relations_name by selecting employeecode
claimform.php
<?= $form->field($model, 'employee_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Employeedetails::find()- >all(),'id','employeecode'),
'language' => 'en',
'options' => [
'placeholder' => 'Select a employeecode ...',
'onchange' => '
$.post( "index.php?r=employeedetails/lists2&id='.'"+$(this).val(), function( data ) {
$( "select#claimprocess-claim_for" ).html( data );
}),
$.post( "index.php?r=employeedetails/lists1&id='.'"+$(this).val(), function( data ) {
$( "select#claimprocess-employee_name" ).html( data );
});',
],
'pluginOptions' => [
'allowClear' => true
],
]);
?>
employeedetailscontroller.php
public function actionLists1($id)
{
$countEmployeedetails = Employeedetails::find()
->where(['id' => $id])
->count();
$employeedetails = Employeedetails::find()
->where(['id' => $id])
->all();
if($countEmployeedetails >= 0)
{
foreach($employeedetails as $employee)
{
echo "<option value='".$employee->id."'>".$employee->name."</option>";
}
}
else{
echo "<option>-</option>";
}
}
public function actionLists2($id)
{
$countEmployeedetails = Employeedetails::find()
->where(['id' => $id])
->count();
$employeedetails = Employeedetails::find()
->where(['id' => $id])
->all();
if($countEmployeedetails >= 0)
{
foreach($employeedetails as $employee)
{
echo "<option value='".$employee->id."'>".$employee->father_name."</option>";
echo "<option value='".$employee->id."'>".$employee->mother_name."</option>";
echo "<option value='".$employee->id."'>".$employee->wife_name."</option>";
echo "<option value='".$employee->id."'>".$employee->child1_name."</option>";
echo "<option value='".$employee->id."'>".$employee->child2_name."</option>";
}
}
else{
echo "<option>-</option>";
}
}
In claim_for field I have to select relation names of the particular employee, so what I did is combine all relation fields such as father_name, mother_name etc using array_merge and I got a name in form.
claimform.php
<?php
$em = ArrayHelper::map(Employeedetails::find()- >all(),'id','father_name');
$emp = ArrayHelper::map(Employeedetails::find()->all(),'id','mother_name');
$emp1 = ArrayHelper::map(Employeedetails::find()->all(),'id','wife_name');
$emp2 = ArrayHelper::map(Employeedetails::find()->all(),'id','child1_name');
$emp3 = ArrayHelper::map(Employeedetails::find()->all(),'id','child2_name');
$print = array_merge($em,$emp,$emp1,$emp2,$emp3);
// echo "<pre>";print_r($print);exit();echo "</pre>";
echo $form->field($model, 'claim_for')->dropDownList($print,['option' => '']);
?>
The problem:
While selecting relation I am getting all the relations name in the form, but while saving it in gridview I am getting employee_id instead of merge_array id.
So here I used this code to get relation name, but I dont know whether my write code is right or wrong:
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'company.companyname',
'employee.employeecode',
'employee.name',
'claim_for',
[
'attribute' => 'claim_for',
'format' => 'raw',
'value' => function($model, $key, $index, $grid) {
$temp = $model->claim_for;
$si = Employeedetails::find()->where(['id' => $temp])->one();
return $si['father_name'];// here how to bring merged array relation name
},
],
'healthcard_no',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
I've been searching for two days but am not able to get it. If anyone can help, I'd appreciate it.
create field in claimprocess table as claim_for and store the getting value from employeedetails in seperate table. thats it, Hope it will you
claimprocesscontroller.php
$commaList = explode('-', $model->claim_for);
$model->claimer_name = $commaList[1];
employeedetails.php
echo "<option value='".$employee->id. '-' .$employee->name. "'>".$employee-RelationName7."</option>";

Categories