Yii2 Load schedule using unclead/yii2-multiple-input - php

I am using unclead / yii2-multiple-input widget.
I want to generate different number of rows with values from my database.
How can i do this?
I can design my columns in view and edit data manualy after page generated. But miss how to program the number of rows and its values in the view.
My code in view:
<?= $form->field($User, 'User')->widget(MultipleInput::className(), [
'min' => 0,
'max' => 4,
'columns' => [
[
'name' => 'name',
'title' => 'Name',
'type' => 'textInput',
'options' => [
'onchange' => $onchange,
],
],
[
'name' => 'birth',
'type' => \kartik\date\DatePicker::className(),
'title' => 'Birth',
'value' => function($data) {
return $data['day'];
},
'options' => [
'pluginOptions' => [
'format' => 'dd.mm.yyyy',
'todayHighlight' => true
]
]
],
]
])->label(false);
How can I make (for example) 8 rows with different values, and also have the ability to edit/remove/update some of them?

You need to look into the documentation as it says that you need to assign a separate field into the model which will store all the schedule in form of JSON and then provide it back to the field when editing/updating the model.
You have not added the appropriate model to verify how are you creating the field User in your given case above. so, i will try to create a simple example which will help you implement it in your scenario.
For Example.
You have to store a user in the database along with his favorite books.
User
id, name, email
Books
id, name
Create a field/column in you User table with the name schedule of type text, you can write a migration or add manually.
Add it to the rules in the User model as safe.
like below
public function rules() {
return [
....//other rules
[ [ 'schedule'] , 'safe' ]
];
}
Add the widget to the newly created column in ActiveForm
see below code
echo $form->field($model,'schedule')->widget(MultipleInput::class,[
'max' => 4,
'columns' => [
[
'name' => 'book_id',
'type' => 'dropDownList',
'title' => 'Book',
'items' => ArrayHelper::map( Books::find()->asArray()->all (),'id','name'),
],
]
]);
When saving the User model convert the array to JSON string.
like below
if( Yii::$app->request->isPost && $model->load(Yii::$app->request->post()) ){
$model->schedule = \yii\helpers\Json::encode($model->schedule);
$model->save();
}
Override the afterFind() of the User model to covert the json back to the array before loading the form.
like below
public function afterFind() {
parent::afterFind();
$this->schedule = \yii\helpers\Json::decode($this->schedule);
}
Now when saved the schedule field against the current user will have the JSON for the selected rows for the books, as many selected, for example, if I saved three books having ids(1,2,3) then it will have json like below
{
"0": {
"book_id": "1"
},
"2": {
"book_id": "2"
},
"3": {
"book_id": "3"
}
}
The above JSON will be converted to an array in the afterFind() so that the widget loads the saved schedule when you EDIT the record.
Now go to your update page or edit the newly saved model you will see the books loaded automatically.

Related

Yii2: Why kartik\select2 widget not filled then I trying update model?

Im have ActiveRecord model and view for update form of this model. Also I have getter and setter in model class that looks like this
public function setTopvisorGoogleRegion($value)
{
$this->myvalue = $value;
return(true);
}
public function getTopvisorGoogleRegion()
{
return([1 => '123']); //I return this array for show you essence of the problem
}
Following logic in this code $model->topvisorgoogleregion must return [1 => '123']
In view I have next code
<?php echo($form->field($model, topvisorgoogleregion)->textInput());?>
<?php echo $form->field($model, 'topvisorgoogleregion')->widget(Select2::classname(), [
'data' => [1 => '123', 2 => '456'],
'options' => [
'id'=>'projectCtrl',
'placeholder' => 'Select option',
'multiple' => true
],
'pluginOptions' => [
'allowClear' => true,
'tags' => true,
],
]);
?>
When I open form I want to see option 1 => '123' already selected in Select2. Its logically because when already existing record is updating, ActiveRecord get data that already stored in model (in this case using getter) and fill fields in view with this data (In first field that using textInput I see text 'Array' because getter in model returns array). But Select2 is empty when I open update page. Whats going wrong?
If I delete first field (textInput) nothing changes
I find the solution - in getter I need provide ActiveQuery object, not array. I dont know why and how it works, but it works

HasMany - save association data

I need help. I have a two tables business_departments and companies with association type hasMany.
I need to modify companies list consisting in the department. Code was generated via bake, after that I modified it.
Controller.
$businessDepartment = $this->BusinessDepartments->get($id, [
'contain' => ['Companies']
]);
$companies = $this->BusinessDepartments->Companies->find('list')->where([
'Companies.active' => true,
'Companies.type IS NOT' => 'service',
'OR' => [
'business_department_id IS NULL',
'business_department_id' => $id
]
])->distinct('Companies.id');
if ($this->request->is(['patch', 'post', 'put'])) {
debug($this->request->getData());
$businessDepartment = $this->BusinessDepartments->patchEntity($businessDepartment, $this->request->getData(), ['associated' => ['Companies']]);
debug($businessDepartment);
if ($this->BusinessDepartments->save($businessDepartment)) {
$this->Flash->success(__('The business department has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The business department could not be saved. Please, try again.'));
}
$this->set(compact('businessDepartment', 'companies'));
Entity.
protected $_accessible = [
'name' => true,
'companies' => true
];
Table
$this->hasMany('Companies', [
'foreignKey' => 'business_department_id',
// Tried it
/*'dependent' => true,
'cascadeCallbacks' => true,
'saveStrategy' => 'replace'*/
]);
template.
echo $this->Form->control('companies._ids', ['options' => $companies, 'multiple' => true, 'class' => 'multiple-find']);
First save with added companies is success, but when I tried to modify companies list (And if try to save without changes) I get error.
Can I save via *._ids or I need to make a custom code for it?
Below debug($this->request->getData())
[
'name' => 'Office',
'companies' => [
'_ids' => [
(int) 0 => '21',
(int) 1 => '29'
]
]
]
But after patchEntity, instead of searching for companies and changing the business_department_id fields in them, patchEntity tries to create new companies and displays an error. Below is a fragment of screenshot.
debug($businessDepartment) and screenshot page
Thank you. I hope for quick answer.
Maybe someone will come in handy!
you have validation errors in your company related data, thats why you
cant save it, if you want to just use _ids as save try clearing
companies field in your $businessDepartment i.e.
$businessDepartment->unsetProperty('companies');
before patchEntity
Graziel

Yii2 Kartik EditableColumn Dropdown Relation Returns wrong Value

I have an issue with a Gridview using kartik\grid\EditableColumn, after changing the value I am returned the wrong value for the column when it updates. I am returned the dropdown key/main table integer rather than the string contained in a linked table.
I have two tables
Leads - columns id and status_id
Related fields - model, field, related_value, related_value
The relation is based on in this case
model:"Leads",
field:"status_id",
related_id:status_id
I have the following relation in my model
public function getStatus()
{
return $this->hasOne(RelatedFields::className(), ["related_id" => "status_id"])->andOnCondition(["status.field" => "status_id", "status.model"=>"Leads"])->from(["status" => RelatedFields::tableName()]);
}
I also created the following as a test based on this link
public function getStatusValue()
{
return $this->status->related_value;
}
Here is the column code
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_id',
'value'=>'status.related_value',
//'value' => function($model){ return $model->status->related_value; },
//'value' => function($model){ return $model->StatusValue; },
//'refreshGrid' => true,//Works but not nice
'vAlign'=>'middle',
'hAlign'=>'center',
'pageSummary' => true,
'readonly' => false,
'width'=>'10%',
'filter'=>Html::activeDropDownList($searchModel, 'status', ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),['class' => 'form-control','prompt' => Yii::t('app', '')]),
'editableOptions'=> [
//'attribute'=>'status_id',
//'value'=>'status.related_value',
//'header' => 'profile',
//'format' => Editable::FORMAT_BUTTON,
'inputType' => Editable::INPUT_DROPDOWN_LIST,
'data'=> ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),
]
],
Commented out are a number of lines in my attempts to fix the issue as well as combinations of them, however all result in the wrong value.
If for example I select the related value "New" which has a related_id 1, after the column has been updated I get the value 1 instead of "New".
When the table is first loaded/reloaded the value does show correctly.
I could reload the grid, but this seems wrong just to fix 1% of the data shown on the page.
I your model take a public variable $status_value
create an assigning value method
public function getStatusValue()(){
return $this->status_value= $this->status->related_value;
}
Now in Gridview use getStatusValueenter code heremethod with assigning value as below
use yii\helpers\Url;
$gridColumns = [
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_value',
'pageSummary' => true,
'readonly' => false,
'value' => function($model){ return $model->statusValue; }, // assign value from getStatusValue method
'editableOptions' => [
'header' => 'status_value',
'inputType' => kartik\editable\Editable::INPUT_TEXT,
'options' => [
'pluginOptions' => [
]
]
],
],
];
If you follow Kartik guide, he suggest to add EditableColumnAction to better handle the editable column:
The EditableColumnAction offers a quick easy way to setup your
controller action for updating, saving and managing the EditableColumn
output from GridView. This action class extends from yii\rest\Action
and hence all properties available with yii\rest\Action are applicable
here. The basic setup of the column involves setting up the controller
action and the EditableColumn.
So you need to add an EditableColumnAction in your controller to handle the update of the model:
public function actions()
{
return ArrayHelper::merge(parent::actions(), [
'edit-lead' => [
'class' => EditableColumnAction::class,
'modelClass' => Leads::class
]
]);
}
In your GridView editable column configuration, include the above
controller action for processing the Editable within editableOptions.
For example
And in your column code you need to add the action to editableOptions property:
'editableOptions' => [
...
'formOptions' => ['action' => ['/leads/edit-lead']]
]
Now, according to the guide, you can add to your action the outputValue property:
'outputValue' => function (Leads $model) {
return $model->status->related_value;
}

Regarding GridView Filter in yii2

I have a table like this with 5 columns.
TableName
-Column1
-Column2
-Column3
-Column4
-Column5
I had merged them to display them in grid view as single column.
Question
How can i make filter condition query to search them based on user input.?
Eg.User types something as input, it have search from all the 5 columns and return the result based on the search input.(Sorting works fine, please help me with filtering)
If someone could helpme it would be great,
Thanks.
UPDATE:
$query->andFilterWhere(['ilike', '"x"."y"', $this->variantName]) ->andFilterWhere(['"a"."b"' => $this->ClassId])
->andFilterWhere(['"c"."d"' => $this->FamilyId])
->andFilterWhere(['"e"."f"' => $this->PlatformId])
->andFilterWhere(['ilike', '"g"."h"', $this->subFamilyName])
This is how my old model looks like the fields with familyId,classId,PlatformId are integer and subfamilyname,variantname are text.
Modified:
$query->andFilterWhere(['or',
['ilike', '"x"."y"', $this->Combo],
['"a"."b"' => $this->Combo],
['"c"."d"' => $this->Combo],
['"e"."f"' => $this->Combo],
['ilike', '"g"."h"', $this->Combo],
])
UPDATE 2:
This is how the query looked before merging columns.
->andFilterWhere(['ilike', '"storeNames"."variantName"', $this->variantName])
->andFilterWhere(['"storeNames"."classId"' => $this->malwareClassId])
->andFilterWhere(['"storeNames"."familyId"' => $this->malwareFamilyId])
->andFilterWhere(['"storeNames"."platformId"' => $this->malwarePlatformId])
->andFilterWhere(['ilike', '"storeNames"."subFamilyName"', $this->subFamilyName]);
I will add an example that uses a countries table to display data with GridView.
In order to accomplish what you need you have to take the following steps.
Create a custom attribute/field inside your SearchModel.
Add the field to gridview column.
Define the field as safe inside the rules.
update the search() function inside the SearchModel to search and compare based on the new custom field.
Let's say I have a Countries table with 2 models
Countries
CountriesSearch
The countries table has name and code field and i want to show country name and code inside a single column like below.
and i want that the filter field above on the gridview should be able to search if i type name or code any one of them.
Add field name $combo in the CountrieSearch
public $combo
add the field to the rules as safe
public function rules() {
return [
[ [ 'id' ] , 'integer' ] ,
[ [ 'name' , 'code' , 'combo' ] , 'safe' ] ,
];
}
Add the new field to the gridview
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'attribute'=>'combo',
'label'=>'Name/Code',
'value'=>function($model){
return '<span class="label label-success">'.$model->name.'</span><span class="label label-info">('.$model->code.')</span>';
},
'format'=>'raw',
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
Then update the search function and add the following lines just before you are returning the $dataProvider inside the search() method
$query->andFilterWhere ( [ 'OR' ,
[ 'like' , 'name' , $this->combo ],
[ 'like' , 'code' , $this->combo ],
] );
Hope this helps you out

CAKEPHP3 form field not returning value

I have a Samples Table in my database.
Here is my Association in SamplesTable.php
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
In my SamplesControler.php add method i'm getting current User data like this:
$users = $this->Samples->Users->get($this->Auth->user('id'), [
'keyField' => 'id',
'valueField' => 'name'
]);
$this->set(compact('sample', 'users'));
In my add.ctp in Template/Samples/ i'm using this variable to show the user name like this:
echo $this->Form->input('user_id', [
'label' => 'Client',
'type' => 'text',
'disabled' => true,
'value' => $users->name,
'required' => true,
]);
It shows the name correctly, but when i hit submit it doesn't returns the id of the user back to the controller to save it in database.
Since i'm very new to this framework i can't figure out if there is anything i'm doing wrong.
I've tried debug response data in my controller add method and there is no user_id in response.
if i change
$users->name to $users
in my template file, it shows all the data about that user in json format in the textfield like this:
{ "id": 4, "name": "firstname lastname", "title": "Engineer", "street": "122", "city": "Lahore", "state": "Punjab", "email_primary": "abc#gmail.com", "created": "2016-03-04T07:45:42+0000", "modified": "2016-03-04T09:56:17+0000"}
If your intention is to show the logged in user's name in the textbox and to save the logged in user's id in the controller, there's no need to fetch the user data.
It's really unnecessary and might cause security issues when you're setting the user id to the template.
When you're trying to access the data of the logged in user, it's the best to keep it this way:
/* For controller: */
$this->Auth->user("field_name");
/* For view: */
$this->request->session()->read("Auth.User.field_name");
Try this:
$name = $this->request->session()->read("Auth.User.first_name")." ". $this->session->read()->("Auth.User.last_name"); // Add this
echo $this->Form->input('user_id', [
'label' => 'Client',
'type' => 'text',
'disabled' => true,
'value' => $name, // Add this
'required' => true,
]);
In the controller:
if ($this->request->is("post")) {
$this->request->data["user_id"] = $this->Auth->user("id"); // Add this
/* Other code */
}
Hope this helps.
Peace! xD

Categories