Is there a way to format the $this->find('all') array into the $this->find('list') in the view? The reason I ask is so that I can pass that new array into the form helper options and then use the $this->find('all') array to build some additional things I need?
Array (
[0] => Array ( [School] => Array ( [id] => 0 [name] => Nature [address] => 112 Main [max_students] => 25 [application_level] => 5 ) )
[1] => Array ( [School] => Array ( [id] => 1 [name] => Math [address] => 112 Smith [max_students] => 25 [application_level] => 0 ) )
[2] => Array ( [School] => Array ( [id] => 2 [name] => Art [address] => 112 Lane [max_students] => 25 [application_level] => 0 ) )
)
So this is the array I get when I do a find('all'). I want to build the array so it looks like:
Array (
[0] => 'Nature'
[1] => 'Math'
[2] => 'Art'
)
This is usually done by the $this->find('list') function. Though the reason I want the whole array is because I need to add the application_level into $this->Form->input() function. This is because I need to add the option of class with the application level attached so I show only the shows with the application level based on the previous selection.
EDIT: Can't I just do $this->find('list', [insert parameters here?]);? I just don't understand how you set up the additional parameters?
If your query isn't overly complicated and isn't going to return a excessive number of results, just run it twice (once for find all and once for find list).
Find all, list, first, whatever are all the same in terms of the paramaters you pass in. E.g.:
$this->Model->find('all', array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
));
... you literally replace all with list. In your case, probably easiest to do it twice, once for each. Like this:
$parameters = array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
);
$alldata = $this->Model->find('all', $parameters);
$listdata = $this->Model->find('list', $parameters);
Otherwise, you can loop through it and populate your own list:
$list = array();
foreach($findall as $row) {
$id = $row['id'];
$name = $row['name'];
$list[$id] = $name;
}
$this->set('listdata', $list);
Short answer to your question is that there's no quick, easy way to select all and list from the same query, but you can re use your parameters (conditions, order etc) by passing them in as predefined arrays, or populate your own list.
An alternative answer to creating the results formatted like find('list') from results from find('all') using CakePHP's hash utility:
//where $data is the result of find all
App::uses('Hash', 'Utility');
$ids = Hash::format($data, array('{n}.Model.id'), '{0}'); //ids in an array.
$names = Hash::format($data, array('{n}.Model.name'), '{0}'); //names in an array
$dataAsList = array_combine($ids, $names);
To improve on kai's answer. The Hash class has a method called combine that can do what you're trying to do in only one line
$list = Hash::combine($data,'{n}.Model.id','{n}.Model.name');
the $list will be a flat array like data from find('list');
Related
I have a large JSON array which is the result of querying the API of an Icinga2 monitoring system.
I have used json_decode like this in my code to decode it:
$response = json_decode($array, true);
and I can see the output looks like this:
Array
(
[results] => Array
(
[0] => Array
(
[attrs] => Array
(
[__name] => HOSTNAME0
[acknowledgement] => 0
[acknowledgement_expiry] => 0
...
...
[state] => 0
[state_type] => 1
[meta] => Array
(
)
[name] => HOSTNAME0
[type] => Host
)
[1] => Array
(
[attrs] => Array
(
[__name] => HOSTNAME1
[acknowledgement] => 0
[acknowledgement_expiry] => 0
...
...
[state] => 0
[state_type] => 1
[meta] => Array
(
)
[name] => HOSTNAME1
[type] => Host
)
There are 400 Records in total and it's quite a complex structure but the only bits I am really interested in are the name and state fields.
Basically my script has a list of 150 hostnames from another source and what I want to do is for each hostname, search for it in the array and return the value of the state field for that host.
So far I've been struggling to do this without looping through the entire array for each of the 150 hostnames. There must be a more efficient way to do a lookup in the array based on a hostname and return a single value but I can't figure it out.
Given, the name field has no logical sorting inside the json result, there is no way to look at least once at each element. If they are sorted alphabetical, you could use a simple binary search, which would give you the result in O(log(n)).
The other thing is, if you have to search for multiple names, you could put them inside an name assiciated array. This way, you only have an initial overhead of O(n) building the list and each following search would return you the state on O(1).
// building the array
$states = [];
foreach ($items as $item) {
$states[$item['name']] = $item['state'];
}
looking for HOSTNAME1
$state = $states['HOSTNAME1'];
I'm hoping that I've got the source data array in the correct layout as the format was a bit confusing from the original question. But the main idea is to use array_column to extract the "attrs" and key the result by the "name" element of this array.
$response = Array(
"results" => Array(
0 => Array(
"attrs" => Array(
"__name" => "HOSTNAME0",
"acknowledgement" => 0,
"acknowledgement_expiry" => 0,
"state" => 0,
"state_type" => 1
),
"name" => "HOSTNAME0",
"type" => "Host"
),
1 => Array(
"attrs" => Array(
"__name" => "HOSTNAME1",
"acknowledgement" => 0,
"acknowledgement_expiry" => 0,
"state" => 2,
"state_type" => 1
),
"name" => "HOSTNAME1",
"type" => "Host1"
)
)
);
$extract = array_column($response["results"], "attrs", "name");
print_r($extract);
With the sample data, this gives...
Array
(
[HOSTNAME0] => Array
(
[__name] => HOSTNAME0
[acknowledgement] => 0
[acknowledgement_expiry] => 0
[state] => 0
[state_type] => 1
)
[HOSTNAME1] => Array
(
[__name] => HOSTNAME1
[acknowledgement] => 0
[acknowledgement_expiry] => 0
[state] => 2
[state_type] => 1
)
)
So to find any server by name, you'd use
echo "HOSTNAME1=".$extract["HOSTNAME1"]["state"].PHP_EOL;
If you only wanted the state field (as you asked for) and wanted to simplify the array, you can then use...
array_walk($extract, function(&$data) {$data=$data["state"];});
print_r($extract);
The array_walk() goes through the array and just copies the state field to be the entry, so the result of this is...
Array
(
[HOSTNAME0] => 0
[HOSTNAME1] => 2
)
So now you just do...
echo "HOSTNAME1=".$extract["HOSTNAME1"].PHP_EOL;
I am retrieving data from the database with the help of belong to association using find method in cakephp. It's work perfectly but getting output like Array
(
[0] => Array
(
[PaymentLine] => Array
(
[Amount] => -1000.000
[OpenAmount] => -1000.000
[Narration] =>
)
[Payment] => Array
(
[TXNName] => Receipt-1
[TXNDate] => 2014-08-06
)
)
)
But I have to arrange data like Array
(
[0] => Array
(
[Amount] => -1000.000
[OpenAmount] => -1000.000
[Narration] =>
[TXNName] => Receipt-1
[TXNDate] => 2014-08-06
)
)
For these purpose I used Set::ClassicExtract method but that method not working for me... please help me...
Are you able to simply loop over the collection that is returned by the find method?
Perhaps try doing something like this:
$values = [];
foreach($array_of_arrays as $key => $array)
{
foreach($array as $value)
{
$values[] = $value;
}
}
$values will then contain all the values. The end result that you listed is not really possible to achieve if I understand what you are trying to do. This is because in that example you have duplicate keys for things such as 'id' and 'created'. If you only want to get the Arrays and store them in an array without the key then all you have to do is use:
array_values($array_of_arrays);
which will return the array but with the inner arrays having numeric keys instead of the enum ones.
If (for example) you had stored your data that you are getting back in a array called $dummy_data it would look something like this:
$dummy_data = [
'Profile' => Array
(
'id' => "12",
'user_id' => "121",
'skill' => "Baking Cakes",
'created' => "2007-05-01 10:31:01"
),
'User' => Array
(
'id' => "121",
'name' => "Gwoo the Kungwoo",
'created' => "2007-05-01 10:31:01"
)
];
If you know for a fact that the created values will always be the same for the 'Profile' and 'User' arrays then you can get a final array by using
array_merge($dummy_data['User'], $dummy_data['Profile']);
i want to get multiple rows value base on multiple id value and i m getting id in array.
id output array
pr($currentSessionData['Category']);
Array
(
[Category] => Array
(
[0] => 1
[1] => 24
[2] => 25
)
)
Below code for getting rows value but not working
$this->YourModelName->find('all', array(
'conditions' => array(
"YourModelName.id" => array($currentSessionData['Category'])
)
));
dont use array() .eg array($currentSessionData['Category'])
Try this.
$cat_ids=array(0=>10,1=>51,2=>51,3=>6561,4=>1,5=>561);
$this->YourModelName->find('all', array(
'conditions' => array(
"YourModelName.id" => cat_ids /*dont use array() */
)
));
i hope its helpful for you :)
I'm using Set class of Cakephp to format the find returned array but cannot seem to find a way to get the counter starting at zero and auto-increment for array keys so it is like
[0] => 3
[1] => 6
[2] => 12
I'm currently using below query to get the data from my HasAndBelongsToMany table.
$interest_ids = Set::combine($this->User->Interestsub->find('threaded', array
(
'conditions' => array
(
'Interestsub.name' => $interests
),
//'fields' => array('Interestsub.id'),
'recursive' => -1
)
),
'{n}.Interestsub.id',
'{n}.Interestsub.id'
);
The reason why I need this is that I'm currently trying to get the returned array as part of bigger parent array preparing to be saved for SaveAll function. To be formatted properly, I need below nested array coming out:
[0] => Array
(
[interestssub_id] => 12
[user_id] => 2
)
[1] => Array
(
[interestssub_id] => 22
[user_id] => 2
)
[2] => Array
(
[interestssub_id] => 32
[user_id] => 2
)
Is there a way we can use Combine class to format the returned array like above?
There's no real reason to use the Set class in this case. Just use good old fashioned php:
$threaded = $this->User->Interestsub->find('threaded', array(
'conditions' => array(
'Interestsub.name' => $interests
),
'recursive' => -1
));
$interest_ids = array();
foreach ($threaded as $thread) {
$interest_ids[] = array(
'interestssub_id' => $thread['Interestsub.id'],
'interestssub_id' => $thread['Interestsub.user_id']
);
}
I have a scenario where i have templates which has many themes.
Clear enough that my relation will be template hasmany themes
I wanna show the template with number of themes and i am applying this code:
$this->Template->recursive = 2;
$this->Template->bindModel(
array(
'hasMany' =>array(
'TemplateTheme'=>array(
'className'=>'TemplateTheme',
'fields' => 'count(TemplateTheme.id) AS themes'
)
)
),false
);
$this->paginate = array(
'order' => array('Template.modified DESC'),
'limit' =>$limit
);
$template = $this->paginate('Template');
pr($template);die();
but i am getting is
Array
(
[0] => Array
(
[Template] => Array
(
[id] => 1
[name] => churchDesign
[status] => Active
[created] => 2011-10-24 10:37:23
[modified] => 2011-10-25 15:16:46
)
[TemplateTheme] => Array
(
[0] => Array
(
[template_id] => 1
[TemplateTheme] => Array
(
[0] => Array
(
[themes] => 3
)
)
)
)
)
[1] => Array
(
[Template] => Array
(
[id] => 2
[name] => blossoms
[status] => Active
[created] => 2011-10-19 00:00:00
[modified] => 2011-10-24 14:05:27
)
[TemplateTheme] => Array
(
)
)
)
The problem here is it is counting all the themes in first array(1st template).see [TemplateTheme] => Array
(
[0] => Array
(
[themes] => 3
)
the third theme actually belongs to the 2nd template.The relation is
template_id is in themes table to represent each theme for a template.
Template 1 has 2 themes and template 2nd has one theme and currently its showing all the three themes in the first template.
i want it like this way
templatename1
count of themes 1st template
template name 2
count of themes of 2nd template
Please tell me the right way to do this.
If you wanted to get the count of the Themes per each Template, the most elegant way is to use counterCache.
Another way is to use Containable behavior, which fetches only the related data which you wish, and then count the fetched data array in PHP.
After you attach the containable behavior to the Template model, some code like this should work:
$allTemplates = $this->Templates->find('all', array(
'contain' => 'TemplateTheme.name'
));
foreach($allTemplates as $template){
$currentThemeCount = count($template['TemplateTheme']);
foreach($template['TemplateTheme'] as $theme){
echo $theme['name'];
}
}
Hope this helps someone along the way.
you can count the themes array like
foreach($template as $t){
count($t['TemplateTheme'])
}
hope this helps
You should be using the find('count') method instead of using a virtual 'count' field like you're doing now. For example:
$allTemplates = $this->Template->find('all');
foreach($allTemplates as $data) {
$templateCount = $this->Template->TemplateTheme->find('count', array(
'conditions' => array(
'TemplateTheme.template_id' => $data['Template']['id']
)
));
// $templateCount should now contain the count of this specific template. Foreach will continue recursion for all others.
}