Yii CGridview - search/sort works, but values aren't being displayed on respective cells - activerecord

I am semi-frustrated with this Yii CGridView problem and any help or guidance would be highly appreciated.
I have two related tables shops (shop_id primary) and contacts (shop_id foreign) such that a single shop may have multiple contacts. I'm using CGridview for pulling records and sorting and my relation function in shops model is something like:
'shopscontact' => array(self::HAS_MANY, 'Shopsmodel', 'shop_id');
On the shop grid, I need to display the shop row with any one of the available contacts. My attempt to filter, search the Grid has worked pretty fine, but I'm stuck in one very strange problem. The respective grid column does not display the value that is intended.
On CGridview file, I'm doing something like
array(
'name' => 'shopscontact.contact_firstname',
'header' => 'First Name',
'value' => '$data->shopscontact->contact_firstname'
),
to display the contact's first name. However, even under circumstances that searching/sorting are both working (I found out by checking the db associations), the grid column comes out empty! :( And when I do a var_dump
array(
'name' => 'shopscontact.contact_firstname',
'header' => 'First Name',
'value' => 'var_dump($data->shopscontact)'
),
The dump shows record values in _private attributes as follows:
private '_attributes' (CActiveRecord) =>
array
'contact_firstname' => string 'rec1' (length=4)
'contact_lastname' => string 'rec1 lsname' (length=11)
'contact_id' => string '1' (length=1)
< Edit: >
My criteria code in the model is as follows:
$criteria->with = array(
'owner',
'states',
'shopscontacts' => array(
'alias' => 'shopscontacts',
'select' => 'shopscontacts.contact_firstname,shopscontacts.contact_lastname',
'together' => true
)
);
< / Edit >
How do I access the values in their respective columns? Please help! :(

Hmm, I have not used the with() and together() methods much. What's interesting is how in the 'value' part of the column, $data->shopscontacts loads up the relation fresh, based on the relations() definition (and is not based on the criteria you declared).
A cleaner way to handle the array output might be like this:
'value' => 'array_shift($data->shopscontacts)->contact_lastname'
Perhaps a better way to do this, though, would be to set up a new (additional) relation, like this in your shops model:
public function relations()
{
return array(
'shopscontacts' => array(self::HAS_MANY, 'Shopsmodel', 'shop_id'), // original
'firstShopscontact' => array(self::HAS_ONE, 'Shopsmodel', 'shop_id'), // the new relation
);
}
Then, in your CGridView you can just set up a column like so:
'columns'=>array(
'firstShopscontact.contact_lastname',
),
Cheers

Since 'shopscontact' is the name of the has-many relation, $data->shopscontact should be returning an array with all the shops related... did you modify the relation in order to return only one record (if I didn't get you wrong, you only need to display one, right?)? If you did it, may I see your filtering code?
P.S. A hunch to get a fast but temporal solution: have you tried 'value' => '$data->shopscontact['contact_firstname']'?

Related

After an ajax need to load a field value and create a select_from_array with the range value

Iam trying and not finding a way to load the value from a field and generate a select_from_array based on its range.
For example:
I have 2 select box
Brand -> loads -> Model (using backpack field types, and its working good)
`'type' => 'select2_from_ajax',
'name' => 'camera_model_id',
'entity' => 'camera_model',
'attribute' => 'name',
'data_source' => url('camera-brands'),
'placeholder' => 'Selecione o Modelo',
'minimum_input_length' => 0,
'dependencies' => ['camera_brand_id'],`
But, after the user selects this last selectBox, I need that another field was modified
`'name' => 'channel',
'label' => "Canal da Câmera",
'type' => 'select2_from_array',
'options' => ['' => '',
'01' => '01',
'02' => '02', ...`
So, the options could be filled with the maximum of the field I registered in the Model field database.
Is it possible? or maybe another approach to achieve the solution?
Thanks in advance!
To have an input that depends on the value of another input, you can make both your fields select2_from_ajax.
That way:
you will have the value of all inputs in the controller (the controller that returns the ajax results; then you can return a filtered set of results depending on how the form is filled so far - CategoryController::index() in the documentation example);
you can use the "dependencies" attribute on the selec2_from_ajax fields, so that when one field is reset, both are;
I hope the answer helps someone. Cheers!

Magento: Add Custom Attribute to Order Grid

I have a custom attribute in my product that i want it to appears in the orders grid, the attribute is added to mg_catalog_product_flat_1 table but when i try to make the above join i get an error, all other built in attributes like name, sku works fine so how can i get this done
Please check my code below
$collection->join(
'sales/order_item',
'`sales/order_item`.order_id=`main_table`.entity_id',
array(
'Productid' => new Zend_Db_Expr('group_concat(`sales/order_item`.product_id SEPARATOR ",")'),
//'manufacturer' => new Zend_Db_Expr('group_concat(`sales/order_item`.manufacturer_value SEPARATOR ",")'),
'name' => new Zend_Db_Expr('group_concat(`sales/order_item`.name SEPARATOR ",")'),
)
);
$collection->getSelect()->group('main_table.entity_id');
parent::setCollection($collection);
$this->addColumn('manufacturer', array(
'header' => Mage::helper('sales')->__('manufacturer'),
'width' => '100px',
'index' => 'manufacturer',
'type' => 'text',
));
If i uncomment manufacturer part i get an error a
An item is not the equivalent of a product. If you want to get attribute of a product in an item, you need to call the $item->getProduct() function to be able to get all the attributes of the product that are not defined in the item.
Then, you need to apply it to your collection. To do that, you can use the function walk of the collection object to apply a function on each element. The problem, in my opinion, is that you need to loop over you collection of order and then on the collection of items to get the attribute you need.
Another solution is to follow the recommandation of custom prdoduct's attribute to quote/order item. This mean the attribute will be added to all items all the time but will be avaiable like sku...
Hope it helps,

Magento Admin formfield multiselect selected

I´m currently developing a custom module for magento thats going to list employees.
I have figured out almost everything. The only thing I got left is how the selected values is going to be highlighted.
The problem I´m having is for the backend.
I got 2 tabs per employee, one for employee data and one tab for magento categories.
1 employee can have 1 or more categories.
The database table that the categories are stored in are a non-eav table.
So my question is
What in a multiselect determines which values are selected? As it is now, only one value is selected.
I think you can do this by simply passing in an array of the id's to be selected into the 'value' attribute of the field being added for the multiselect in the _prepareForm() method. Something like the following.
$fieldset->addField('category_id', 'multiselect', array(
'name' => 'categories[]',
'label' => Mage::helper('cms')->__('Store View'),
'title' => Mage::helper('cms')->__('Store View'),
'required' => true,
'values' => Mage::getSingleton('mymodule/mymodel')->getMymodelValuesForForm(),
'value' => array(1,7,10),
));
The id of the form element (e.g. category_id) must not be an attribute in your model, otherwise when the form values get set with $form->setValues() later on, the attribute value will be overwritten.
I normally store multiple selections as a text column separated by commas much like most magento modules handles stores which requires a slightly different approach as shown below.
In the form block for the tab with the multiselect, you firstly define the element to be displayed like so in the _prepareForm() method. You then get the values from the model and set put them into the form data.
protected function _prepareForm()
{
...
$fieldset->addField('store_id', 'multiselect', array(
'name' => 'stores[]',
'label' => Mage::helper('cms')->__('Store View'),
'title' => Mage::helper('cms')->__('Store View'),
'required' => true,
'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, true),
));
...
if ( Mage::getSingleton('adminhtml/session')->getMymodelData() )
{
$data = Mage::getSingleton('adminhtml/session')->getMymodelData();
} elseif ( Mage::registry('mymodel_data') ) {
$data = Mage::registry('mymodel_data')->getData();
}
$data['store_id'] = isset($data['stores']) ? explode(',', $data['stores']) : array();
$form->setValues($data);
}
I normally store the selected stores (categories as in your case) in the main model as a text column and comma separated values of ids, hence the explode.
In the controller for for the edit action, I put the model being edited into the mage registry so we can load it and it's values in the step above.
Mage::register('mymodel_data', $model);
Thanks for answering.
This is how my field looks like:
$fieldset->addField('npn_CatID', 'multiselect', array(
'label' => Mage::helper('employeelist')->__('Kategori'),
'class' => 'required-entry',
'required' => true,
'name' => 'npn_CatID',
'values' => $data,
'value' => array(3,5)
));
npn_CatID is the value in my db where the category id is saved.
I have tried to change the name and field ID but cant get it working.
When its the field id is like above ONE value is selected and its the last one inserted for the chosen employee
My data array looks likes
array(array('value' => '1', 'label' => 'USB'), array('value' => '2', 'label' => 'Memories'))

Adding custom columns in order grid (Magento 1.7.0.0)

I'm having this problem with adding custom columns in the order grid in Magento 1.7.0.0 and I was hoping you'd be able to give me a hand in here.
Basically I followed this guide http://www.atwix.com/magento/customize-orders-grid/ which explained I had to make a local version of /app/code/core/Mage/Adminhtml/Block/Sales/Order/Grid.php and make a couple of changes to have the extra columns I want. By following said guide, it said that I had to edit the function _prepareCollection() to add this line (specifying the fields I want to extract in the array)
$collection->getSelect()->join('magento_sales_flat_order_address', 'main_table.entity_id = magento_sales_flat_order_address.parent_id',array('telephone', 'email'));
Before
return parent::_prepareCollection();
And add the two columns in _prepareColumns() like this:
$this->addColumn('telephone', array(
'header' => Mage::helper('sales')->__('Telephone'),
'index' => 'telephone',
));
$this->addColumn('email', array(
'header' => Mage::helper('sales')->__('Email'),
'index' => 'email',
));
And that was it, apparently... Or maybe not, since it throws the following error when I do that:
Item (Mage_Sales_Model_Order) with the same id "XXXX" already exist
To which the solution, according to the comments underneath, was to add the following line in _prepareCollection before $this->setCollection($collection):
$collection->getSelect()->group('main_table.entity_id');
After adding the line, the Order Grid now shows the Email and Phone columns just like I want it, but turns out the pagination stopped working, it only shows the most recent 20 and it says "Pages 1 out of 1", "2 records found" on top. I can't seem to figure out why this is happening and every comment I see around doesn't go beyond the last instruction above. What could possibly be the cause of this issue?
I assume it could be replicated since I haven't made any other modifications of this model.
Alright, solved it. This is what I did:
Inspired by this answer https://stackoverflow.com/a/4219386/2009617, I made a copy of the file lib/Varien/Data/Collection/Db.php, placed it under app/core/local/Varien/Data/Collection/Db.php and copied the modifications suggested on that answer to fix the group select count error that was giving me problems above. So far it seemed to work.
However, there was a problem in the rows, when I clicked on the orders it said the Order "no longer exists", so I checked the actual url and turns out the order_id in the url was the same as the "entity_id" in the order_address table, which didn't correspond with the actual associative id of the order (parent_id). After tweaking for a long time with the MySQL query, I realized the issue was in the functions called by the _prepareColumns() and getRowUrl() functions in the /app/code/local/Mage/Adminhtml/Block/Sales/Order/Grid.php I made, since they were retrieving the wrong Id. So I made the following changes:
In _prepareColumns(), within the code corresponding to the Action column, I changed the 'getter' to 'getParentId', like this:
if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/view')) {
$this->addColumn('action',
array(
'header' => Mage::helper('sales')->__('Action'),
'width' => '50px',
'type' => 'action',
//~ 'getter' => 'getId',
'getter' => 'getParentId',
'actions' => array(
array(
'caption' => Mage::helper('sales')->__('View'),
'url' => array('base'=>'*/sales_order/view'),
'field' => 'order_id',
)
),
'filter' => false,
'sortable' => false,
'index' => 'stores',
'is_system' => true,
));
}
And in the getRowUrl() function, I changed the $row statement within the getUrl() function like this:
public function getRowUrl($row)
{
if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/view')) {
//~ return $this->getUrl('*/sales_order/view', array('order_id' => $row->getId()));
return $this->getUrl('*/sales_order/view', array('order_id' => $row->getParentId()));
}
return false;
}
And now it works like a charm. I hope this helps somebody else.
The problem is in query. Instead of this query:
$collection->getSelect()->join('magento_sales_flat_order_address', 'main_table.entity_id = magento_sales_flat_order_address.parent_id',array('telephone', 'email'));
You should use this:
$collection->getSelect()->join('sales_flat_order_address', 'main_table.entity_id = sales_flat_order_address.parent_id AND sales_flat_order_address.address_type = "shipping" ',array('telephone', 'email'));
In the table sales_flat_order_address, parent_id is duplicated. The first is for billing and the second one is for shipping. So you just need to select one of this: billing or shipping. This values are in column address_type...
Try using filter_index in the addColumn function:
$this->addColumn('telephone', array(
'header' => Mage::helper('sales')->__('Telephone'),
'index' => 'telephone',
'filter_index' => 'tablename.telephone'
));
You can find out the table name with printing out the sql query:
var_dump((string)$collection->getSelect())

CakePHP model design - better way?

I have a model "Goal" that has four types of children (which all have different fields):
campaign_roi
call_success
phone_skill
call_qluality
The goal table contains a field "model" which specifies one of the four models above.
But one "Goal" only has one of the 4 children. They will never have two types of children. Is there a better way than creating a model for each child? And if not, how can I dynamically specify which child type any goal is?
For instance, I want to see a goal and it's child - in this case a campaign_roi. How can I set it up where I can do this
$this->Goal->find('first', array(
'conditions' => array(
'Goal.id' => $id
)
)
And it returns information from the Goal model and it's child campaign_roi?
Let's assume that the 4 types of models hold information that can't be merged in to a single type. That is, they have no commonality, except that they all have a goal_id and are mutually exclusive. Let's assume that an instance of any one of these models can only belong to one goal.
You can set up your Goal model, using the belongsTo relationship, and the conditions clause, so that it will attempt to load a child of each type.
class Goal extends AppModel {
/* ... */
var $belongsTo = array(
'CampaignRoi' => array(
'className' => 'CampaignRoi',
'conditions' => array('Goal.model' => 'CampaignRoi'),
'foreignKey' => 'model_id'
),
'CallSuccess' => array(
'className' => 'CallSuccess',
'conditions' => array('Goal.model' => 'CallSuccess'),
'foreignKey' => 'model_id'
),
'PhoneSkill' => array(
'className' => 'PhoneSkill',
'conditions' => array('Goal.model' => 'PhoneSkill'),
'foreignKey' => 'model_id'
),
'CallQuality' => array(
'className' => 'CallQuality',
'conditions' => array('Goal.model' => 'CallQuality'),
'foreignKey' => 'model_id'
)
);
/* ... */
}
In your goal table, you'll have the model field and a model_id field. The model field will be populated with CampaignRoi, CallSuccess, PhoneSkill or CallQuality. After you've done your find on Goal, you'll still need to inspect goal.model to determine what type of child you need to work with.
This is a method I've used several times, though I can't quite remember what happens with the other models. At worst, it's an empty array or field (i.e. empty($goal['CampaignRoi']) == true, if $goal['Goal']['model'] == 'CallSuccess').
Sub-typing in relational databases is never fun.
For model inheritance, we can create sub_table with main_table_id to point back to the main_table. But it's quite convoluted when you need to get a full record.
If that 4 fields are the only difference between the children, you can put them all in the goals table. And if they are the same data type, you can use just one field. You can write custom find methods in the model (findModel1(),...) or define an array('model1name'=>'campaign_roi',...) for easy reference later when you do find().
How you design it is really a trade-off between robustness/extensibility and convenience.
Can't you work it the other way around? Not from the goal point of view, but from the others?
Don't specify no relation in the Goal model, specify hasOne relation in CampaignRoi, CallSuccess, PhoneSkill and CallQuality.
Do your queries thru CampaignRoi, CallSuccess, PhoneSkill and CallQuality.

Resources