I can't figure out how to display, in my custom admin controller, 1 fields_list + content of a .tpl file. The goal is to display my product keys in stock + some extra features below (content from a tpl file).
I can display either the fields list OR the message from the .tpl file. But not combined... I found a tutorial online and this comes very close, but not working.
<?php
require_once(_PS_MODULE_DIR_ . 'avanto_key/classes/AvantoStock.php');
class AdminAvantokeyStockController extends ModuleAdminController
{
protected $position_identifier = 'id_avanto_keys';
public function __construct()
{
//$this->fields_form = $this->fieldForm();
$this->bootstrap = true;
$this->table = 'avanto_keys'; // DB table name where your object data stored
$this->className = "AvantoStock"; // The class name of my object
//$this->identifier = 'id_avanto_keys';
//$this->list_id = 'id_avanto_keys';
$this->_defaultOrderBy = 'id_avanto_keys';
//$this->lang = FALSE;
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),
'confirm' => $this->l('Delete selected items?')), );
//Shop::addTableAssociation($this->table, array('type' => 'shop'));
parent::__construct();
$this->_select = 'pl.`name` as product_name, a.`serial_key` as serial_display';
$this->_join = 'LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = a.`id_product` AND pl.`id_lang`
= '.(int)$this->context->language->id.')';
}
public function renderView()
{
$tpl = $this->context->smarty->createTemplate(
dirname(__FILE__).
'/../../views/templates/admin/view.tpl');
return $tpl->fetch();
}
public function renderList()
{
$this->toolbar_title = $this->l('Stock Management');
$this->toolbar_btn['new'] = null;
$this->fields_list = array(
'id_avanto_keys' => array(
'title' => $this->l('ID Key'),
'width' => 140,
),
'id_product' => array(
'title' => $this->l('Product ID'),
'width' => 140,
),
'serial_key' => array(
'title' => $this->l('Serial Keys'),
'width' => 140,
),
'product_name' => array(
'title' => $this->l('Product Name'),
'width' => 140,
),
);
return parent::renderList();
}
public function init()
{
parent::init();
}
public function initContent()
{
$this->context->smarty->assign(array(
'form' => $form,
'base_dir' => _PS_MODULE_DIR_,
));
$this->setTemplate('stock.tpl');
$lists = parent::initContent();
$this->renderList();
$lists .= parent::initContent();
return $lists;
}
public function renderForm()
{
$this->display = 'edit';
$this->initToolbar();
$this->fields_form = array(
'tinymce' => true,
'legend' => array(
'title' => $this->l('Edit product key'),
),
'input' => array(
array(
'type' => 'text',
'label' => $this->l('Key ID'),
'name' => 'id_product',
),
array(
'type' => 'text',
'label' => $this->l('Product ID'),
'name' => 'id_avanto_keys',
),
array(
'type' => 'text',
'label' => $this->l('Serial Key'),
'required' => true,
'name' => 'serial_key',
),
),
'submit' => array(
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
)
);
return parent::renderForm();
}
}
?>
The code above only displays the message hello world and not my product listing.
Anyone has an idea how to combine this?
Thanks in advance!!!
It's a little bit confused, we have to make some tidy :):
First:
The fields of the list it's better to declare in the __construct so:
public function __construct()
{
$this->module = 'YourModuleName'; // Here you have to put your module name
$this->bootstrap = true;
$this->table = 'avanto_keys'; // DB table name where your object data stored
$this->className = "AvantoStock"; // The class name of my object
//$this->identifier = 'id_avanto_keys';
//$this->list_id = 'id_avanto_keys';
$this->_defaultOrderBy = 'id_avanto_keys';
//$this->lang = FALSE;
$this->explicitSelect = true; // This if you do a select manually after
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),
'confirm' => $this->l('Delete selected items?')), );
//Shop::addTableAssociation($this->table, array('type' => 'shop'));
$this->_select = 'pl.`name` as product_name, a.`serial_key` as serial_display';
$this->_join = 'LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = a.`id_product` AND pl.`id_lang`
= '.(int)$this->context->language->id.')';
$this->fields_list = array(
'id_avanto_keys' => array(
'title' => $this->l('ID Key'),
'width' => 140,
),
'id_product' => array(
'title' => $this->l('Product ID'),
'width' => 140,
),
'serial_key' => array(
'title' => $this->l('Serial Keys'),
'width' => 140,
),
'product_name' => array(
'title' => $this->l('Product Name'),
'width' => 140,
),
);
parent::__construct();
}
Second
The parent renderList method make other stuff, let's separate that from what do you want to display:
public function renderList()
{
// Here we retrieve the list (without doing any strange thing)
$list = parent::renderList();
// Assign some vars to pass to our custom tpl
$this->context->smarty->assign(
array(
'var1' => "Test",
'var2' => "Test2"
)
);
// Get the custom tpl rendered
$content = $this->context->smarty->fetch(_PS_MODULE_DIR_ . "avanto_key/views/templates/admin/avantokeystock/customcontent.tpl");
// return the list plus your content
return $list . $content;
}
Third
Leave the parent initContent as is, do not override, because he make a lot of stuffs
I guess that is a great point to start :)
Try this way and let me know ;)
HI i Want to create Alpha Pager search , search is working fine but when i click search it disturb UI like load header and menu second time and site start showing menu again
Here is admin.php view page:
<?php
/* #var $this AreaController */
/* #var $model Area */
$this->breadcrumbs = array(
'Areas' => array('index'),
'Manage',
);
?>
<h1>Manage Areas</h1>
<div>
<?php
echo CHtml::ajaxLink('F', Yii::app()->createUrl('area/admin'), array('data' => array('q' => 'F'), 'update' => '#area-grid'), array());
echo CHtml::ajaxLink('E', Yii::app()->createUrl('area/admin'), array('data' => array('q' => 'E'), 'update' => '#area-grid'), array());
//echo CHtml::link('S', array('area/admin', 'q' => 'S'), array('target' => '_blank'));
?>
</div>
<?php
$this->widget('bootstrap.widgets.TbGridView', array(
'id' => 'area-grid',
'itemsCssClass' => 'table-bordered items',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
array(
'class' => 'editable.EditableColumn',
'name' => 'Area',
'headerHtmlOptions' => array('style' => 'width: 110px'),
'editable' => array(//esditable section
'url' => $this->createUrl('area/updateEditable', array('model' => 'Area', 'field' => 'status')),
'placement' => 'right',
)
),
array(
'class' => 'editable.EditableColumn',
'name' => 'status',
'headerHtmlOptions' => array('style' => 'width: 100px'),
'editable' => array(
'type' => 'select',
//'url' => Area::getStatusList(),
'url' => $this->createUrl('area/updateEditable', array('model' => 'Area', 'field' => 'status')),
'source' => $this->createUrl('area/getStatuses'),
'options' => array(//custom display
'display' => 'js: function(value, sourceData) {
var selected = $.grep(sourceData, function(o){ return value == o.value; }),
colors = {"Active": "green", "InActive": "red"};
$(this).text(selected[0].text).css("color", colors[value]);
}'
),
//onsave event handler
'onSave' => 'js: function(e, params) {
console && console.log("saved value: "+params.newValue);
}',
'htmlOptions' => array(
'data-username' => '$data->status'
)
)
),
),
));
?>
Controller admin action code:
public function actionAdmin() {
$model = new Area('search');
$model->unsetAttributes(); // clear any default values
if (isset($_GET['Area']))
$model->attributes = $_GET['Area'];
$this->render('admin', array(
'model' => $model,
));
}
Model search function code:
public function search() {
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria = new CDbCriteria;
$criteria = new CDbCriteria;
if (isset($_GET['q'])) {
$criteria->condition = 'Area like "' . $_GET["q"] . '%" ';
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
$criteria->compare('id', $this->id);
$criteria->compare('Area', $this->Area, true);
$criteria->compare('status', $this->status, true);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
I am currently working on a web application (written in PHP, based on Yii), which, among other things, lets you connect different price values for products. Each product can have multiple prices, but the system is built in a way that I can easily (and correctly) determine the type of the price field - so while each product can have multiple price fields, it can only have one of each type of price fields.
The part where I stuck is when I have to display the stored value of these fields in a list, and order the list view by them - I can display them correctly and sort them by one column, but as soon as I try to make the list to be sortable by all of the columns (not at the same time, of course), the rows start to display the wrong values.
Here are the relevant pieces of code:
In the model, at relations:
'productPriceConnects' => array(Product::HAS_MANY, 'ProductPriceConnect', 'product_id'),
'price1' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceBeszerzesi.product_price_field_id=:product_price_field_id AND priceBeszerzesi.active = 1',
'params' => array(':product_price_field_id' =>ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 6))->id),
'alias' => 'priceBeszerzesi',
),
'price2' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceEladasi.product_price_field_id=:product_price_field_id AND priceEladasi.active = 1',
'params' => array(':product_price_field_id' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 1))->id),
'alias' => 'priceEladasi'
),
'price3' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceAkcios.product_price_field_id=:product_price_field_id AND priceAkcios.active = 1',
'params' => array(':product_price_field_id' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 5))->id),
'alias' => 'priceAkcios'
),
In the model, at search:
...
$criteria->with('price1', 'price2', 'price3);
...
$criteria->compare('price1.price', $this->beszerzesi, true);
$criteria->compare('price2.price', $this->eladasi, true);
$criteria->compare('price3.price', $this->akcios, true);
...
$sort->attributes = array(
'price1' =>array(
'asc' => 'priceBeszerzesi.price ASC',
'desc' => 'priceBeszerzesi.price DESC',
),
'price2' =>array(
'asc' => 'priceEladasi.price ASC',
'desc' => 'priceEladasi.price DESC',
),
'priceAkcios' =>array(
'asc' => 'price3.price ASC',
'desc' => 'price3.price DESC',
),
'*'
);
...
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'sort' => $sort,
'pagination' => array(
'pageSize' => 10,
),
)
);
The columns' data in gridview:
'eladasi' => array(
'name' => 'price2',
'header' => 'Eladási ár',
'type' => 'raw',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'value' => '!empty($data->price2->price) ? $data->price2->price == "" ? "-" : $data->price2->price : "-"',
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
'akcios' => array(
'name' => 'priceAkcios',
'header' => 'Akciós',
'value' => '!empty($data->price3->price) ? $data->price3->price == "" ? "-" : $data->price3->price : "-"',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
'beszerzesi' => array(
'name' => 'price1',
'header' => 'Beszerzési ár',
'type' => 'raw',
'value' => '!empty($data->price1->price) ? $data->price1->price == "" ? "-" : $data->price1->price : "-"',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
This code makes it possible to sort the list by all three of the relation-dependent columns, but every column displays the same value - the value of the last relation in the with array. If the last element in the array is price3, then the columns display the value of the price3 relation.
When I remove all relation names from the with array expect one, I can sort the list by that column, but not the others.
My question is this:
Is there any way to
1) surely add any number of relations to a model, connecting to the same db field, but depending on conditions,
2) and display these values WHILE enabling sorting based on them?
Find solution below:
I created these tables at my system and used your relation and gridview code. I made some changes in that code and now in below code searching and sorting is working perfectly.
I defined three variable in model class i.e.
public $beszerzesi;
public $eladasi;
public $akcios;
Then I changed the param's name in relation arraye used with left join. This was the main issue in your code. You used same name for parameters i.e. :product_price_field_id I assigned different name for each parameters. While yii prepare sql query it will replace the parameters which are assigned to query. In your case it was replacing same value for all three parameters.
Also i made some changes in sort and compare attributes passed in CActiveDataProvider. You can find all changes in below model file.
Product.php
<?php
/**
* This is the model class for table "product".
*
* The followings are the available columns in table 'product':
* #property integer $id
* #property string $name
*/
class Product extends CActiveRecord
{
public $beszerzesi;
public $eladasi;
public $akcios;
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'product';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('name', 'required'),
array('name', 'length', 'max' => 100),
// The following rule is used by search().
// #todo Please remove those attributes that should not be searched.
array('id, name, beszerzesi, eladasi,akcios', 'safe', 'on' => 'search'),
);
}
/**
* #return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'productPriceConnects' => array(Product::HAS_MANY, 'ProductPriceConnect', 'product_id'),
'price1' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceBeszerzesi.product_price_field_id=:product_price_field_id1 AND priceBeszerzesi.active = 1',
'params' => array(':product_price_field_id1' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 6))->id),
'alias' => 'priceBeszerzesi',
),
'price2' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceEladasi.product_price_field_id=:product_price_field_id2 AND priceEladasi.active = 1',
'params' => array(':product_price_field_id2' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 1))->id),
'alias' => 'priceEladasi'
),
'price3' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
'joinType' => 'LEFT JOIN',
'on' => 'priceAkcios.product_price_field_id=:product_price_field_id3 AND priceAkcios.active = 1',
'params' => array(':product_price_field_id3' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 5))->id),
'alias' => 'priceAkcios'
),
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'ID',
'name' => 'Name',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
*
* Typical usecase:
* - Initialize the model fields with values from filter form.
* - Execute this method to get CActiveDataProvider instance which will filter
* models according to data in model fields.
* - Pass data provider to CGridView, CListView or any similar widget.
*
* #return CActiveDataProvider the data provider that can return the models
* based on the search/filter conditions.
*/
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria = new CDbCriteria;
$criteria->with = array('price1', 'price2', 'price3');
$criteria->compare('id', $this->id);
$criteria->compare('name', $this->name, true);
$criteria->compare('priceBeszerzesi.price', $this->beszerzesi, true);
$criteria->compare('priceEladasi.price', $this->eladasi, true);
$criteria->compare('priceAkcios.price', $this->akcios, true);
// $criteria->attributes = ;
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'sort' => array(
'attributes' => array(
'beszerzesi' => array(
'asc' => 'priceBeszerzesi.price',
'desc' => 'priceBeszerzesi.price DESC',
),
'eladasi' => array(
'asc' => 'priceEladasi.price',
'desc' => 'priceEladasi.price DESC',
),
'akcios' => array(
'asc' => 'priceAkcios.price',
'desc' => 'priceAkcios.price DESC',
),
'*'
)
),
'pagination' => array(
'pageSize' => 10,
),
)
);
}
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* #param string $className active record class name.
* #return Product the static model class
*/
public static function model($className = __CLASS__)
{
return parent::model($className);
}
}
gridview code
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id' => 'product-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'id',
'name',
'eladasi' => array(
'name' => 'eladasi',
'header' => 'Eladási ár',
'type' => 'raw',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'value' => '!empty($data->price2->price) ? $data->price2->price == "" ? "-" : $data->price2->price : "-"',
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
'akcios' => array(
'name' => 'akcios',
'header' => 'Akciós',
'value' => '!empty($data->price3->price) ? $data->price3->price == "" ? "-" : $data->price3->price : "-"',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
'beszerzesi' => array(
'name' => 'beszerzesi',
'header' => 'Beszerzési ár',
'type' => 'raw',
'value' => '!empty($data->price1->price) ? $data->price1->price == "" ? "-" : $data->price1->price : "-"',
'headerHtmlOptions' => array('class' => 'auto-width text-center'),
'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
array(
'class' => 'CButtonColumn',
),
),
)); ?>
You can found step by step guide for the searching and sorting on relation data at Searching and sorting by related model in CGridView | Wiki | Yii PHP Framework
I have User model which contain a function that calculates the average revenue per user. Now i want apply sorting in CGridView on the column which is associated with getAvgRevenue() function. While license is relation in the User model.
Here is my code,
public class User{
$user_id;
$email;
public function getAvgRevenue(){
$count = 0;
$revenue = 0;
foreach ($this->license as $license){
$revenue += $license->price;
$count++;
}
if($count!= 0){
$averageRevenue = $revenue/$count;
return $averageRevenue;
}
else{
return null;
}
}
}
In Controller
$modelUser = new CActiveDataProvider('User', array(
'sort' => array(
'attributes' => array(
'user_id',
'email',
'averagerevenue'
),
'defaultOrder' => array(
'user_id' => CSort::SORT_ASC,
),
),
));
In view
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id' => 'user-grid',
'dataProvider' => $modelUser,
'columns' => array(
array(
'header' => 'User ID',
'name' => 'user_id',
),
array(
'header' => 'Email',
'name' => 'email',
),
array(
'header'=>'Average Revenue',
'value'=>'$data->averagerevenue',
),
)
));
?>
Sorting is applicable on user_id and email but Average Revenue column is not sortable. how to specify model method in sort() of CActiveDataprovider
Please help me to solve the problem.
Try this:
$modelUser = new CActiveDataProvider('User', array(
'sort' => array(
'attributes' => array(
'user_id',
'email',
'avgRevenue' //here's the change for you
),
'defaultOrder' => array(
'user_id' => CSort::SORT_ASC,
),
),
));
And your gridview column should be:
array(
'header'=>'Average Revenue',
'value'=>'avgRevenue',
),
and you can read more info on it over here:
http://www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-get-set-methods/
I need to export the whole data of an dataobject. Database fields and relations.
private static $db = array (
'URLSegment' => 'Varchar(255)',
'SKU' => 'Text',
'Title' => 'Text',
'Price' => 'Text',
'Content' => 'HTMLText',
'ItemConfig' => 'Int',
'Priority' => 'Int'
);
private static $has_one = array (
'Visual' => 'Image'
);
private static $has_many = array (
'Sizes' => 'ShopItem_Size',
'Colors' => 'ShopItem_Color'
);
private static $many_many = array (
'Visuals' => 'Image',
'Categories' => 'ShopCategory'
);
I added everything to getExportFields(). But as expected the result for the relations is "ManyManyList" or "HasManyList"
public function getExportFields() {
return array(
'SKU' => 'SKU',
'Title' => 'Title',
'Price' => 'Price',
'Content' => 'Content',
'ItemConfig' => 'ItemConfig',
'Visual' => 'Visual',
'Visuals' => 'Visuals',
'Sizes' => 'Sizes',
'Colors' => 'Colors',
'Categories' => 'Categories'
);
}
Is it possible to create such an export?
Thank you in advance
You can use any method name instead of a field/relation name in the exportFields array.
In your ModelAdmin class
public function getExportFields() {
return array(
'SKU' => 'SKU',
'Title' => 'Title',
'CategoryNames' => 'Categories'
);
}
and just have a method with that name on the DataObject, returning the relation data as a string:
public function CategoryNames(){
$catNames = array();
foreach($this->Categories() as $cat){
$catNames[] = $cat->getField('Title');
}
//use a separator that won't break the CSV file
return join("; ", $catNames);
}
I think this is even way better than the modeladmin creating magic fields in your CSV file,
that would make it inconsistent...