How to get data from model A in view B Yii2 - php

I am quite new to Yii 2 and I am currently trying to display data from the Teams model in the User view in a GridView. In Yii1 I used a function with the $data variable. In Yii2 it does not seem to work that way.
I have tried the following in the /user/index.php
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* #var $this yii\web\View */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Users');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="user-index">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a(Yii::t('app', 'Create User'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
// ['class' => 'yii\grid\SerialColumn'],
// 'iduser',
'username',
'surname',
'givenname',
'email:email',
'mobile',
'admin:boolean',
'language',
// Below is what I have been trying to do so far...
['class' => 'yii\grid\DataColumn',
'label' => 'Teams',
'value' => 'getTeams($data-teams)'
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
<?php
// And this is the function I use. In Yii1 it worked just fine.
function getTeams($data) {
$tmp = "";foreach ($data as $team) {
$tmp .=$team['name'].", ";
}
return trim($tmp, ", ");
}
?>
EDIT
In the User model this is what I have set the relation as followed:
public function getTeamIdteams()
{
return $this->hasMany(Team::className(), ['idteam' => 'team_idteam'])->viaTable('user_has_team', ['user_iduser' => 'iduser']);
}
While in the Team model I have set following relations:
public function getUserIdusers()
{
return $this->hasMany(User::className(), ['iduser' => 'user_iduser'])->viaTable('user_has_team', ['team_idteam' => 'idteam']);
}
I have been looking around but can't seem to find a solution for this one. Basically I simply want the Team to be displayed in which the user is a part of. There is a table called UserHasTeam in which the id's of the teams and users are saved.
I am a little lost on how to go about the entire thing. Any help is greatly appreciated.

I assume you have properly set relation to Team model in User model. See the Relations via Junction Table part of guide if you don't know how to set the relation.
You can use closure in $value property of DataColumn to control what value is used for column.
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
// ... other columns ...
[
'label' => 'Teams',
'value' => function($model) {
$tmp = "";
foreach ($model->teams as $team) {
$tmp .= $team->name .", ";
}
return trim($tmp, ", ");
}
],
],
]); ?>
You might also want to consider using eager loading to load all related at once instead of running query for each user in grid.

Related

Yii2 Remove initial Search

I have been searching for hours on the internet on trying to remove the initial filter on yii2 gridview. All the options point on setting a default value in the search. I would like when the index page loads, that no data is shown in the gridview only when user search. Any assistance is highly appreciated.
https://github.com/yiisoft/yii2/issues/5668
https://stackoverflow.com/questions/33608796/default-filter-in-gridview-with-yii2
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* #var $this yii\web\View */
/* #var $searchModel frontend\models\AnimalSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Animal and Animal Products Search';
$this->params['breadcrumbs'][] = $this->title;
?>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
//////prevent the dropdown from being populated when the user click back
$(window).bind("pageshow", function() {
$("#commodity").val('');
$("#species").val('');
$("#finality").val('');
$("#origin").val('');
});
</script>
<div class="animal-index">
<h1 align="center"><?= Html::encode($this->title) ?></h1>
<br>
<?php echo $this->render('_search', ['model' => $searchModel]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
// 'showHeader'=> false,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// 'id',
'commodity',
'species',
'finality',
'origin',
// 'intendeduse',
// 'risk',
// 'conditions',
// 'scu',
// 'maf',
// 'cities',
// 'fisheries',
// 'forestry',
// 'comments',
['class' => 'yii\grid\ActionColumn',
'visibleButtons' => [
'update' =>false, // or whatever condition
'delete' => false,
],
],
],
]); ?>
</div>
You need to add a condition that would yield no results whenever no search params are present
update your search model to something like this
public function search($params)
{
$query = Model::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
// this ensures no rows are returned if nothing was submitted
if (is_null($params) || empty($params)) {
$query->where('0 = 1');
return $dataProvider;
}
// ....
}

Yii2 get data from many to many relation in gridview and apply filter

i'm developing a web application with Yii2 framework, and i'm facing a problem right now. I want to display the data from a many-to-many relation in a gridview and be able to filter from those fields later on.
I've read the official documentation here, some stackoverflow post like this and other resources but can't seem to get it to work. I have 3 tables: actividad, plan_actividad and circulo_icare, actividad is related to plan_actividad and circulo_icare is also related to it (plan_actividad is the junction table). So i have defined the following relations in my Actividad model:
class Actividad extends \yii\db\ActiveRecord
{
....
public function getPlanActividad()
{
return $this->hasMany(PlanActividad::classname(), ['act_id' => 'act_id']);
}
public function getCirculo()
{
return $this->hasMany(CirculoIcare::classname(), ['cirica_id' => 'act_id'])->via('planActividad');
}
...
}
The in my view index.php i'm trying to show the values in a gridview like this:
<?= GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// 'act_id',
['attribute' => 'Codigo Evento', 'value' => 'act_numorden'],
['attribute' => 'Nombre Evento', 'value' => 'act_nombre'],
['attribute' => 'Fecha Evento', 'value' => 'act_fecha'],
['attribute' => 'Locacion', 'value' => 'locacion.loc_nombre'],
[
'attribute' => 'Circulo',
'value' => 'circulo.cirica_nombre',
],
['attribute' => 'Circulo id',
'value' => 'planActividad.cirica_id',
],
// 'act_horaini',
// 'act_horafin',
// 'act_idencuesta',
// 'act_vigencia:boolean',
// 'loc_id',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
The problem is, i can't get any values to show with the circulo relation, it always shows (not set). If i change hasMany in getPlanActividad() with hasOne() then it shows some values (only 2 of 11 it should, based on the cirica_id that exist on plan_actividad table) but these are not correct anyway. I know that i can filter for those fields later on in search view but i don't really understand why the relations doesn't work as i expected.
Any help would be greatly appreciated, let me know if more info is needed and thank you in advance.
Answering my own question (credits to softark from the yii official forums).
In order for the relation to work as expected, I had to change:
public function getCirculos()
{
return $this->hasMany(CirculoIcare::classname(), ['cirica_id' => 'act_id'])->via('planActividad');
}
to
public function getCirculos()
{
return $this->hasMany(CirculoIcare::classname(), ['cirica_id' => 'cirica_id'])->via('planActividad');
}
and use a callback function in the gridview to display the correct values, since a hasMany relation gives an array of models and not a single model. So I modified the gridview code to:
<?= GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
...
['attribute' => 'circulo',
'value' => function($model){
$items = [];
foreach($model->circulos as $circulo){
$items[] = $circulo->cirica_nombre;
}
return implode(', ', $items);
}],
...
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
This gives the expected results. You can then apply filter by the relation fields easily by adapting the search model.

Yii2: Status 'enabled' & 'disabled' helper function - dynamic use

I would like to combine 2 functions together, to consolidate code, and to be dynamic depending on how it is used. I do not know if this is possible.
First, lets lay out the basic use. In my example, I have Post and PostCategory models (and CRUD built). You create a category, then create a new post and assign it to the category. A post has the ability to be enabled or disabled. Essentially, you can create some new posts and have them not be viewable to the end users until your ready. One use-case would be a drip feed system, where you could add 100 posts and have one switch to enabled every x days. That is beyond the scope of this.
views\post\_form.php
<div class="post-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'category_id')->dropDownList(
$model->getPostCategoryConst(),
['prompt'=> '- Category -']
)->label('Category')
?>
<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'text')->textarea(['rows' => 6]) ?>
<?= $form->field($model, 'status')->dropDownList(
$model->getPostStatusConst(),
['prompt'=> '- Status -']
) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
Notice the drop down lists for category_id and status and the functions they call
\common\models\Post.php
const STATUS_ENABLED = 1;
const STATUS_DISABLED = 0;
public function getCategory()
{
return $this->hasOne(PostCategory::className(), ['id' => 'category_id']);
}
/* -- Added -- */
public function getPostCategoryConst()
{
return ArrayHelper::map(PostCategory::find()->orderBy('name DESC')->all(), 'id', 'name');
}
public function getPostStatusConst()
{
return [
self::STATUS_DISABLED => 'Disabled',
self::STATUS_ENABLED => 'Enabled',
];
}
Now this works just fine :) However, I don't like using get as in getPostStatusConst() as it isn't accessed like $model->postStatusConst similar to how relations are with the "getter".
I would like to use these as "getters" as well though. In the index and view, it would be nice to also call the same functions. Instead of returning an array, to return a "nice name" such as "Enabled" or "Disabled"
For the sake of this, I won't rename the function, as I don't want to add any more confusion.
views\post\view.php
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'category.name',
'name',
'text:ntext',
'postStatusConst', // <-- Calls getPostStatusConst()
'created_at:datetime',
'updated_at:datetime',
],
]) ?>
Notice postStatusConst is the same function as we used in _form for our create action. In the _form, it needed to return an array for the drop down list. In our view, it just needs to return a nice name such as Enabled or Disabled.
I Tried
I tried this function in the Post model:
public function getPostStatusConst()
{
if ( isset($this) ) {
return ($this->status === self::STATUS_ENABLED) ? 'Enabled' : 'Disabled';
}
return [
self::STATUS_DISABLED => 'Disabled',
self::STATUS_ENABLED => 'Enabled',
];
}
That obviously didn't work :) I didn't expect it to, because I know $this references itself in a class. It just shows what I am going for.
In relations, the hasOne() seems to know whether we are using it as a direct call (Post::getCategory) or inline ($model->category->name)..
Question
Is it possible to have getPostStatusConst() do the same? To use as $model->postStatusConst to display Enabled or Disabled nicely, or as Post::getPostStatusConst() to get the array for the drop down.
It is possible but really not worth all the changes in code. You would have to override magic __get() method and think about some way to store and access both returns in one structure.
I would leave getPostStatusConst() with current status name and add other method (even static) with the list of statuses for dropdown list.
I was pretty close. I wasn't thinking PHP OOP lines, but more Yii. A few Google searches and I slapped my forehead. When using frameworks, you forget your even writing in PHP sometimes ;)
public function getPostStatus()
{
if ( isset($this) && get_class($this) == __CLASS__) {
// not static
return ($this->status === self::STATUS_ENABLED) ? 'Enabled' : 'Disabled';
}
return [
self::STATUS_DISABLED => 'Disabled',
self::STATUS_ENABLED => 'Enabled',
];
}
I renamed the function so that it makes more sense.
It works everywhere. Lets see the index of my CRUD:
views\post\index.php
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'category.name',
'name',
//'text:ntext',
'postStatus',
// 'created_at',
// 'updated_at',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
views\post\view.php
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'category.name',
'name',
'text:ntext',
'postStatus',
'created_at:datetime',
'updated_at:datetime',
],
]) ?>
and views\post\_form.php
<?= $form->field($model, 'status')->dropDownList(
Post::getPostStatus(),
['prompt'=> '- Status -']
) ?>
All those cases seem to work fine. Anyone have any cases this would not work?

get values from table with foreign key

Hi I have 2 tables and models for them. First of them is
User
and second
News
I want to take user id from table News and draw her name and surname. There is printscreen of table News:
I am trying to use the function:
public function getUrUser()
{
$q= News::find()->where(['urUser_Id' =>'Id'])->one();
$RoyalUserData=User::findOne($q);
//$RoyalUserData= User::find()->where(['Id' => $q])->one();
return $RoyalUserData->Name;
}
But this doesnt work. Only when I prescribe to q value 3 for egzample then it work. I know that is the wrong code in my first line of my function probably. I know that is easy but I am a beginner and I've fought with the code for about 1 hour. Can anyone help me?
In my view I use this:
<?=$model->getUrUser();?>
And I use this in ListView.
My controller:
public function actionIndex()
{
$model = new NewsForm();
$searchModel = new NewsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->setSort(['defaultOrder' => ['Id'=>SORT_DESC]]);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->saveNews();
return $this->redirect(['/content/news']);
} else {
return $this->render('index', [
'model' => $model,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
}
My index where i use _item:
echo ListView::widget( [
'dataProvider' => $dataProvider,
'itemView' => '_item',
], $options = ['class' => 'sorter'] ); ?>
And content of _item
<?php
use yii\helpers\Html;
?>
Treść Newsa:
<?=$model->Text;?> <br>
Autor:
<?=Yii::$app->user->identity->Name?>
<?=Yii::$app->user->identity->Surname?> <br>
Status Newsa:
<?=$model->cnNewsContentType_Id;?> <br>
<?= Html::a('Update', ['update', 'id' => $model->Id], ['class' => 'btn btn-primary']) ?>
<?= Html::a('Delete', ['delete', 'id' => $model->Id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
]) ?><br><br><br>
The News::find()->where(['urUser_Id' =>'Id'])->one() return a model not a field
then you must get the id field by the model this way
public function getUrUser($id)
{
// you already have the news model so don't need retrieve it
// it's enough pass the urUser_Id by $id
$RoyalUserData=User::findOne($id);
return $RoyalUserData->Name;
}
Then if you want show the ($RoyalUserData) Name in _item
<?=$model->getUrUser($model->urUser_Id);?>
public function getUrUser()
{
$q = News::find()->where(['urUser_Id' =>'Id'])->one();
return $q->user->name;
}
In user model you should create relation:
class News extends ActiveRecord
{
// ...
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
}

Yii2 Use two Models in one GridView

I have a GridView which displays an employee's summary of his/her payslip. Here's a screenshot for more understanding:
Those highlighted columns come from a different model which is the User model while the rest come from the Payslip model.
How do I merge 2 models in one GridView? Because in GridView, it is more likely for you to have a single model to display data. But how about 2 models?
Here's a code in my Payslip model, note that getUser() is generated with gii since user_id is a foreign key in my payslip table:
public function getUser()
{
return $this->hasOne(User::className(), ['user_id' => 'user_id']);
}
public function getFirstName()
{
return $this->user ? $this->user->fname : 'First Name';
}
public function getLastName()
{
return $this->user ? $this->user->lname : 'Last Name';
}
The Payslip controller:
public function actionIndex()
{
$searchModel = new PayslipSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
And then the Payslip view:
<?php
echo GridView::widget([
'dataProvider' => $dataProvider,
//'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'payslip_id',
//'store_id',
'firstName',
'lastName',
'total_earnings',
'total_deduction',
'net_pay',
['class' => 'yii\grid\ActionColumn'],
],
]);
?>
BTW, in this example, I just created payslip #1 manually to give you a demo.
Additional info:
The logged in user is a BizAdmin user, and all of BizAdmin's staff users should be displayed in the payslip table (that table above) even if these staff users still don't have any payslip created for them. So by default, that table will be occupied already with staff users under the logged in BizAdmin user, and those staff users who still have no payslips created will be indicated "Create Payslip"
Here's an example in KashFlow:
Update view to:
<?php
echo GridView::widget([
'dataProvider' => $dataProvider,
//'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'payslip_id',
//'store_id',
['value' => function ($data) {return $data->getFirstName();}, 'label' => 'First Name'],
['value' => function ($data) {return $data->getLastName();}, 'label' => 'LastName'],
'total_earnings',
'total_deduction',
'net_pay',
['class' => 'yii\grid\ActionColumn'],
],
]);
?>
And in $searchModel->search add you relation, like that:
$query = Payslip::find()->with(['user']);
Read for data column - http://www.yiiframework.com/doc-2.0/guide-output-data-widgets.html#data-column

Categories