Programmatically updating/editing an attribute's option in Magento - php

I have an attribute called "vendor_id". I have N products that have the "vendor_id" as an attribute of the product. The options for the "vendor_id" attribute are being generated programmatically when an admin adds a new "vendor" entity. The code is like this:
public function saveAction()
{
$data = $this->getRequest()->getPost();
$designerName = $data['title'];
$product = Mage::getModel('catalog/product');
$attributes = Mage::getResourceModel('eav/entity_attribute_collection')
->setEntityTypeFilter($product->getResource()->getTypeId())
->addFieldToFilter('attribute_code', 'vendor_id')
->load(false);
$attribute = $attributes->getFirstItem()->setEntity($product->getResource());
$myresults = array ('value'=> array('optionone'=>array($designerName)));
$attribute->setData('option',$myresults);
$attribute->save();
And this works for now. It will create an option for the "vendor_id" attribute, and when the user adds a new product (or edit's an existing product) the drop down for "vendor_id" will be populated by these "vendor" entities we've created on the saveAction() method.
Now, in the case an admin wants to edit an existing attribute option, I don't want to create a new option, I want to edit an existing one. It's important that the option ID stays the same when we change the name/label.
I've hooked in the newAction to set a static var so in the saveAction() we can check and see if we are editing or creating a new option:
if (null == MyController::$_editScope)
{
error_log('Need to update option attribute');
$attribute->addData($myresults);
}
The problem is that the addData() method does just that, it adds the data, but doesn't update the existing on. The attribute is:
$attribute = $attributes->getFirstItem()->setEntity($product->getResource());
Which is an instance of: http://docs.magentocommerce.com/Mage_Eav/Mage_Eav_Model_Entity_Attribute.html
Which has 3x parent classes and I've looked through them all for a method that will allow me to edit* or update* an existing option's name ...

You can find this useful. See complete details here.
//Get the eav attribute model
$attr_model = Mage::getModel('catalog/resource_eav_attribute');
//Load the particular attribute by id
//Here 73 is the id of 'manufacturer' attribute
$attr_model->load(73);
//Create an array to store the attribute data
$data = array();
//Create options array
$values = array(
//15 is the option_id of the option in 'eav_attribute_option_value' table
15 => array(
0 => 'Apple' //0 is current store id, Apple is the new label for the option
),
16 => array(
0 => 'HTC'
),
17 => array(
0 => 'Microsoft'
),
);
//Add the option values to the data
$data['option']['value'] = $values;
//Add data to our attribute model
$attr_model->addData($data);
//Save the updated model
try {
$attr_model->save();
$session = Mage::getSingleton('adminhtml/session');
$session->addSuccess(
Mage::helper('catalog')->__('The product attribute has been saved.'));
/**
* Clear translation cache because attribute labels are stored in translation
*/
Mage::app()->cleanCache(array(Mage_Core_Model_Translate::CACHE_TAG));
$session->setAttributeData(false);
return;
} catch (Exception $e) {
$session->addError($e->getMessage());
$session->setAttributeData($data);
return;
}

Related

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.

SIlverStripe PHP - "Add new" or select existing in Dropdown

This should be relatively simple. I know how to create a form and hide it and show it if a specific option is selected
I want to have a Dropdown populated with a specific DataObject. That much is simple. But I need to add an option that says 'Add new' to this Dropdown, which is in a form
I can handle the submission (like if the value is something like 'new' instead of an ID), but I do not know how to add this option into the select with Silverstripe, or if it is even possible. Any help is appreciated!
You can use a DropdownField and populate it with whatever key value pairs you want. For example:
$itemsField = DropdownField::create(
'Items',
'Items',
['add-new' => 'Add new'] + $this->Items()->map()->toArray()
);
The third argument is an array that gets turned into the option html elements for the drop down.
Then you can add it to your form's FieldList:
$form = Form::create(
$this,
'MyForm',
FieldList::create(
[
$someField,
$someOtherField,
$itemsField
]
),
$actions,
$requiredFields
);
http://api.silverstripe.org/3.1/class-DropdownField.html
A Dropdown for selecting a $has_one is fine. E.g. if you have a list of Addresses and want to select the Country.
Sometimes a new Country has to be added. For this i used the quickaddnew extension which adds a nice button "add new" beneath the dropdown and opens a form in a modal for adding the new item. You can use it in your code like this:
public function getCMSFields() {
/**
* #FieldList
*/
$fields = parent::getCMSFields();
$countries = function() {
return Country::get()->map('ID','Title')->toArray();
};
$country = new DropdownField('CountryID', 'Country', $countries());
$country->setEmptyString('-- please select --');
$country->useAddNew('Country', $countries);
$fields->insertAfter($country, 'Telephone');
$fields->removeByName('SortOrder');
return $fields;

Yii2 grid updating with editable field

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!

Magento set the new attribute value

I have created a new IMEI attribute of type textarea for all products, see from the image. Can anyone point out a function to update the its value. I have the code like the following.
$this belogns to Mage_Sales_Model_Order.
foreach ($this->getAllItems() as $item) {
$item->setImei('123');
$item->save();
echo $item->getImei();
}
I am getting 123 from the last statement but when I am viewing from admin. Its not changing there. Also in which table the attribute and value will be stored, So I can debug from there.
What class is $this->getAllItems() is it Mage_Catalog_Model_Product?
If it not Mage_Catalog_Model_Product then load the product by id and save the product
foreach ($this->getAllItems() as $item) {
$product = Mage::getModel('catalog/product')->load($item->getId() or $item->getProductId())
$product->setImei($product->getImei() . '123');
$product->save();
}
The values of catalog product attributes of type text are stored in the table catalog_product_entity_text. An SQL would be
select * from catalog_product_entity_text where attribute_id = {insert your attribute id} and entity_id = {insert your product id}
The query will return results for every store view in the system.
The reason why you do not see a change to the attribute in the backend is probably because the new value is set for a different website/store than the one loaded in the backend.
You are already using a correct way to set the attribute value assuming $item is of type Mage_Catalog_Model_Product:
$item->setImei('123');
$item->save();
heres how you do it differently: (color = attribute name, red = attribute value id)
starts assuming you already have $product available.
$attr = $product->getResource()->getAttribute('color');
if ($attr->usesSource()) {
$avid = $attr->getSource()->getOptionId('red');
$product->setData('color', $avid);
$product->save();
}

Conditional fieldgroups/fieldsets in Drupal 7

Background: In Drupal 7, I have created a form with CCK (aka the Field UI). I used the Field group module to create a fieldgroup, but I need it to be conditional, meaning it will only display depending on a previous answer.
Previous research: To create a conditional field, you can use hook_form_alter() to edit the #states attribute like so:
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'person_info_node_form') {
// Display 'field_maiden_name' only if married
$form['field_maiden_name']['#states'] = array(
'visible' => array(
':input[name="field_married[und]"]' => array('value' => 'Yes'),
),
);
}
}
However, there seems to be no way to use the States API for fieldgroups. One thing to note is that, while fields are stored in $form, fieldgroups are stored in $form['#groups'] as well as in $form['#fieldgroups']. I don't know how to distinguish between these, and with this in mind, I have tried to apply a #states attribute to a fieldgroup in the same manner as above. However, it only produces server errors.
Question: Is there a way to make a fieldgroup display conditionally using the States API or some alternative approach?
you have to use the hook_field_group_build_pre_render_alter()
Simply :
function your_module_field_group_build_pre_render_alter(&$element) {
$element['your_field_group']['#states'] = array(
'visible' => array(
':input[name="field_checkbox"]' => array('checked' => TRUE),
),
);
}
This works perfecly. If the group A is in an another group, do this
$element['groupA']['groupB']['#states'] etc....
You may need to add an id attribute if none exists:
$element['your_field_group']['#attributes']['id'] = 'some-id';
$element['yout_field_group']['#id'] = 'some-id';
Here's the simplest solution I came up with. There are essentially 2 parts to this: (1.) programmatically alter the display of the form, and (2.) use the GUI to alter the display of the content.
(1.) First, I used hook_form_alter() to programmatically create the conditional fieldset and add existing fields to it. The code is shown below.
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'FORM_ID_node_form') {
// programmatically create a conditional fieldset
$form['MYFIELDSET'] = array( // do NOT name the same as a 'Field group' fieldset or problems will occur
'#type' => 'fieldset',
'#title' => t('Conditional fieldset'),
'#weight' => intval($form['field_PARENT']['#weight'])+1, // put this fieldset right after it's "parent" field
'#states' => array(
'visible' => array(
':input[name="field_PARENT[und]"]' => array('value' => 'Yes'), // only show if field_PARENT == 'Yes'
),
),
);
// add existing fields (created with the Field UI) to the
// conditional fieldset
$fields = array('field_MYFIELD1', 'field_MYFIELD2', 'field_MYFIELD3');
$form = MYMODULE_addToFieldset($form, 'MYFIELDSET', $fields);
}
}
/**
* Adds existing fields to the specified fieldset.
*
* #param array $form Nested array of form elements that comprise the form.
* #param string $fieldset The machine name of the fieldset.
* #param array $fields An array of the machine names of all fields to
* be included in the fieldset.
* #return array $form The updated form.
*/
function MYMODULE_addToFieldSet($form, $fieldset, $fields) {
foreach($fields as $field) {
$form[$fieldset][$field] = $form[$field]; // copy existing field into fieldset
unset($form[$field]); // destroy the original field or duplication will occur
}
return $form;
}
(2.) Then I used the Field group module to alter the display of the content. I did this by going to my content type and using the 'Manage display' tab to create a field group and add my fields to it. This way, the fields will appear to be apart of the same group on both the form and the saved content.
Maybe you can try to look at the code of this module to help you find an idea.

Categories