Yii2 grid updating with editable field - php

I followed the post on setting up an editable field in a grid here http://webtips.krajee.com/setup-editable-column-grid-view-manipulate-records/
Everything seems to work - I click on the field, change it's value and it shows the updated value in the grid. However - next time I refresh, the value reverts to the original value.
I've done a little debugging, and it would appear that the save in the controller is failing.
My table has a two key fields (key1 and key2) and a value field. I am trying to update this value field.
My view code is as follows:
$gridColumns = [
'AnotherField',
[
'class' => 'kartik\grid\EditableColumn',
'attribute'=>'value',
'label' => 'some value',
'editableOptions'=> [
'header' => 'profile',
'format' => Editable::FORMAT_BUTTON,
]
];
...
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => $gridColumns
]) ?>
and the controller code is as follows:
<snip>
//default code from gii-generated controller
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// orig post said to use this: $dataProvider = $searchModel->search(Yii::$app->request->getQueryParams());
// Validate if there is an editable input saved via AJAX
if (Yii::$app->request->post('hasEditable')) {
$model = new MyTable;
$post = [];
$posted = current($_POST['MyTable']);
$post['MyTable'] = $posted;
// Load model like any single model validation
if ($model->load($post)) {
// When doing $result = $model->save(); I get a return value of false
$model->save();
$out = Json::encode(['output'=>$output, 'message'=>'']);
}
// Return AJAX JSON encoded response and exit
echo $out;
return;
}
// Non-AJAX - render the grid by default
</snip>
How does the controller know which record to update as I am only passing in the 'value' field, not the keys?
Do I somehow need to add these key1 and key2 fields to the view within the editable widget in order to get these values passed to the controller (so that the load and save know which record to update) or is there some other way that the controller handles this?

In the example on this page http://demos.krajee.com/grid-demo (you can see the column definition too) you can see with firebug that the following values are sent:
Book[5][buy_amount] 1
_csrf MEQ0NnEzNjFhd2dBB2wBCHN0cGVCA3lfZHF1TDReRlAAcQNUPUFjBQ==
editableIndex 5
editableKey 6
hasEditable 1
I believe the 5 in Book[5] is probably matching the index of the record.
This part does that from what I see
$post = [];
$posted = current($_POST['Book']);
$post['Book'] = $posted;
I have never used the widget so I might be wrong.

I have found the solution with some experimentation. I had to unserialize the $editableKey value and extract my key field values from it. I then call 'findModel' to load in the record to be updated.
The updated controller code is as follows:
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// Validate if there is an editable input saved via AJAX
if (Yii::$app->request->post('hasEditable')) {
$keys = unserialize(Yii::$app->request->post('editableKey'));
$model = $this->findModel($keys['key1'], $keys['key2']);
// Store a default JSON response as desired by editable
$out = Json::encode(['output'=>'', 'message'=>'']);
// Fetch the first entry in posted data (there should only be one
// entry anyway in this array for an editable submission)
$values = current($_POST['MyTable']);
// Load model like any single model validation
if ($model->key1) {
// Update the Profile with the new value passed in
$model->value = $values['value'];
$model->save();
...
Thanks for the advice

I was also looking for a solution of updating table using Editable column. Looking at the ajax post I got similar like this output:
Book[5][buy_amount] 1
_csrf MEQ0NnEzNjFhd2dBB2wBCHN0cGVCA3lfZHF1TDReRlAAcQNUPUFjBQ==
editableIndex 5
editableKey 6
hasEditable 1
Here I found the value of editableKey is the value of id of the table.
So to update specific row on gridview column I used the editableKey and update based on it.
In controller :
if (Yii::$app->request->post('hasEditable')) {
$_id=$_POST['editableKey'];
$model = $this->findModel($_id);
$post = [];
$posted = current($_POST['MyTable']);
$post['MyTable'] = $posted;
// Load model like any single model validation
if ($model->load($post)) {
// When doing $result = $model->save(); I get a return value of false
$model->save();
if (isset($posted['buy_amount']))
{
$output = $model->buy_amount;
}
$out = Json::encode(['output'=>$output, 'message'=>'']);
}
// Return AJAX JSON encoded response and exit
echo $out;
return;
}
I just get the post data using $_id=$_POST['editableKey'];
and set my $model as $model = $this->findModel($_id);
now you can update multiple editable column just adding condition like this
if (isset($posted['buy_amount']))
{
$output = $model->buy_amount;
}
Thanks!

Related

How to automatically fill other form fields after one field is filled?

I'm creating a form in moodle using moodleform class. I have many fields in the form. What I'm wanting to do is when the user fills in the first field, I want to get the data that he/she entered, search the relevant DB table for a field that matches that input, then populate the other fields for that record.
Please note that the user hasn't pressed any submit button yet. I've been trying to find a function that gets the entered data but all efforts have been in vain. What I found was a get_data() method but I don't even know how to use that correctly. I've been reading the moodle docs but nothing is helping. I'm not a beginner to coding but neither am I an expert.
Here's a code snippet:
class requestcourse_form extends moodleform
{
function definition()
{
global $CFG, $currentsess, $DB, $USER, $currentrecord;
$mform =& $this->_form; // Don't forget the underscore!
// Form header
$mform->addElement('header', 'mainheader','<span style="font-size:22px">'.get_string('courserequestform','block_usp_mcrs'). '</span>');
// Course Code field.
$coursecodearray = array();
$coursecodearray[0] = get_string('choosecoursecode', 'block_usp_mcrs');
$allcoursecodes = $DB->get_records_select('block_usp_mcrs_courses', 'id > 0', array(), 'id', 'id, course_code');
foreach ($allcoursecodes as $id => $coursecodeobject) {
$coursecodearray[$id] = $coursecodeobject->course_code;
}
$coursecode = $mform->addElement('select', 'coursecode', get_string('coursecode', 'block_usp_mcrs'), $coursecodearray);
$mform->addRule('coursecode', get_string('required'), 'required', null, 'client');
$mform->setType('coursecode', PARAM_RAW);
// Course Name field. TODO: Course Name to pick automatically after entering Course Code
$coursenamearray = array();
$coursenamearray[0] = get_string('choosecoursename', 'block_usp_mcrs');
$allcoursenames = $DB->get_records_select('block_usp_mcrs_courses', 'id > 0', array(), 'id', 'id, course_name');
foreach ($allcoursenames as $id => $coursenameobject) {
$coursenamearray[$id] = $coursenameobject->course_name;
}
$mform->addElement('select', 'coursename', get_string('coursename', 'block_usp_mcrs'), $coursenamearray);
$mform->addRule('coursename', get_string('required'), 'required', null, 'client');
$mform->setType('coursename', PARAM_RAW);
Any help would be appreciated.
You have to achieve this using javascript, as Moodle has no functionality to fill data in a nested way.
Add AMD module js file where you are calling your mform for display purpose.
In the file where you render your mform
$mform->render();
add below line to call you amd js file.
$PAGE->requires->js_call_amd('local_acestructure/registration', 'init');
In your amd js file make httprequest / ajax call to fetch data based on your course_code select drop down change.
Thank you.

How to properly display a calculated value of a non-db field in the ListView?

In Suitecrm/SugarCRM 6.5
I need to modify the ListView. Showing the value of a field based on a certain condition.
In the Account module, the name field may be empty based on certain conditions, I require that when this occurs show the value of a custom field. To do this, first I tryied using 'customCode' + smarty as usual in Edit/Detail vies, but in several posts mention that this is not possible in the ListView. The concession is to use logic hooks + a non-db field to store the calculated field. Following this SO answer I have written the following:
Custom field at custom/Extension/modules/Accounts/Ext/Vardefs/custom_field_name_c.php
<?php
$dictionary['Account']['fields']['name_c']['name'] = 'account_name_c';
$dictionary['Account']['fields']['name_c']['vname'] = 'LBL_ACCOUNT_NAME_C';
$dictionary['Account']['fields']['name_c']['type'] = 'varchar';
$dictionary['Account']['fields']['name_c']['len'] = '255';
$dictionary['Account']['fields']['name_c']['source'] = 'non-db';
$dictionary['Account']['fields']['name_c']['dbType'] = 'non-db';
$dictionary['Account']['fields']['name_c']['studio'] = 'visible';
Label at suitecrm/custom/Extension/modules/Accounts/Ext/Language/es_ES.account_name_c.php
<?php
$mod_strings['LBL_ACCOUNT_NAME_C'] = 'Nombre de cuenta';
Logic hook entry at custom/modules/Accounts/logic_hooks.php
$hook_array['process_record'] = Array();
$hook_array['process_record'][] = Array(
2,
'Get proper name',
'custom/modules/Accounts/hooks/ListViewLogicHook.php',
'ListViewLogicHook',
'getProperName'
);
Logic hook at custom/modules/Accounts/hooks/ListViewLogicHook.php
<?php
class ListViewLogicHook
{
public function getProperName(&$bean, $event, $arguments)
{
if (empty($bean->fetched_row['name'])) {
$bean->account_name_c = $bean->fetched_row['person_name_c'];
} else {
$bean->account_name_c = $bean->fetched_row['name'];
}
// Here I also tried, but same result
// $bean->account_name_c = empty($bean->name) ? $bean->person_name_c : $bean->name;
}
}
listviewdefs.php at custom/modules/Accounts/metadata/listviewdefs.php
I added the field
'NAME_C' =>
array (
'width' => '20%',
'label' => 'LBL_ACCOUNT_NAME_C',
'default' => true,
),
After these modifications and repair I hope to see the field full with the right value. But it appears empty. I have verified that the logic hook is properly called, but the name andperson_name_c fields are always empty. In case it is relevant I have verified in Studio that the name fields in Account is typename I do not know what this means when it comes to obtaining its value.
I appreciate your comments
The problem is in the logic hook is due to first that the $bean does not have access to the custom fields, so I have to invoke them using $bean->custom_fields->retrieve(); Also the name field is always empty, I had to use DBManager to get only the name field.
The logic of the final logic hook is the following:
<?php
class ListViewLogicHook
{
public function getProperName($bean, $event, $arguments)
{
// Get access to custom fields from $bean
$bean->custom_fields->retrieve();
// Get access to name property using DBManager because $bean->name return null
$sql = "SELECT name FROM accounts WHERE id = '{$bean->id}'";
$name = $GLOBALS['db']->getOne($sql);
// Assign a value to non-db field
$bean->name_c = empty($name) ? $bean->nombre_persona_c : $name;
}
}
I was not familiar with the method $bean->custom_fields->retrieve() and at the moment I do not know why is empty the name field and I understand others fields remain empty.
I hope this is useful
We achieved that doing an after_retrieve hook, very fast and simple - This is taken from a working example.
public function RemoveCost(&$bean, $event, $arguments)
{
global $current_user;
include_once(‘modules/ACLRoles/ACLRole.php’);
$roles = ACLRole::getUserRoleNames($current_user->id);
if(!array_search("CanSeeCostRoleName",$roles)){
$bean->cost = 0;
$bean->cost_usdollar = 0;
}
}
All you need is to define and add this function to the module logic_hooks.php
You can even tailor down to specific calls:
if (isset($_REQUEST['module'],$_REQUEST['action']) && $_REQUEST['module'] == 'Opportunities' && $_REQUEST['action'] == 'DetailView')
As some times there are views you do want to show the field, for example quicksearch popup.

Yii2 CSV text field to multiple select dropDownList

There is a database field called path it is text type and it is used to store paths marking strings as CSV, something like the following:
*:*,site/about,site/*
Originally, it is used as a text input field like the following:
<?= $form->field($model, 'path')->textarea(['rows' => 6]) ?>
I want to, temporary, convert those paths into a dropDownSelect list in the view, then in the controller I will implode the selected items into string again to be validated and saved.
In the view _form.php I have the following:
<?= $form->field($model, 'path')->dropDownList($model->getPathSelectList($model->path), ['multiple' => 'multiple']) ?>
In the controller:
public function actionCreate()
{
$model = new StaticHtml();
// var_dump($model->path);
// die();
if (is_array($model->path)) $model->path = implode(',', $model->path);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
$model->path = $model->getPathsList(Yii::$app->getRequest()->getQueryParam('path'));
return $this->render('create', [
'model' => $model,
]);
}
}
Please Notice the two commented lines var_dump($model->path) and die() I set them to debug, because every time I select from the dropDownList and submit the form, the validation fails and tell me that Path must be a string and of course there is no any selection reserved unlike other fields and the var_dump($model->path) prints NULL:
In the code above, there is two model's methods:
getPathsList() it is called from the controller and it convert an initial path to path marks and set them as csv string.
getPathSelectList in the view and it convert the csv into an associative array which its keys and values are equals for each one to be data list for the dropDownList field.
Till this point, I think it will need more for update, but however, What's the problem in my code that prevents posting the $model->path so it prints NULL with var_dump()?
I'm pretty sure that it is a problem in receiving data from the form because I tried to:
var_dump(Yii::$app->getRequest()->post('path'));
var_dump(Yii::$app->getRequest()->post('title'));
var_dump(Yii::$app->request->post('path'));
and all of them returns NULL
if your validation rule you can add
skipOnEmpty => true
Otherwise you can override the check this way
['path', 'required', 'isEmpty' => function ($value) {
return empty($value);
}]

Laravel & Eloquent: Correct way to save data simultaneously to two models which requires the id of each of them as the foreign key in the other

When a new page is created part of that data goes to the slugs model (slug url, template type, template id) this is standard on every page, then the rest of the data goes to a template model, in this case called text (simple text page).
The slug is required for example.com/{slug} which grabs the slug_url and looks for the appropriate page/template to produce the correct view and pass the correct data.
Logic:
Slug requires Text id
Text requires Slug id
But as I have to save them at the same time I’m not sure how I get the id of one and pass it to the other, simultaneously, so I came up with this:
Save new Slug
Save new Text (page template)
Update Slug with Text id
Here is the offending code - TextsController:
public function store()
{
$validator = Validator::make(Input::all(),
array(
'title' => 'required',
'slug_url' => 'required',
'menu_order' => 'required|numeric'
)
);
if($validator->fails()) {
Flash::error('Page not created! Please check errors below');
return Redirect::route('app.pages.index')->withErrors($validator);
} else {
$slug = new Slug;
$slug->user_id = Auth::id();
$slug->slug_url = Input::get('slug_url');
$slug->type = Input::get('type');
$slug->menu_order = Input::get('menu_order');
$slug->save();
$text = new Text;
$text->user_id = Auth::id();
$text->slug_id = $slug->id;
$text->title = Input::get('title');
$text->active = Input::get('active');
$text->save();
$slug_update = Slug::where('user_id', Auth::user()->id)->where('slug_url', $slug->slug_url)->first();
$slug_update->type_id = $text->id;
$slug_update->update();
return Redirect::route('app.texts.edit', $text->id);
}
}
I’m pretty new to Laravel (and development generally) but from what I have see so far, I’m pretty sure there must a cleaner way to do this?
You can change those lines:
$slug_update = Slug::where('user_id', Auth::user()->id)->where('slug_url', $slug->slug_url)->first();
$slug_update->type_id = $text->id;
$slug_update->update();
into:
$slug->type_id = $text->id;
$slug->save();

Zend Forms in Zf2, not able to retain values

i am using zend forms in my zf2 application which has two text boxes. Once i submit the data, i take that and i filter the data array which i use to display it on the table. Once the data is filtered and displayed on the table, the form data is reset.
Is there a way to retain the values in the form even after submission ?
Thats the code i have, on a button click i get into this page and the action is as shown below. So this Page has a table which will get populated based on the two text boxes that i have. When i click the submit button, the data in the textboxes disappear and the data gets populated in the table.
So what i want to do here is, retain the entered values in the textboxes. Can i do that ? I know HTTP is a stateless protocol. I know i can do it using front end client side technologies, just wanted to know if there is a work around to do this.
public function searchAction()
{
//search form instantiation
$form = new SampleForm();
// get the post request
$request = $this->getRequest();
//instantiation of select for querying
$select = new Select();
// get parameters
$artist = $request->getPost('artist');
$title = $request->getPost('title');
//prepare search criteria array
$search = array('artist' => $artist, 'title' => $title);
//remove empty criteria from the search criteria list
foreach ($search as $key => $value) {
if (empty($value)) {
unset($search[$key]);
}
}
$result = $this->getAlbumTable()->fetchAll($select->where($search));
return array(
'albums' => $result,
'form' => $form,
);
}
I finally figured out what i was doing. I just had to do this.
if ($request->isPost()) {
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
}

Categories