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
Related
The Setup
Using the Reviews extension for WP Job Manager, I have comments with the meta field review_average which is an average of `review_stars. I'm customizing the WP REST API so I can read and write comments remotely via the mobile app I'm building.
I've exposed these fields with
register_meta( 'comment', 'review_average', array( 'show_in_rest' => true,
'type' => 'string',
'description' => 'review_average',
'single'=> false));
register_meta( 'comment', 'review_stars', array( 'show_in_rest' => true,
'type' => 'string',
'description' => 'review_stars',
'single'=> false));
which results in this field in the REST API response for comments:
meta: {
review_average: [
"4"
],
review_stars: [
"Array"
]
},
(I can't seem to break down that array, but there's only one stars category so the average is fine)
I've written a create_review function that uses add_comment_meta to write to review_average and review_stars which successfully gives the comment the right stars. Both those meta values are required for it to work.
function create_review($param) {
$id = wp_insert_comment(array('comment_post_ID' => $param['post_id'],
'comment_author' => $param['username'],
'comment_author_email' => $param['email'],
'user_id' => $param['user_id'],
'comment_content' => $param['content']));
if ($id) add_comment_meta($id, 'review_average', $param['rating']);
if ($id) add_comment_meta($id, 'review_stars', array('Your Rating'=>$param['rating']));
return get_comment($id);
}
The Problem
I can't seem to get the ratings meta info into a response for the comments. On my way to writing the "index" function get_comments, I've written the "show" function, get_commment:
function get_review($param) {
$id = $param['id'];
$info = get_comment($id);
$res = array(
'id' => $id,
'author_name' => $info->comment_author,
'author_email' => $info->comment_author_email,
'author_id' => $info->user_id,
'date' => $info->comment_date,
'rating' => $info->review_average
);
return $res;
}
The response has rating: null. Same result with 'rating' => $info->meta->review_average, as well as using _review_average in both those scenarios.
I have another function for my custom posts, which are job_listings that in my app are customers.job_listing has a meta field that shows up in the default REST API response under meta as _job_location, but inside my get_customer function, $res['address'] = $info->_job_location; works just fine!
How do I get the damn rating_average meta!?
Well, 'rating' => get_comment_meta($id) inside my get_review method gives me this:
"rating": {
"review_average": [
"4"
],
"review_stars": [
"a:1:{s:11:\"Your Rating\";s:1:\"4\";}"
]
}
And then
'rating' => get_comment_meta($id)['review_average'][0],
'rating_info' => get_comment_meta($id),
Gives me a nice full
"rating": "4",
"rating_info": {
"review_average": [
"4"
],
"review_stars": [
"a:1:{s:11:\"Your Rating\";s:1:\"4\";}"
]
}
I'm learning php as I go, so I'd love if someone could post a comment about why
get_comment_meta($id)->review_average
returns null but
get_comment_meta($id)['review_average']
works.
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
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.
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;
}
Using Yii2, I'm trying to create a detailView. I want to hide empty rows, and therefore I use the kartik-v detailview. However, I also want to hide attributes if they conform to a certain condition. So I stumbled across this SO question, which captures the intention of my question. It does not, however, answer it satisfactory. (This question asks roughly the same thing). An example
<?= DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => [
'id',
'name', //cant be null, always shown
'description:ntext', //can be null, so hidden thanks to kartiks detailview
isAdmin() ? "password" :"", //an example, of course
"hypotheticalOtherField",
isAdmin() ? [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
] : ""
]
]) ?>
As you can see, I want to show some fields based on (in this example) whether or not the user is admin. Sadly, inserting emtpy strings, empty arrays, or null values into the attributes array if the condition isn't met, produces an error (IE The attribute must be specified in the format of "attribute", "attribute:format" or "attribute:format:label" when inserting empty strings)
I suppose I could create the attributes array like this:
$attrs = ['id','name','description:ntext'];
if (isAdmin()) array_push($attrs, "password");
array_push($attrs, "hypotheticalOtherField");
if (isAdmin()) array_push($attrs, [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
]);
echo DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => $attrs
]);
but then the overview with the standard Yii2 code layout is severely undermined.
So is there some way to conditionally insert values into an array, so I can keep coding Yii-style: estetic, organized, and uncluttered? Or maybe a values from which Yii2 knows it should be skipped when creating the View
You can use visible to DetailView
<?= DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => [
'id',
'name', //cant be null, always shown
'description:ntext', //can be null, so hidden thanks to kartiks detailview
[
'visible' => (isAdmin() ? true : false),
'value' => $model->password,
'label' => 'test'
],
]) ?>
Add whatever condition you want to add!!! in visible
If you try with an array append shorthand and the look of the code is more yii2 stylish
The attribute based on array is a correct (good) practice.
$attrs[] = ['id','name','description:ntext'];
if (isAdmin()) {
$attrs[] = ['password'];
}
$attrs[] = ['hypotheticalOtherField']
if (isAdmin()) {
$attrs[] = [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
}
echo DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => $attrs
]);