I would like to have a CRUD table, and to be specific I don't need to edit/delete records, only the part with filtering results, which appears at the top of the CRUD generated table, is the part that I want to have. My table contains data from 1 table in database, but I have one column that is not connected to this or any other table in database (it's a comment, that is auto-generated, based on one column from the table). I generate my table manually, but I'd like to add the part with filtering. I have no idea how to do it though. Is it possible in Yii2 to do it manually, or do I have to use CRUD generator?
I don't use CRUD generator as it doesn't generate the code I want (and I think it also doesn't generate filters?). I use a basic template which fits to nearly all gridviews I need to display. Here's one example that can give you a filter:
use yii\grid\GridView;
/** #var array $userTypes All user types (classes) */
// ...
echo GridView::widget([
'dataProvider' => $modelProvider,
'filterModel' => $model,
'columns' => [
[
'attribute' => 'id',
'format' => 'raw',
'filter' => Html::input('text', 'User[id]', $model->id, ['class' => 'form-control', 'placeholder' => 'Filter ID']),
[
'attribute' => 'type',
'format' => 'raw',
'filter' => Html::activeDropDownList($model, 'type', $userTypes, ['class' => 'form-control', 'prompt' => 'All types']),
],
]);
In here I use 2 different input field types (text and dropdown).
For Html::input, first is type (text), then full attribute name (model name + attribute name), then default value and finally other options.
For Html::activeDropDownList we have model first, then attribute name (only), that items list (array) and finally other options.
I guess you are talking about GridView, if yes then you can have your own columns in it, no problem. lets call that column comment as you mentioned
If you use basic template, generated by Gii, and you also generate search class for that model, then you comment to safe attributes and add code for that code to be able to filter it.
If you could be more detailed about mentioned column, possible values or algorythm you might get more suitable answers...
Also take look at Yii 2.0: Displaying, Sorting and Filtering Model Relations on a GridView
Lets say your model is called Xyz as you did not provided any. Also I named column from your table as column_from_your_table and your virtual column as comment
In your model Xyz you will add relation (method with specific name to define it)
public function getComment()
{
$column_from_your_table = $this->column_from_your_table;
$comment = '';
// your code to specify the relation
// ...
// this is value dislpayed in column grid
return $comment;
}
and in file XyzSearch.php in \app\models\
you will have something like this (edit to your needs ofcourse)
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\db\Expression;
/**
* XyzSearch represents the model behind the search form about `app\models\Xyz`.
*/
class XyzSearch extends Xyz
{
public $comment; // your virtual column
/**
* #inheritdoc
*/
public function rules()
{
return [
// add it to safe attributes
[['comment'], 'safe'],
// you will have more rules for your other columns from DB probably
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = Xyz::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
// I dont know your how it is your comment column autogenerated
// or what is the relation, so I give you just basic idea
// your algorythm needs to be able to be rewritten in SQL
// otherwise I dont know how you could possibly sort it
$dataProvider->sort->attributes['comment'] = [
'asc' => [Xyz::tableName().'.column_from_your_table' => SORT_ASC],
'desc' => [Xyz::tableName().'.column_from_your_table' => SORT_DESC],
'default' => SORT_ASC,
];
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// again, you will have more filtering conditions from your generated code
// then you will add your custom filtering condition
// I dont know your how it is your comment column autogenerated
// or what is the relation, so I give you just basic idea
$query->andFilterWhere(['like', Xyz::tableName().'.column_from_your_table', $this->comment]);
return $dataProvider;
}
}
finaly in your view file add your virutal column
<?php echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// other columns from database
// ...
'comment',
['class' => 'yii\grid\ActionColumn'],
]
]); ?>
Related
I'm trying the 4.1 new feature "Inline create", but I can't seem to associate the ids of the items created. Let me explain what I'm doing / what I want:
I have "Folders" that have "Chapters" inside (so 1-n relation).
My code:
CRUD::addField([ //Folder crud
'name' => 'chapters',
'type' => 'relationship',
'label' => 'Unidad',
'model' => "App\Models\Chapter",
'inline_create' => [
'entity' => 'chapter',
'modal_class' => 'modal-dialog modal-xl',
'modal_route' => route('chapter-inline-create'),
'create_route' => route('chapter-inline-create-save'),
]
]);
protected function setupCreateOperation() //Chapter crud
{
CRUD::setValidation(ChapterRequest::class);
CRUD::addField([
'name' => 'name',
'type' => 'text',
'label' => 'Nombre'
]);
}
public function chapters() //Folder model
{
return $this->hasMany(Chapter::class);
}
public function folder() //Chapter model
{
return $this->belongsTo(Folder::class);
}
It creates the main item and the related items no problem, but it doesn't actually relate them in the database at any point.
Any clue of what I might be doing wrong? Followed the docs but can't seem to make it work.
Thank you.
Do you have the right column names in the db ? The columns that are making the relationship possible, i.e in the folder table you should have a column named something like chapter_name or chapter_id, to identify the chapter where the folder belongs to.
Moreover, if those columns do not follow laravel conventions you need to add them as the second and third parameter when you are implementing the relationship in the models
More details here https://laravel.com/docs/8.x/eloquent-relationships#one-to-many
One note on this... I was running into this issue and realized that I forgot to make the parent_id fillable on my child model.
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'parent_id',
]
I have a model Certificates which has a foreign key application_id of another model called Application. So each certificate belongs to the single application.
Now there is a situation where I would like to show all the certificates of the existing user. The user id exist inside the application model like user_id.
This is the query
SELECT * FROM `certificates`
inner join applications b ON
application_id = b.id where b.user_id = 7
Now based on the records coming from the above query I would like to show some columns of the certificates and some from the applications using grid view. But for some reasons, if records are more than one then I don't get any column data from the application.
<?php Pjax::begin(); ?> <?= GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'application_id',
'verified_application_file_path',
'certificate_name',
'application.name',
'application.user_id',
[
'attribute' => 'creation_date',
'format' => ['date', 'php:d/m/Y']
],
[
'attribute' => 'expiry_date',
'format' => ['date', 'php:d/m/Y']
],
],
]); ?>
<?php Pjax::end(); ?></div>
The above grid shows name and user id if a single record get return otherwise it shows "Not set". I Am not sure why 'application.name' and 'application.user_id'are not working when more than one records receive.
Here is my query using yii2
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search_vendor_certificates($user_id)
{
$query = ApplicationCertificates::find()->joinWith('application b',true , 'INNER JOIN')->where(["b.user_id"=>$user_id]);
// add conditions that should always apply here
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
]);
return $dataProvider; }
I will appreciate if someone would tell me what is the mistake I am doing in displaying the proper attributes of the application model.
First of all (don't use this, i'll show u an logical mistake):
->joinWith('application b',true , 'INNER JOIN')
U set alias for application b, and then in GridView use application. Anyway, it's still bad if u rename it to b in GridView.
Based on this answer:
Model TableTwo:
public function getTableOneRecord()
{
return $this->hasOne(TableOne::className(), ['id' => 't1_id']);
}
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecord');
}
And the view file:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
't1_id',
'tableOneRecord.id',
't3_id',
'tableThreeRecord.id',
'tableThreeRecord.t4_id',
'tableFourRecord.id',
],
]);
In easiest way to say, your relation from search won't work in GridView, you have to define relations in Model and then use thems ;)
See the code on GitHub
in index.php :
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'ID_REQUEST',
'NOMOR_SURAT',
[
'label' => 'Nama Depan',
'attribute' => 'ID_KARYAWAN',
'value' => 'iDKARYAWAN.FIRST_NAME'
],
[
'label' => 'Nama Belakang',
'attribute' => 'ID_KARYAWAN',
'value' => 'iDKARYAWAN.LAST_NAME'
],
which is iDKARYAWAN is relation from another table in my model
class Request extends \yii\db\ActiveRecord {
/**
* #inheritdoc
*/
public static function tableName() {
return 'ytms_it.request';
}
public function getIDKARYAWAN() {
return $this->hasOne(Karyawan::className(), ['ID_KARYAWAN' => 'ID_KARYAWAN'])->from(Karyawan::tableName(). ' b');
}
How to merge those two column ?
For the elp, thanks.
Create method called getFullName() in related model and calculate full name using PHP concatenation:
use yii\helpers\Html;
...
/**
* #return string
*/
public function getFullName()
{
return Html::encode($this->name . ' ' . $this->surname);
}
Optionally define a label for it in attributeLabels() method of related model:
`fullName` => 'Label for full name',
Then in GridView it's possible to display full name of related model in one column like so:
1) The shortest form:
'relatedModel.fullName',
2) Overriding the label:
[
'attribute' => 'relatedModel.fullName',
'label' => 'Overrided label',
],
3) Using closure:
[
'attribute' => 'relatedModel.fullName', // The attribute name can be different in this case
'value' => function ($model) {
// You can calculate full name here.
// But it's better to just call according method since view is only for display.
return $model->author->fullName;
},
],
Another way is to calculate full name using SQL and include as a part of query result in separate column.
Use Active Record - Selecting extra fields official docs section as a guide, also see this related issue on Github - JoinWith - assign a column aliases to an attribute of related model.
Add $fullName as public property of related model class. Modify query like so:
use yii\db\Expression;
...
->joinWith(['relatedModel' => function (\yii\db\ActiveQuery $query) {
$query->addSelect('fullName' => new Expression("CONCAT(name, ' ', surname)")]);
}]
Then to display it in GridView column you can use one of the options desribed above, for example:
'relatedModel.fullName'
I am new to yii. I have tried myself,googled and found that yii2 default CRUD generator Gii will not generate CRUD for tables which has many to many relations. Also found that yii achieves(not in the sense Gii) Many to Many via One to Many yiiDrills .
Now I am trying to emulate the same kind of default CRUD manually with the help of Github issue trail and stackoverflow trail. I am facing the below issues while trying this.
Issue-1 (Model class of the table with Many to Many relations): Not able to initialize the class ActiveDataProvider,
$query = TableA::find();
$dataProvider = new ActiveDataProvider([
'query' => $query->TableB(),
]);
Issue-2(View): Even if I were able to initialize it how to render it via GridView
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//How to give the column names like we give for one to many
/* Example */
'TableA.attr1',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
Also I would like to know if it is desired to create a Model class for the table with Many to Many relations to handle CRUD.
Thanks
You should create models for all tables.
Example relations
/**
* #return \yii\db\ActiveQuery
*/
public function getCountry()
{
return $this->hasOne(Country::className(), ['id' => 'country_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCity()
{
return $this->hasOne(City::className(), ['id' => 'city_id']);
}
Example "Search" method
$query = Tour::find();
// Important: lets join the query with our previously mentioned relations
// I do not make any other configuration like aliases or whatever, feel free
// to investigate that your self
$query->joinWith(['city', 'country']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
You can replace hasOne with hasMany in your case.
Please check the link http://www.yiiframework.com/wiki/653/displaying-sorting-and-filtering-model-relations-on-a-gridview/
I am trying to setup the filter for related model in Yii2's GridView widget, but I am keep getting the error like the filter value must be an integer.
I have followed this question. Now, I have a two models Services.php and ServiceCharge.php.
In ServiceCharge.php the relation is setup like:
public function getServiceName()
{
return $this->hasOne(Services::className(),['id'=>'service_name']);
}
In the ServiceChargeSearch.php the code is like this:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\ServiceCharges;
/**
* ServiceChargesSearch represents the model behind the search form about `app\models\ServiceCharges`.
*/
class ServiceChargesSearch extends ServiceCharges
{
/**
* #inheritdoc
*/
public function attributes()
{
// add related fields to searchable attributes
return array_merge(parent::attributes(), ['serviceName.services']);
}
public function rules()
{
return [
[['id'], 'integer'],
[['charges_cash', 'charges_cashless'], 'number'],
[['id', 'serviceName.services', 'room_category'], 'safe'],
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = ServiceCharges::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$dataProvider->sort->attributes['serviceName.services'] = [
'asc' => ['serviceName.services' => SORT_ASC],
'desc' => ['serviceName.services' => SORT_DESC],
];
$query->joinWith(['serviceName']);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
// 'service_name' => $this->service_name,
'room_category' => $this->room_category,
'charges_cash' => $this->charges_cash,
'charges_cashless' => $this->charges_cashless,
])
->andFilterWhere(['LIKE', 'serviceName.services', $this->getAttribute('serviceName.services')]);
return $dataProvider;
}
}
and in my Gridview it is setup like this:
[
'attribute'=>'service_name',
'value'=>'serviceName.services',
],
Which is showing the services name from the related model correctly.
I am not able to see what I am doing wrong, but the filter field for the attribute for service is not showing at all.
Actually it is much simpler than it seems.
add the column_name to safe attribute.
Note: this should be relation Name
add the join with query - like - $query->joinWith(['serviceName','roomCategory']);
add the filter condition like:
->andFilterWhere(['like', 'services.services', $this->service_name])
->andFilterWhere(['like', 'room_category.room_category', $this->room_category]);
if like to add sorting add the code like:
$dataProvider->sort->attributes['service_name'] = [
'asc' => ['services.services' => SORT_ASC],
'desc' => ['services.services' => SORT_DESC],
];
$dataProvider->sort->attributes['room_category'] = [
'asc' => ['room_category.room_category' => SORT_ASC],
'desc' => ['room_category.room_category' => SORT_DESC],
];
5 you should also set the relation name say public $roomCategory
That's it. Both sorting and filtering for related table works perfectly.
Note: Remove default validation like integer for related column and default filtering generated by gii otherwise it will generate an error.
Update on Latest version:
Adding Public $attribute is not needed.
Adding safe attribute for relation is also not needed.
but the attribute in your current model, which you want filter is
to added to safe attribute that is a must.
and most importantly in your gridview, the related attribute has to
be in closure format.
that is example
[
'attribute=>'attribute_name',
'value=function($data){
return $data->relationname->related_table_attribute_name
}
],
remember it you are using relation_name.related_table_attribute_name filter somehow doesn't work for me.
There is a fairly comprehensive set of instructions on the Yii Framework website. The only thing to note is that the search model complains about the following lines, but everything appears to work as intended without them:
$this->addCondition(...);
For a model, PaymentEvent (table: subs_payment_event), which has a currency_id field linked to model Currency, this is the complete set of additional code (using the Basic template):
In the main model, PaymentEvent.php:
public function getCurrencyName()
{
return $this->currency->name;
}
In the search model, PaymentEventSearch.php:
public $currencyName;
In its rules:
[['currencyName'], 'safe'],
In the attributes of its setSort statement, include:
'currencyName' => [
'asc' => ['subs_currency.name' => SORT_ASC],
'desc' => ['subs_currency.name' => SORT_DESC],
'label' => 'Currency'
],
Before the grid filtering conditions:
$query->joinWith(['currency' => function ($q) {
$q->where('subs_currency.name LIKE "%' . $this->currencyName . '%"');
}]);
Finally, in the GridView columns array in the view (including my usual link across to the related model records):
[
'attribute' => 'currencyName',
'label' => 'Currency',
'format' => 'raw',
'value' => function ($data) {
return Html::a($data->currency->name, ['/currency/' . $data->currency_id]);
},
],