Where do I define view functions in CakePHP? - cakephp

I have a modifier procedure to show the "date" of an event.
if($event['Event']['is_multi_day']){
if( date('n',strtotime($event['Event']['start_day']) ) == date('n',strtotime($event['Event']['end_day'])) )
$date = date('j',strtotime($event['Event']['start_day'])).' - '.
date('j',strtotime($event['Event']['end_day'])).' '.
date('M',strtotime($event['Event']['end_day']));
else
$date = date('j',strtotime($event['Event']['start_day'])).' '.
date('M',strtotime($event['Event']['start_day'])).' - '.
date('j',strtotime($event['Event']['end_day'])).' '.
date('M',strtotime($event['Event']['end_day']));
}else{
$date = date('j M', strtotime($event['Event']['start_day']));
}
I have to include that code block in every page I display an event.
How do I define a function for this, which can be called from views I want?

Looks to me like you're going to be using this in a loop that displays a number of events and your code will be far from optimal in that situation.
I think you can reduce this line:
if( date('n',strtotime($event['Event']['start_day']) ) == date('n',strtotime($event['Event']['end_day'])) )
)
to:
if($event['Event']['start_day']==$event['Event']['end_day'])
(or something similar that compares the stored value without formatting. Formatting is for display purposes, not algorithmic comparison)
and this:
date('j',strtotime($event['Event']['end_day'])).' '.date('M',strtotime($event['Event']['end_day']));
to:
date('j M',strtotime($event['Event']['end_day']));
...and similar edits elsewhere. If this is in a loop, you need to reduce the number of unnecessary function calls and avoid concatenation of strings.
Personally, as this is a display function, I'd keep it on the view side of things (as opposed to the controller) and I'd probably do it as an element with passed parameters - see http://book.cakephp.org/view/560/Passing-Variables-into-an-Element

I would create a helper for this. For me it's more sensible rather than a component. If you using this data only to display it I believe that Helper is the proper solution. How to build custom helper

If it's raw data that you're manipulating (as shown) and you just want to return a value for display, I'd create a component and do it at the controller level. If you want to include presentation markup and/or logic, then an element is probably what you're looking for.

Related

Get each item in a collection with one query

I have a collection of slugs and want to get each corresponding page with one query.
Something like ...
Page::whereIn('slug', $slugs)->get();
... does only return the first page matching any slug in the collection.
Currently there is a loop, but that are dozens of queries I want to avoid.
Try using the whereRaw method and imploding your array into a string:
Page::whereRaw('slug IN ("' . $slugs->implode('","') . ')')->get();
As it turned out, whereIn was the right way. There was one minor mistake in my logic, and at the same time insufficient seeding data, that blowed everything up.
If someone does not know: whereRaw should be used with caution. To avoid SQL injection vulnerability, all user-submitted entries have to be passed as parameters.
Page::whereRaw('slug IN (?)', [$slug]);
Beware: Wrapping ? with quotes is a syntax error. The passed data will be single-quoted by default, at least on my machineā„¢.
select * from `pages` where `slug` in ('page');

Drupal Views variable $rows outputs as a String

I've developed a custom .tpl.php file for my View and in the past it has worked. Suddenly, while working on my Macbook using MAMP, Drupal decided that the $views->rows needs to be output as a String type and not an array. I've searched online and here for an answer but can't find one. I'm not doing any pre-process or views_render hooks in my template.php file for the theme. Does anyone have any ideas or have seen this before?
Thanks
After going through the Views module code, I couldn't find a _render hook that I would alter to cause the $rows to go back to an Array type. I did go through the modules/views/theme/views-view.tpl.php
So I replaced most of the code in my own template with the views-view.tpl.php code, as well as replacing the database with a previous version so I could start completely over. Turns out the issue was with my template file not outputting the exposed filters and such, as well as Views using
print $rows
instead of using $rows as an array. Seems like whatever version of Views I'm using uses the $rows variable as a String. So I've put in a %SPLIT% string in the Rewrite Results box so that I can do a PHP preg_split, feed that resulting array into my function to generate what I need, then do a preg_replace to get rid of the %SPLIT% strings in $rows. The result looked like what I had.
So, bottom line, looks like Unformatted Fields in Views now outputs $rows as a String variable instead of an array.
I also discovered this when trying to theme a certain row differently if a condition was met. Most things can be done in the Views UI but I couldn't figure out this one like that. I was finally able to do this with yourtheme_preprocess_views_view_unformatted(&$vars) in my template.php file. $rows seemed to still behave like an array there (although it went back to being a string later).
function yourtheme_preprocess_views_view_unformatted(&$vars) {
if ($vars['view']->name == "name_of_view") {
$rows = $vars['rows'];
$newRows = array();
foreach ($rows as $r) {
$test = strpos($r, "string_i_looked_for");
if ($test) {
$newRows[] = "<hr>$r"; // I needed to put in a divider if the condition was met.
}
else {
$newRows[] = $r;
}
}
$vars['rows'] = $newRows; // So that the array of new rows is what will be sent along.
}
}
My actual problem required the divider only in the first instance of the test, so I also used a counter, but I think the example above gives the idea.

In CakePHP, how do I go about making the following manipulation to my specific text files, and caching it?

Essentially at the moment I have a text file written in the script of another language. It will be stored as an element, and called to be served up as text when needed.
I want to manipulate this file with the following:
Replace all the existing variables with a php array value storing that variable name. So if the variable looks like #foo, it becomes $variables['foo'].
Create an array out of the entire file, making each line a row in the array.
The result is something like
return array(
'first line of code which has a variable called ' . $variables['foo'] . 'in it',
'second line...'
);
What would the simplest method be to go about this, and is there a way to cache the process, or should I perhaps just save and store the new file some where I can access it?
Thank you!
Use str_replace() (documentation: php.net) to replace all occurences
Use explode() in the following way: How to put string in array, split by new line?
Caching.
I suggest you use View caching, a built in CakePHP helper.
To distinguish between the different caches for the different variable names, make sure you call the controller function with a parameter that discriminates among the variable names. E.g.:
public function generate_script($varname){
// some code
}
If the discriminating variable is not known on calling the controller method (but is e.g. determined inside the controller method), you should look into the Cache API

Invalidate Fields from Multiple Models

I have a form with multiple models in it. The validates in the models seems correct and the models are associated properly. But how do I invalidateFields from two models and pass the display error back to the form?
Code in my users)_controller.php is:
$errors = $this->User->invalidFields(array('fieldList' => array('password','cpassword','firstname','lastname','email')));
$this->User->set('errors',$errors);
But I have a Profile model chained like this:
$this->User->Profile
and want it to invalidFields to Profile.zip.
you can use chained if clauses like described at
http://www.dereuromark.de/2010/10/09/about-php-basics-and-pitfalls/
basically, you use & instead of &&
so if you got a main model and related data:
$this->User->set($this->data);
$this->User->Profile->set($this->data);
if ($this->User->validates() & $this->User->Profile->validates()) {
//continue
}
the single & makes sure that both conditions are executed (with && you would only trigger the first one if there was an error and therefore the validation rules would not get rendered for the related model)
you could also do:
$val1 = $this->User->validates();
$val2 = $this->User->Profile->validates();
if ($val1 && $val2) {}
this way they both get executed before you go into the if clause.
Instead of manually setting errors in the invalid fields array, I would suggest to use the $validate array to set up validation rules.
You can define your own, complex rules if the built in ones are not enough.

CakePHP: Use Inflector Class over whole array

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.

Resources