I am trying to get rid of the redundant model names in the results array returned by the find method in CakePHP. As it is now, if I were to do something like $results = $this->Model->find('all'), I would have to access a result field by $results[Model][fieldName] instead of $results[fieldName].
I understand that having the model name in the array has benefits but I am trying to build an api so I need to json encode the array. With the model name included I get something hideous like:
[{"Model":{"field":"blah","field":"blah"}},{"Model":{"field":"blah","field":"blah"}}]
I want something more elegant like:
[{"field":"blah","field":"blah"},{"field":"blah","field":"blah"}]
Any ideas?
In your controller, instead of serializing the results of the find, serialise a level deeper.
Assuming CakePHP 2:
$things = $this->Thing->find('all');
$things = Set::extract('/Thing/.', $things);
Now your results should be free of the extra level in your JSON.
The alternative, lengthy way of doing it is to for loop over the results:
foreach ($things as $k => &$v) {
$v = $v['Thing']
}
After that, your $things will have removed the extra level of keys.
For later versions of Cake, use $things = Set::extract($things, '{n}.Thing');
Related
What I'm trying to achieve is something like this:
Hash::set($result, 'Model.{n}.field', 'replacementvalue');
But there doesn't seem to be a function in the hash class that does this. How can I achieve this without a foreach loop?
Edit: made it more clear I'm replacing a value not setting a new one
$result = Hash::insert($result, 'Model.{n}.field', 'newvalue')
seems to do exactly what you're asking
I have an object CuratedPage with property pageName.
I am creating an array of CuratedPage objects in controller and setting it for the view like this:
$this->set('curatedPages', $curatedPages);
In the view I am creating a dropdown of page names like this:
$pageNames = array();
foreach($curatedPages as $curatedPage) {
array_push($pageNames, $curatedPage->getPageName());
}
echo $this->Form->input('curatedPage', array('options' => $pageNames));
Is there a way in cakephp that will allow me to pass the array of CuratedPage objects to the Form->input(...) instead of creating an array of scalar values.
I'm not sure what you would expect the form helper to do in that case. However, depending on your PHP version (>= 5.2.0 required) the magic __toString() method might do it. If you implement it to return the pagename, then you would end up with the same result as with your posted snippet, ie an numerical indexed (the value attribute) HTML option list with the page names as labels.
However, implementing this only for that purpose in this specific view seems wrong to me, you're probably better of utilizing a custom helper, or as #BrianGlaz suggested prepare the data in the controller.
In these days I thought on how to work better with AppHelpers in CakePHP. I thought to use the AppHelper to make links and other html elements consistent depending by the context i need, for example, for users I have the method
$this->AppUser->profile($data, $options, $attributes);
this method returns a link styled for the users, with a specific css classes, maybe something line this:
<a class="user female" href="http://url/profiles/username">Username</a>
My problem is the data is structured differently by the situation, in some case I have an array like this:
$data['User']['id']
$data['User']['username']
$data['Profile']['user_id']
$data['Profile']['sex']
$data['Profile']['other']
And in some other cases, with different queries and different entities I have this:
$data['User']['id']
$data['User']['username']
$data['User']['Profile']['user_id']
$data['User']['Profile']['sex']
$data['User']['Profile']['other']
So I would like to understand if I missing something in the data hierarchy because it should be always structured in the same way?
And so should I to send data to the Helper always structured in the same way?
Should I let the helper parse the data depending by the situation, so with conditions to find where the data is?
That's pretty common, and is a result of finding related items multiple levels deep. I usually have a helper method on the Helper that normalizes the data.
I would always send the data to the helper as-is, and then restructure it as needed within the helper. It would look something like this:
function normalizeUserData($data) {
foreach ($data['User'] as $field => $value) {
if (is_array($value)) {
// move it to the same level as User
$data[$field] = $value;
unset($data['User'][$field]);
}
}
}
Now your functions can always expect the Profile data on the same level as the User key. This function isn't perfect and isn't recursive, but should give you a good start.
So i have a lot of different model types. Comments, posts, reviews, etc. And I want to join them into one integrated feed. Is there a CakePHP style of merging all this data for display, ordered by timestamp?
There are a lot of messy ways to do this but I wonder if there is some standard way which I am missing. Thanks!
Since the items are from different tables, it's difficult to retrieve them sorted together from the database in any case. Depending on how well organized your data is though, something as non-messy as this should do:
$posts = $this->Post->find(...);
$reviews = $this->Review->find(...);
$comments = $this->Comment->find(...);
$feed = array_merge($posts, $reviews, $comments);
usort($feed, function ($a, $b) {
$a = current($a);
$b = current($b);
return $strtotime($a['created']) - strtotime($b['created']);
});
philosophical? lol
No, I don't think there is one. Although you could write an afterSave() in app_model. Check for the data you're looking for, and if found, put it in Cache. It will probably be messy, but at least it's in one place, and doesn't affect the performance much.
There's definitely no way to merge the data automatically, but instead of firing separate queries for each relationship, you can grab it all at once using CakePHP's Containable behavior.
I need to use Inflector::slug() over all results fetched from my database, which are, of course, retrieved in an array. Is it possible somehow, or I'll need to loop each result and slugify it?
Thanks!
PHP's array_map() function might do what you need (although it assumes a simple indexed array).
array_map( 'Inflector::slug', $your_result )
If you're looking at something more complex, CakePHP's Set utility class may be helpful in a multi-step implementation.
I haven't tried this in a CakePHP context (i.e. mapping through a CakePHP class method), but I can't think of any reason it wouldn't work off the top of my head. Maybe it'll at least get you started.
Depending on the array you can use array_walk or array_walk_recursive.
Something like this should work.
This is for 5.3+;
array_walk_recursive($posts, function(&$value) {
$value = Inflector::slug($value);
});
If you wanted to limit it to a certain field you could also do something like this:
array_walk_recursive($posts, function(&$value, $key) {
if ($key == 'title') {
$value = Inflector::slug($value);
}
});
I haven't used Cake in a while but like Rob Wilkerson said, you might find that the Set class could make lighter work of this.