Yii2 ActiveDataProvider sort using count in relational - php

Yii2 ActiveDataProvider sort using count in relational ?
I've 3 tables, bellow :
And I displaying data from table inventory_device_type, here is the code :
Controller :
<?php
namespace app\controllers\inventory;
use Yii;
use yii\base\Action;
use app\models\tables\InventoryDeviceType;
class DeviceInventory extends Action
{
public function run()
{
$model = new InventoryDeviceType();
$dataProvider = $model->search(Yii::$app->request->post());
return $this->controller->render('device-inventory',[
'dataProvider' => $dataProvider,
]);
}
}
Model :
<?php
namespace app\models\tables;
use yii\db\ActiveRecord;
use yii\data\ActiveDataProvider;
use yii\web\NotFoundHttpException;
class InventoryDeviceType extends ActiveRecord
{
public static function tableName()
{
return 'inventory_device_type';
}
public function rules()
{
return [
[['name','id_device_vendor'],'required'],
['name','unique']
];
}
public function attributeLabels()
{
return [
'id_device_vendor' => 'Device Vendor',
];
}
/** queries **/
public function findData($id)
{
if(($data = self::findOne($id)) == null){
throw new NotFoundHttpException;
}else{
return $data;
}
}
public function getNormal()
{
return $this->getList()->where(['inventory_device_list.device_condition' => 'normal'])->count();
}
public function getBroken()
{
return $this->getList()->where(['inventory_device_list.device_condition' => 'broken'])->count();
}
public function getSold()
{
return $this->getList()->where(['inventory_device_list.device_condition' => 'sold'])->count();
}
public function getTotal()
{
return $this->getList()->where(['!=','inventory_device_list.device_condition',''])->count();
}
public static function getVendorList($sort=[])
{
return InventoryDeviceVendor::find()->orderBy($sort)->all();
}
public function search($params, $spesific=[],$sort=[])
{
$query = self::find();
$query->where($spesific);
$query->joinWith(['list']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => $sort]
]);
$dataProvider->sort->attributes['vendor_name'] = [
'asc' => ['inventory_device_vendor.name' => SORT_ASC],
'desc' => ['inventory_device_vendor.name' => SORT_DESC],
];
/** sort on page device-inventory **/
$dataProvider->sort->attributes['device_vendor'] = [
'asc' => ['inventory_device_vendor.name' => SORT_ASC],
'desc' => ['inventory_device_vendor.name' => SORT_DESC],
];
/** sort on page device-inventory end **/
return $dataProvider;
}
/** queries end **/
/** relation **/
public function getListLeft()
{
return InventoryDeviceList::find();
}
public function getList()
{
return $this->hasMany(InventoryDeviceList::className(),['id_device_type' => 'id']);
}
public function getVendorLeft()
{
return InventoryDeviceVendor::find();
}
public function getVendor()
{
return $this->hasOne(InventoryDeviceVendor::className(),['id' => 'id_device_vendor']);
}
/** relation end **/
}
and here is the view :
<?=
GridView::widget([
'dataProvider' => $dataProvider,
'layout' => '<div class="table-responsive">{items}</div> {summary} {pager}',
'columns' => [
[
'class' => 'yii\grid\SerialColumn',
'headerOptions' => ['width' => 50]
],
'name',
[
'attribute' => 'device_vendor',
'class' => 'yii\grid\DataColumn',
'value' => function($data){
return $data->vendor->name;
}
],
[
'attribute' => 'normal',
'class' => 'yii\grid\DataColumn',
'format' => 'html',
'value' => function($data){
return Html::a($data->getNormal(),'#',['class' => 'label label-success']);
},
],
[
'attribute' => 'sold',
'class' => 'yii\grid\DataColumn',
'format' => 'html',
'value' => function($data){
return Html::a($data->getSold(),'#',['class' => 'label label-warning']);
},
],
[
'attribute' => 'broken',
'class' => 'yii\grid\DataColumn',
'format' => 'html',
'value' => function($data){
return Html::a($data->getBroken(),'#',['class' => 'label label-danger']);
},
],
[
'attribute' => 'Total',
'class' => 'yii\grid\DataColumn',
'format' => 'html',
'value' => function($data){
return Html::a($data->getTotal(),'#',['class' => 'label label-primary']);
},
],
],
]);
?>
and how to sort it, like normal, broken, and, sold ?
here is : the result view :
the header normal, sold, broke and all not sortable, i can sort by relation if not using count like this code (like Device Vendor):
$dataProvider->sort->attributes['device_vendor'] = [
'asc' => ['inventory_device_vendor.name' => SORT_ASC],
'desc' => ['inventory_device_vendor.name' => SORT_DESC],
];

Related

Yii2 | GridView table, communication between models does not work

The connection between the modules does not work, and because of this, the data in the table is not displayed. I can not understand why
Code in the controller
public function actionIndex()
{
$searchModel = new SuggestedNewsSearch();
$dataProvider = $searchModel->getAllNews(Yii::$app->request->queryParams);
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
}
code in suggestedNewsSearch.php
class SuggestedNewsSearch extends SuggestedNews
{
public function getAllNews($params)
{
$query = $this::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if ($this->validate() AND !($this->load($params))) {
return $dataProvider;
}
if (!empty($this->getAttribute('title'))) {
$query->andFilterWhere(['LIKE', 'title', $this->getAttribute('title')]);
}
if (!empty($this->getAttribute('category'))) {
$query->andFilterWhere(['LIKE', 'category', $this->getAttribute('category')]);
}
if (!empty($this->getAttribute('status'))) {
switch (mb_strtolower($this->getAttribute('status'))) {
case $this::APPROVED:
$status = $this::ACTIVE_STATUS;
break;
case $this::NOT_APPROVED:
$status = $this::DEACTIVATED_STATUS;
break;
}
$query->andFilterWhere(['=', 'status', $status]);
}
return $dataProvider;
}
}
code on SuggestedNews.php
class SuggestedNews extends \yii\db\ActiveRecord
{
CONST ACTIVE_NEWS = 1;
CONST ACTIVE_STATUS = 1;
CONST DEACTIVATED_STATUS = 0;
CONST APPROVED = 'одобренно';
CONST NOT_APPROVED = 'не одобренно';
/**
* {#inheritdoc}
*/
public static function tableName()
{
return 'suggested_news';
}
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['news'], 'string'],
[['category', 'status'], 'integer'],
[['date'], 'safe'],
[['title', 'news_source'], 'string', 'max' => 255],
[['category'], 'exist', 'skipOnError' => true, 'targetClass' => Category::className(), 'targetAttribute' => ['category' => 'id']],
];
}
/**
* {#inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => 'Title',
'news' => 'News',
'category' => 'Category',
'status' => 'Status',
'date' => 'Date',
'news_source' => 'News Source',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCategory()
{
return $this->hasOne(Category::className(), ['id' => 'category']);
}
public function deleteNewsById($id)
{
$customer = $this::findOne($id);
if ($customer->delete()) return true;
else return false;
}
public function getNewsByIdWithCategory($id){
return $this::find()->where(['id' => $id])->with('category')->one();
}
}
code on Category.php
class Category extends \yii\db\ActiveRecord
{
CONST STATUS_CATEGORY_OFF = 0;
CONST STATUS_CATEGORY_ON = 1;
CONST NEW_CATEGORY_INTEGER = 01;
CONST NEW_CATEGORY_NAME = 'New Category';
/**
* {#inheritdoc}
*/
public static function tableName()
{
return 'category';
}
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['name', 'status_category'], 'required'],
[['status_category'], 'integer'],
[['name'], 'string', 'max' => 255],
];
}
/**
* {#inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
'status_category' => 'Status Category',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getSuggestedNews()
{
return $this->hasMany(SuggestedNews::className(), ['category' => 'id']);
}
public function getAllCategories(){
return $this::find()->where(['status_category' => $this::STATUS_CATEGORY_ON])->all();
}
}
my index.php file(view)
<?php echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
[
'attribute' => 'title',
'format' => 'text',
'label' => 'title',
'filter' => true,
],
[
'attribute' => 'category.Category',
'format' => 'text',
'label' => 'Category',
],
[
'attribute' => 'status',
'filter' => true,
'value' => function($model) {
if($model->status == 1){
return $model::APPROVED;
}else{
return $model::NOT_APPROVED;
}
}
],
'date',
[
'class' => 'yii\grid\ActionColumn',
],
],
]);
?>
and on result i have this:result table
enter image description here
table category
table suggested_news
You field name and relation are the same, so You have to change Category relation name like this:
/**
* #return \yii\db\ActiveQuery
*/
public function getCategory1()
{
return $this->hasOne(Category::className(), ['id' => 'category']);
}
//Gridview
//...
[
'attribute' => 'category',
'label' => 'Category',
'value' => function($model){
return $model->category1->name;
}
],
//...
//or
//...
[
'attribute' => 'category1.name',
'format' => 'text',
'label' => 'Category',
],
Hope it will helps.

Yii2 Grid view syntax to access data from related model

for example I can retrieve the data in the view with this command:
<?=$model->instructor->manager->location['location_title']?>
I have a relation defined in Instructor model like:
/**
* #return mixed
*/
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
/**
* #return mixed
*/
public function getManager()
{
return $this->hasOne(Manager::className(), ['user_id' => 'manager_id']);
}
and in Manager Model relations are defined like this:
/**
* #return mixed
*/
public function getLocation()
{
return $this->hasOne(Location::className(), ['id' => 'location_id']);
}
/**
* #return mixed
*/
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
Now How I can retrieve the same data in grid-view as also add filter for the same.
Thanks.
update: Gridview Code
echo GridView::widget(
[
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'first_name',
'last_name',
// 'username',
// 'auth_key',
// 'password_hash',
// 'password_reset_token',
'email:email',
// 'phone',
//'user_role',
['attribute' => 'created_at', 'label' => 'Last Login', 'value' => function ($data) {
return $data->getLast($data);
},
'contentOptions' => ['style' => 'width:100px']
],
['attribute' => 'created_at', 'label' => 'Create Date', 'contentOptions' => ['style' => 'width:100px'], 'value' => function ($data) {return date('M d, Y', $data->created_at);}
],
// 'updated_at',
['attribute' => 'status', 'value' => function ($data) {
return $data->getStatus($data);
},
'filter' => ['10' => 'Active', '0' => 'Deactive'],
'contentOptions' => function ($data) {
$clr = $data->status == 10 ? 'green' : 'red';
return ['style' => 'width:80px;font-weight:bold;color:' . $clr];
}
]
]
]
);
Update-1
public function actionInstructor()
{
$cond = "user_role='instructor' ";
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams, $cond);
return $this->render(
'instructor', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider
]
);
}
update-2
public function getInstructor()
{
return $this->hasOne(Instructor::className(), ['user_id' => 'id']);
}
You can specify relations either as a string of relation names concatenated with . like below. Make sure you have the relations defined in the respective models the below is for the $dataProvider from the UserSearch model and your User Model should have the relation getInstructor() defined
[
'intructor.manager.location.location_title'
]
or
[
'label' => 'Manager Location',
'value' => function($model){
return $model->instructor->manager->location->location_title;
}
]
if I have the $dataProvider for the UserSearch model below should be the gridview code.
<?=GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'first_name',
'last_name',
[
'label'=>'Manager Location'
'attribute'=>'manager_id'.
'value'=>function($model){
return $model->instructor->manager->location->location_title;
}
]
// 'username',
// 'auth_key',
// 'password_hash',
// 'password_reset_token',
'email:email',
// 'phone',
//'user_role',
['attribute' => 'created_at', 'label' => 'Last Login', 'value' => function ($data) {
return $data->getLast($data);
},
'contentOptions' => ['style' => 'width:100px'],
],
['attribute' => 'created_at', 'label' => 'Create Date', 'contentOptions' => ['style' => 'width:100px'], 'value' => function ($data) {return date('M d, Y', $data->created_at);}],
// 'updated_at',
['attribute' => 'status', 'value' => function ($data) {
return $data->getStatus($data);
},
'filter' => ['10' => 'Active', '0' => 'Deactive'],
'contentOptions' => function ($data) {
$clr = $data->status == 10 ? 'green' : 'red';
return ['style' => 'width:80px;font-weight:bold;color:' . $clr];
},
],

GridView filter model in Yii2

I have a gridview with a dataprovider which has joined two tables as query:
$applicationDataProvider = new ActiveDataProvider([
'query' => Application::find()->with('applicantInfo')
->where(['job_posting_id' => $jobPosting->id,
'deleted_flag' => 0])->orderBy('created_at desc'),
'pagination' => [
'pageSize' => 5
]
]);
and here is my GridView:
<?= GridView::widget([
'dataProvider' => $applicationDataProvider,
'filterModel'=>$searchModel,
'columns' => [
'id',
[
'class' => yii\grid\DataColumn::className(),
'headerOptions' => ['style' => 'color:#337ab7'],
'header' => 'სახელი',
'label'=>'name',
'value' => function ($data) {
return $data->applicantInfo->first_name . ' ' . $data->applicantInfo->last_name;
}
],
'applicantInfo.email',
'applicantInfo.phone',
'created_at:date',
[
'class' => 'yii\grid\ActionColumn',
'headerOptions' => ['style' => 'color:#337ab7'],
'template' => '{view}',
'buttons' => [
'view' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-eye-open"></span>', $url, [
'title' => Yii::t('app', 'გახსნა'),
]);
}
],
'urlCreator' => function ($action, $model, $key, $index) {
if ($action === 'view') {
$url = '/app-info/?id=' . $model->id;
return $url;
}
}
...
in a search model I have email field only because I want to gridview has just only email search field.
class ApplicationSearch extends Application
{
public function rules()
{
return [
[['email'], 'safe'],
];
}
but here is not drawn search field for email, how can I fix it?
You need to add email attribute to ApplicationSearch to store value from filter:
class ApplicationSearch extends Application {
public $email;
public function rules() {
return [
[['email'], 'safe'],
];
}
}
Use this attribute for filtering:
$applicationDataProvider = new ActiveDataProvider([
'query' => Application::find()->with('applicantInfo')
->where(['job_posting_id' => $jobPosting->id, 'deleted_flag' => 0])
->andFilterWhere(['like', 'applicantInfo.email', $this->email])
->orderBy('created_at desc'),
'pagination' => [
'pageSize' => 5
]
]);
And use value from relation in grid:
// ...
[
'attribute' => 'email',
'value' => function ($data) {
return $data->applicantInfo->email;
},
],
'applicantInfo.phone',
// ...

Yii2: display some records at the top of GridView widget

In Yii2 app I have model Document which can belong to user. Belonging is set with owner_id field.
In view I display list of Documents using GridView widget. Default sort is by document_id.
Every user sees all documents (event if specific document doesn't belong to him).
But I need to display documents which belongs to current logged in user at the top of GridView. How can I do this?
I suppose I should make some changes to Document::search() method by can't find out what excactly I should do.
Here is my controller action:
public function actionIndex()
{
$modelFullName = $this->modelFullName;
$model = new $modelFullName();
$dataProvider = $model->search(Yii::$app->request->queryParams);
return $this->render(
'index',
[
'model' => $model,
'dataProvider' => $dataProvider,
]
);
}
Part of view:
echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $model,
'columns' => [
[
'attribute' => 'document_id',
],
[
'attribute' => 'status',
'format' => 'raw',
'value' => function($model) {
return $model->statusString;
},
],
[
'attribute' => 'title',
],
[
'attribute' => 'date_created'
],
[
'attribute' => 'client_id',
'value' => function($model) {
return $model->client ? $model->client->title : '';
},
'filter' => ArrayHelper::map(Clients::find()->all(), 'client_id', 'title')
],
[
'attribute' => 'project_id',
'value' => function($model) {
return $model->project ? $model->project->title : '';
},
'filter' => ArrayHelper::map(Projects::find()->all(), 'project_id', 'title')
],
[
'class' => yii\grid\ActionColumn::className(),
'template' => '{view} {delete}',
'buttons' => [
'view' => function ($url, $model) {
return $this->context->getBtn('view', $model);
},
'delete' => function ($url, $model) {
if (Yii::$app->user->can('deletePrsSum')) {
return $this->context->getBtn('delete', $model);
}
},
],
'visibleButtons' => [
'update' => Yii::$app->user->can('updateDocument'),
'delete' => Yii::$app->user->can('deleteDocument'),
]
],
],
]);
Current Document::search() implementation:
public function search($params)
{
$query = self::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => ['document_id' => SORT_ASC]
]
]);
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
if ($this->client_id) {
$query->andFilterWhere([
'client_id' => $this->client_id,
]);
}
if ($this->project_id) {
$query->andFilterWhere([
'project_id' => $this->project_id,
]);
}
return $dataProvider;
}
UPDATE
So how I managed to do this:
public function search($params)
{
$userId = Yii::$app->user->identity->user_id;
$query = self::find()->select(
"*, IF(owner_id={$userId},(0),(1)) as sort_order"
)->orderBy([
'sort_order' => SORT_ASC,
'document_id' => SORT_ASC
]);
//further code...
Add a filter in your search function to filter with the id of the logged in person/user:
$query->andFilterWhere(['owner_id' => Yii::$app->user->identity->id]);

GridView search filters don't appear Yii2

I've created GridView widget in my view with following params:
<?= GridView::widget([
'dataProvider' => $provider,
'filterModel' => $searchModel,
'summary' => '<br>',
'showOnEmpty' => false,
'columns' => [
[
'attribute' => 'name',
'value' => function ($model) {
return substr($model->name, 0, 50);
},
],
[
'label' => "Ім'я користувача",
'attribute' => "user.name",
'value' => function ($model) {
return substr($model->user->name, 0, 50);
},
],
[
'label' => 'Назва предмету',
'attribute' => "subjects.name",
'value' => function ($model) {
return substr($model->subjects->name, 0, 50);
},
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{view} {delete}'
]
],
]) ?>
My controller has such code:
$searchModel = new DocumentsSearch();
$provider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'provider' => $provider,
'searchModel' => $searchModel
]);
Also I'm using DocumentSearch model that has following description:
class DocumentsSearch extends Documents
{
public $username;
public $subject_name;
public function rules()
{
return [
[['username', 'subject_name'], 'safe']
];
}
public function search($params)
{
$query = Documents::find()
->joinWith(['user', 'subjects']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pagesize' => 30,
],
]);
$dataProvider->sort->attributes['user.name'] = [
'asc' => ['user.name' => SORT_ASC],
'desc' => ['user.name' => SORT_DESC],
];
$dataProvider->sort->attributes['subjects.name'] = [
'asc' => ['subjects.name' => SORT_ASC],
'desc' => ['subjects.name' => SORT_DESC],
];
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andWhere('user.name LIKE "%' . $this->name . '%" ');
$query->andWhere('user.name LIKE "%' . $this->username . '%" ');
$query->andWhere('subjects.name LIKE "%' . $this->subject_name . '%" ');
return $dataProvider;
}
}
DocumentSearch extends Document model. And I've next relations:
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'owner_id']);
}
public function getSubjects()
{
return $this->hasOne(Subjects::className(), ['id' => 'subject_id']);
}
All these things give me next result:
Before asking I've tried to create inputs by myself using filter param for each column like 'filter'=>Html::input('text','DocumentSearch[subjects.name]')
It triggers JS to send the request but if (!($this->load($params) && $this->validate())) method obvious returns false.
Try using the correspondenting name in rules
[
'label' => "Ім'я користувача",
'attribute' => "username",
'value' => function ($model) {
return substr($model->user->name, 0, 50);
},
],
[
'label' => 'Назва предмету',
'attribute' => "subject_name",
'value' => function ($model) {
return substr($model->subjects->name, 0, 50);
},
],
see this guide http://www.yiiframework.com/doc-2.0/guide-output-data-widgets.html#filtering-data
// only fields in rules() are searchable
Or you must extend you search model for calculated or related field adding eventually the proper value in rules

Categories