I have enum function in some of the rows and was wondering how we could fetch this in Cake to view it as select box?
Below is the function example:
enum('Uusi hakija','Jatkohakemus','40+','60+','Työyhteisöhanke','Mieshanke','Urheiluseurahanke')
The proper "Cake" way of doing this would be to use the Array Datasource from the official datasources plugin.
You setup a model for your enum data and assign all the normal relationships. To set the data set the records property in your model like so:
public $records = array(
array('id' => '1', 'name' => 'stuff'),
array('id' => '2')
);
$enumList = enum('Your', 'stuff', 'goes', 'here');
$vars = explode('.', $enumList);
$this->Form->select('Model.field_name', $vars);
Very simple but should work. Your option names would be 0, 1, 2, etc.
Check out CakePHP's FormHelper and the select input.
Related
I am working with a database which has all uppercase snakecase column names and when I fetch them with eloquent I do something like:
foreach($data as $key => $item){
$data[$key] = array_change_key_case($item);
}
This makes the keys ie the column names to lower case, but it soon becomes inefficient since I need to nested arrays too like so:
foreach($tasks as $key => $task){
foreach($task['users'] as $innerKey => $user){
$task['users'][$innerKey] = array_change_key_case($user);
}
$tasks[$key] = array_change_key_case($task);
}
And I can't change the database. Is there a way I can make eloquent give me back the column names in lower case?
You can transform column names at the driver level using PDO attributes. To do so, set your Laravel connection options (in app/config/database.php) like so:
return array(
'connections' => array(
'mysql' => array(
'options' => array(
PDO::ATTR_CASE => PDO::CASE_LOWER,
),
),
)
)
The default is PDO::CASE_NATURAL, which is why your code sees them as the database has them stored.
Update: If you are using MySQL, you might consider setting lower_case_table_names = 2, which tells the server:
If set to 2, table names are stored as given but compared in lowercase. This option also applies to database names and table aliases.
If override the getAttribute() method on your model you can transform the key before the call:
public function getAttribute($key)
{
$databaseColumn = implode('_', array_map('ucfirst', explode('_', $key)));
return parent::getAttribute($databaseColumn);
}
This will allow you to do $model->get_model_param and it will access $model->Get_Model_Param on the model.
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) {
I have two tables: 1. STUDENT- fields are- (studentid,studentname,batch)
2. BATCH- fields are- (batchid,batchname)
I want to populate a dropdown list from the field "batchname" ( from the table "BATCH") and have the batchname selected based on the field-"batch" (from the table "Student")
My controller -
function update($id){
$this->load->model('mod_studentprofile');
$data['query']= $this->mod_studentprofile->student_get($id); //to retrieve information from the table "STUDENT"
$data['query']= $this->mod_studentprofile->batch_get();
$data['main_content']='update_studentprofile';
$this->load->view('includes/template',$data);
}
My model -
function batch_get()
{
$query = $this->db->get('batch');
return $query->result();
}
Now, I cannot figure out how to populate the dropdown in the "View". Would you please kindly help me with this?
Thanks in Advance.
You need to put the options you want displayed in the drop down into an array, like so
$options = array(
'red' => 'Red',
'green' => 'Green',
'blue' => 'Blue',
);
// The params here are:
// 'color' => name of the drop down
// '$options' => options for the drop down
// 'red' => the selected value of the drop down
echo form_dropdown('color', $options, 'red');
What I would do is create a function in my model, say $model->dropdown_options() and use that to get the rows from the database and put them into an array.
See Jamie Rumbelow's 'MY_Model' class, he has a method that does exactly what you're describing.
https://github.com/jamierumbelow/codeigniter-base-model/blob/master/core/MY_Model.php
I'm using CakePHP 1.3.8, and I've installed the CakeDC Search plugin. I have a Tutorial model, which is in a HABTM relationship with a LearningGoal model.
I have a search action & view in the Tutorials controller with which I can successfully search fields in the Tutorial model. I'd also like to filter my tutorial search results using LearningGoal checkboxes on the same form. I've tried adding various parameters to Tutorial's $filterArgs and TutorialsController's $presetVars. I've also tried moving the relevant $filterArgs to the LearningGoal model. I have not yet been able to successfully trigger the entry for learning goals in $filterArgs.
I think I must be missing something obvious. Or maybe the Search plugin doesn't support what I'm trying to do. Does anyone know how to use this plugin to search on associated models?
So here's what I've figured out. You can combine what's below with the Search plugin directions to search on related models.
The $filterArgs piece in the Tutorial model must look like this:
var $filterArgs = array(
array('name' => 'LearningGoal', 'type' => 'subquery', 'method' => 'findByLearningGoals', 'field' => 'Tutorial.id'),
);
Here's the supporting function in the Tutorial model:
function findByLearningGoals($data = array()) {
$ids = explode('|', $data['LearningGoal']);
$ids = join(',', $ids);
$this->LearningGoalsTutorial->Behaviors->attach('Containable', array('autoFields' => false));
$this->LearningGoalsTutorial->Behaviors->attach('Search.Searchable');
$query = $this->LearningGoalsTutorial->getQuery('all',
array(
'conditions' => array('LearningGoalsTutorial.learning_goal_id IN (' . $ids . ')'),
'fields' => array('tutorial_id'),
)
);
return $query;
}
In TutorialsController, $presetVars should look like this:
public $presetVars = array(
array('field' => 'LearningGoal', 'type' => 'checkbox', 'model' => 'Tutorial'),
);
And in my search action in TutorialsController, I did this:
$this->LearningGoal = $this->Tutorial->LearningGoal;
The Prg component seems to need that.
I am using CakePHP version 2.X
Every time I come to do this in a project I always spend hours figuring out how to do it using CakeDC search behavior so I wrote this to try and remind myself with simple language what I need to do. I've also noticed that although Michael is generally correct there is no explanation which makes it more difficult to modify it to one's own project.
When you have a "has and belongs to many" relationship and you are wanting to search the joining table i.e. the table that has the two fields in it that joins the tables on either side of it together in a many-to-many relationship you want to create a subquery with a list of IDs from one of the tables in the relationship. The IDs from the table on the other side of the relationship are going to be checked to see if they are in that record and if they are then the record in the main table is going to be selected.
In this following example
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
all the records from ArosHandover will be selected if they have an aro_id of 3 then the Handover.id is used to decide which Handover records to select.
On to how to do this with the CakeDC search behaviour.
Firstly, place the field into the search form:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
etc...
notice that I have not placed the form element in the ArosHandover data space; another way of saying this is that when the form request is sent the field aro_id will be placed under the array called Handover.
In the model under the variable $filterArgs:
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
notice that the type is 'subquery' as I mentioned above you need to create a subquery in order to be able to find the appropriate records and by setting the type to subquery you are telling CakeDC to create a subquery snippet of SQL. The method is the function name that are going to write the code under. The field element is the name of the field which is going to appear in this part of the example query above
WHERE Handover.id in
Then you write the function that will return the subquery:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
In the Handovers controller:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
If you want any further explanation as to why I have done something, ask and if I get an email to tell me that you have asked I will give an answer if I am able to.
I'd like to exclude results from a call to a Lithium model's find() method. I need to do this for models with both MongoDB and MySQL data sources, but in SQL I mean something like WHERE myfield NOT IN (1,2,3).
I'd like to just be able to pass a not clause in the conditions array like below, but that doesn't appear to be possible.
Item::all(array('conditions' => array('not' => array('myfield' => array(1,2,3))));
So my question is, is this possible in Lithium in a way that I've overlooked? And if not, what would be the most Lithium-ish way to implement it for my models?
Just to clarify, Lithium's MongoDB adapter supports most SQL comparison operators as a convenience, so for either Mongo or MySQL, you could simply write the query as follows:
Item::all(array('conditions' => array(
'myfield' => array('!=' => array(1,2,3))
)));
And it should give you the results you expect. For MySQL, the query should look something like:
SELECT * FROM items WHERE myfield NOT IN (1, 2, 3);
And in Mongo:
db.items.find({ myfield: { $nin: [1, 2, 3] }})
Merely filtering for MongoDB can easily be achieved like this:
Item::all(array('conditions' =>
array('myfield' => array(
'$nin' => array(1,2,3)
))
));
If this is something you do a lot you could even create a custom finder for it :
class MyModel extends \lithium\data\Model {
public static function __init()
{
parent::__init();
static::finder('notin', function($self, $params, $chain) {
// Take all array keys that are not option keys
$array = array_diff_key($params['options'],
array_fill_keys(array('conditions', 'fields','order','limit','page'),0));
// Clean up options leaving only what li3 expects
$params['options'] = array_diff_key($params['options'], $array);
$params['options']['conditions'] = array(
'myfield' => array(
'$nin' => $array
)
);
return $chain->next($self, $params, $chain);
});
}
}
And call it like this :
MyModel::notin(array(1,2,3));
In the same manner you could create a custom finder for MySQL sources.
As you probably can see this creates some issues if you pass something like array('fields'=>$array) as it would overwrite the option.
What happens is that ::notin() (finders in general) has a distinct behavior for the (array,null) signature. If that happens it thinks the first array is options and the finder took no arguments.
Using notin($array,array()) breaks the previous finder because the first argument ends up in $params['notin'] when the real second argument (options) is passed.
If you mix data sources on the fly here I would create a custom model that does not inherit \lithium\data\Model and have it delegate
to the different models and create the conditions based on the end models data source.
class MyFacadeModel {
public static function byNotIn($conditions, $source) {
return ($source == "mongodb")
? $source::find( $rewrittenConditions)
: $source::find( $rewrittenConditionsForMysql );
}
}
(Code might be slightly incorrect as its mostly taken from the top of my head)