I have a function in my Teacher model which returns categories array.
getCaterogies() {
return array('1' => 'short tempered', '2' => 'funny', '3' => 'visionary', ...);
}
I am storing indexes in database and displaying values everywhere using the value of the array corresponding to that..
$categories = $teacher->categories;
$category = $categories[$teacher->category];
I am doing this because once somebody suggested to me not to store strings in a database which are statuses, instead store integer values, and either store the conversion in the database or define it in ht model. The problem with strings is that they are more prone to human errors in comparisons. Maybe because of case sensitiveness.
Now the problem I am facing is that while displaying values in gridview, I need to write the 2 lines in a value field, but it is an expression, and outside variables also it doesn't take.
How can I get this working for gridview?
You can use anonymous function as value which can take $row, $data params where $row holds the row number (zero-based) and $data contains the data model for the row.
That way you can have it defined inside only.
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
array(
'name'=>'..',
'value'=>function($data,$row){
$categories = $teacher->categories;
return $categories[$data->category];
},
),
),
));
And if you want to use it from outside, you can rely on PHP's use:
$categories = $teacher->categories;
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
array(
'name'=>'..',
'value'=>function($data,$row) use ($categories){
return $categories[$data->category];
},
),
),
));
I would personally recommend second one, because that way the calculation of the array will be only once and will be used in all cases.
You can write
$categories = $teacher->categories;
$category = $categories[$teacher->category];
in one line:
$category = $teacher->categories[$teacher->category];
Also, I suggest you to use another solution:
class ModelClass
{
const STATUS_SHORT_TEMPERED = 1;
const STATUS_FUNNY = 2;
const STATUS_VISIONARY = 3;
}
This allow you to use a more semantic
ModelClass::STATUS_FUNNY;
instead of less semantic
2;
Also, you can compose your array in this way:
getCaterogies() {
return array(
ModelClass::STATUS_FUNNY => 'status funny',
...
);
}
'urlCreator' => function ($action, $model, $key, $index) use($under_category) {
Related
Trying to convert a procedural script to OOP.
In my procedural script I define $metadata = array() before I define variables in a foreach loop like such:
foreach ($productdata as $productinfo) {
$price = (float) $productinfo['Price'];
$regularprice = (float) $productinfo['RegularPrice'];
And then proceeded to manually input/type what I wanted the key value (_cost and _regular_cost)
$metadata[] =
[
'key' => '_cost',
'value' => $price
];
$metadata[] =
[
'key' => '_regular_cost',
'value' => $regularprice
];
Now I am trying to compact it into a class, but am uncertain how to generate these key => _{value} names.
Something I've thought to try.. could be totally off.
So the name of my class is WooCommerceController
Class WooCommerceController
{
protected $metadata;
static $metadata_keys = ['_cost', '_regular_cost'];
Then I thought about making the class function either accept an individual metadata value
public function generateMetaData(string $metadatavalue) {
or an array of values
public function generateMetaData(array $metadatavalue_array) {
but no matter which I can think of, even if I have access to the $metadata_keys static variable, I can't think of a way for the function to distinguish between for example $price and $regularprice.
The only thing I can think of is to pass it in a strict indexed way (ensure the same order of values being passed congruent with the values in WooCommerceController::$metadata_keys)..
or I thought maybe I could name my variables --- instead of $price, rename them $_cost --- and then I was just researching methods to converting variables name to string but this seems like it is more of a hackish solution
Can anyone think of a more proper solution?
Not sure how proper this is still very much learning OOP and OOP design patterns but I came up with this solution:
Class WooCommerceController
{
protected $metadata = array();
public function generateMetaData(string $metadatakeyname, string $metadatavalue) {
$this->metadata[] =
[
'key' => $metadatakeyname,
'value' => $metadatavalue
];
return $this->metadata;
}
And in context using it like this:
$metadata = $woo->generateMetaData('_cost', $price);
$metadata = $woo->generateMetaData('_regular_cost', $price);
And it appears to fill up the array in the class context (this->metadata) and also each additional statement adds to the $metadata array out of the class context.
I am using PHP Yii and try to display a value that were derived from the value saved in database.
Here is my model
Model-TradeRecord
public type; //Type:'1' means Buy,'2' means Sell. $model->type is get from database
public function attributeLabels(){
return array(
/* some attribute */
'type'=>'Trade Type' //This is also the column header
}
public function getTradetype(){
return array('1' => 'Buy', '2' => 'Sell');
}
View- index
<!-- dropdown list-->
<?php echo CHtml::dropDownList('TradeRecord[type]', $model->type, //any better solution?
$model->tradetype,
array('empty' => '(select..)','class'=>'form-control'));
?>
<!--CgridView column-->
<?php $this->widget('bootstrap.widgets.BsGridView', array(
'id'=>'trade-record-grid',
'dataProvider'=>$dp,
'columns'=>array(
array(
'header' => '$data->getAttributeLabel("type")', //not worked!
'name'=>'type',
'value'=>'($data->tradetype($data->type))', //not worked!
),
),
As you can see,I had set an getTradetype method in the model for the mapping relation.
I tried to make the code clean. But I thought there might be some better solution for the dropdownlist case.As for the Cgridview case, my code did not work at all.
Thanks.
for the grid view, change you function to return string, not array
public function getTradetype(){
$types = array('1' => 'Buy', '2' => 'Sell');
return isset($types[ $this->type ]) ? $types[ $this->type ] : 'undefined';
}
if you want default name of the variable in the header, you don't need to specify it in the array, value and name would be enough, I think you didn't set the name properly
As an alternative to Tinybyte's answer, I usually put the relation as an array and use a function similar to his/her answer.
public static $tradeTypes = array('1' => 'Buy', '2' => 'Sell');
...
public function getTradeType() {
return isset(self::$tradeTypes[ $this->type ]) ? self::$tradeTypes[ $this->type ] : 'undefined';
}
This makes it possible to use TradeRecord::tradeTypes as the dropdown list's options and tradeType as the value for the grid view.
i've been trying to learn more about how to have fat models and skinny controllers the right way, because before my models would have basically no code and i'm trying to change that. My function works, but now i'm trying to combine two find() queries that look almost exactly the same except one of them has a simple condition.
My model looks something like this:
function pieChart() {
//Get Data for PieChart
$this->RecordDrug->virtualFields['sum'] ='COUNT(*)';
$records = array();
$records=$this->RecordDrug->find('list',
array('fields' => array( 'Drug.drug', 'sum'),
'contain' => array( 'Drug', 'Record' ),
'group' => 'Drug.Drug'
));
$this->set('output',$records);
return $records;
}
I will have two controllers using this. One of them will use this code as is, just simply call the pieChart() function. The other controller will have to see a condition that only selects the users entries. So
'conditions' => array('Record.user_id' => $this->Auth->user('id'))
How do I go about this the right way? I think i'm having trouble with this because my OOP knowledge is pretty limited. If anyone has any examples or resources that can help me make my find() functions more efficient and streamlined, i'd really appreciate it.
I done that kind of things very simple:
public function myQuery($conditions = null) {
$this->virtualFields['sum'] ='COUNT(*)';
$result = $this->find('all', array('conditions' => $conditions,
'fields' => array('Drug.drug', 'sum'),
'contain' => array('Drug','Record'),
'group' => 'Drug.Drug'
));
return $result;
}
Now you can call this with your argument:
$conditions = array('Record.user_id' => $this->Auth->user('id'));
$data = $this->RecordDrug->myQuery($conditions);
Or without it:
$data = $this->RecordDrug->myQuery();
Note that in this case you need to put myQuery() in to RecordDrug model and you need to use 'all' instead of 'list', because 'list' doesn't support contain option.
So now if you have additional conditions - you just need to pass it in the argument. If you leave it null - it do the query without the conditions statement.
Sometimes while initializing variables, you want to pass them values that are too complex to be computed in a single command, so you usually either compute a dummy variable before and then pass its value, or define a function elsewhere, and pass it's return value to our variable.
My question (wish) is, is it possible instead compute to a variable on the fly using anonymous functions?
for example, instead of use this:
$post = get_post();
$id = $post->ID;
$array = array(
'foo' => 'hi!',
'bar' => $id
);
Lets use something like this:
$array = array(
'foo' => 'hi!',
'bar' => (function(){
$post = get_post();
return $post->ID;
})
);
Code is totaly random.
In your example, the following would do just fine:
$array = array('foo'=>'hi!','bar'=>(get_post()->ID));
However, with consideration to your question being a bit more open ended and not specific to your code snippet, you may find this stackoverflow answer acceptable.
$a = array('foo' => call_user_func(
function(){
$b = 5;
return $b;
})
);
var_dump($a);
I want to do something very straight forward and simple. I want to have two different sets of paginated data on the same page. The two different sets depend on different models. For discussion's sake we'll say they are Image and Item.
I can set up two pagers for two models, and get the correct set of objects. I can get the correct pager links. But when it comes to actually following the links to the parameters, both pagers read the parameters and assume they apply to them.
It winds up looking something like this:
$this->paginate = array (
'Item'=>array(
'conditions'=>array('user_id'=>$id),
'limit' => 6,
'order' => array(
'Item.votes'=>'desc',
'Item.created'=>'desc'
),
'contain'=>array(
'User',
'ItemImage' => array (
'order'=>'ItemImage__imageVotes desc'
)
)
),
'Image'=>array(
'limit'=>6,
'contain'=>array(
'User',
'ItemImage'=>array('Item'),
),
'order'=>array(
'Image.votes'=>'desc',
'Image.views'=>'desc'
),
'conditions'=>array(
'Image.isItemImage'=>1,
'Image.user_id'=>$id
)
)
);
$this->set('items', $this->paginate('Item'));
$this->set('images', $this->paginate('Image'));
That's in the controller. In the view I have sort links that look like this:
<div class="control"><?php echo $this->Paginator->sort('Newest', 'Image.created', array('model'=>'Image')); ?></div>
However, that yields a link that looks like this:
http://localhost/profile/37/page:1/sort:Image.created/direction:asc
There's nothing in there to tell the paginator which model I intend to sort. So when I click on the link it attempts to sort both models by Image.created. The result is an error, because Item cannot be sorted by Image.created. Is there something I'm doing wrong? Or is this something that isn't supported by CakePHP's paginator?
You'll need to override the paginate method for the Model of the Controller of that page.
I did something similar, maybe this snippet will help:
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array())
{
$pageParams = compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group');
$this->contain('ModuleType', 'NodeDescriptor');
$pageItems = $this->find('all',$pageParams);
$pagesOut = array();
foreach($pageItems as $pageItem)
{
$status = $pageItem['SiteAdmin']['status_id'];
$moduleInfo = null;
$nodeTitle = $pageItem['NodeDescriptor']['title'];
$published = $pageItem['NodeDescriptor']['published'];
$pageitemID = $pageItem['SiteAdmin']['id'];
$moduleId = $pageItem['SiteAdmin']['module_id'];
$contName = $pageItem['ModuleType']['controller'];
if($moduleId)
{
$thisModel = ClassRegistry::getObject($moduleType);
$thisModel->contain();
$moduleInfo = $thisModel->read(null,$moduleId);
$moduleInfo = $moduleInfo[$moduleType];
}
$pagesOut[] = array(
'status'=>$status,
'node'=>$nodeTitle,
'published'=>$published,
'info'=>$moduleInfo,
'module_id'=>$moduleId,
'contName'=>$contName,
'pageitem_id'=>$pageitemID);
}
return $pagesOut;
}
By doing it this way, you gain control over the parameters passed to paginate, so you can pass model specific data, control flags etc.
The easiest solution would be to implement both grids as elements that fetch their own data and use AJAX to load the elements into the page.
The only other option would be to modify the params so you pass the params for both grids to each grid when sorting or stepping through pages. The code posted by Leo above is a good start. You can prepend the Model key from the paginate array onto each named param and make sure you pass all url params to the paginate function and you should be headed in the right direction.