Hi,
This is my first post on the forums. I've been learning silverstripe for a while, and have started my first production website on it. I have an issue that has been bugging me for months and I can't seem to resolve on my own. So I decided to post here and ask for help. First some code:
these are the relevant parts of page.php
class Page extends SiteTree {
public static $db = array(
'ShowChildren' => 'Boolean',
'IsHotel' => 'Boolean',
'HotelCategory' => 'Varchar',
'HotelBoard' => 'Varchar',
'NChildren' => 'Int'
);
...
class Page_Controller extends ContentController {
public static $allowed_actions = array (
);
public function init() {
parent::init();
}
public function getHotelStars() {
if($this->data()->IsHotel) {
$hc = $this->data()->HotelCategory;
if( preg_match( '/(\*+)(\+?)/', $hc, $matches ) ) {
$stars = strlen($matches[1]);
$halfStar = $matches[2] == '+';
} else if( preg_match( '/(\d)(\++)/', $hc, $matches ) ) {
$stars = $matches[1];
$halfStar = $matches[2] == '+';
} else if( preg_match( '/(\d)(\.5)?/', $hc, $matches ) ) {
$stars = $matches[1];
$halfStar = $matches[2] == '.5';
}
$hc = array_fill( 0, $stars, array( 'star' => '*' ));
if ( $halfStar ) array_push( $hc, array( 'star' => '+' ));
return new DataObjectSet($hc);
} else return '';
}
and this is the relevant bit of page.ss:
<% if IsHotel %>
<% loop $HotelStars %>
<% if star = '*' %>
<img src="$ThemeDir/images/star.png" />
<% else %>
<img src="$ThemeDir/images/halfstar.png" />
<% end_if %>
<% end_loop %>
<% end_if %>
Ok so what I've been doing here is try and predict every possible value a user can add to the property $HotelCategory and parse inside the controller function HotelStars into a standard format: Array( '*', '*', '*', '+') for a hotel with 3.5 stars. I used the array format so it can be looped through in the template and I can keep the relevant img tags in the template.
It works too. Well, it works as long as you are printing out the stars on the same page. If you are looping through child pages, it doesn't work. It took me a while to figure out why, but now I understand that the controller functions are not accessible within that context. I can call the controller function of the parent page using up or top but that only returns the category (if it exists) of the top page. So I modified the function as such:
public function getHotelStars($hc = -1) {
if($hc != -1 || $this->data()->IsHotel) {
if( $hc == -1 ) $hc = $this->data()->HotelCategory;
if( preg_match( '/(\*+)(\+?)/', $hc, $matches ) ) {
$stars = strlen($matches[1]);
$halfStar = $matches[2] == '+';
} else if( preg_match( '/(\d)(\++)/', $hc, $matches ) ) {
$stars = $matches[1];
$halfStar = $matches[2] == '+';
} else if( preg_match( '/(\d)(\.5)?/', $hc, $matches ) ) {
$stars = $matches[1];
$halfStar = $matches[2] == '.5';
}
$hc = array_fill( 0, $stars, array( 'star' => '*' ));
if ( $halfStar ) array_push( $hc, array( 'star' => '+' ));
return new DataObjectSet($hc);
} else return '';
}
Now theoretically, I should be able to pass a string argument to the function and it will use that instead of $HotelCategory. Then inside the template I could do this:
<% loop $PaginatedChildren %>
<% if IsHotel %>
<% loop $Top.HotelStars($HotelCategory) %>
<% if star = '*' %>
<img src="$ThemeDir/images/star.png" />
<% else %>
<img src="$ThemeDir/images/halfstar.png" />
<% end_if %>
<% end_loop %>
<% end_if %>
But this doesn't work. Not only can I not pass $HotelCategory to the function, I can't pass a literal, not even in the regular scope. A simple:
$HotelStars(5)
$HotelStars('5')
$HotelStars("5")
Does nothing adding an echo $hc to the beginning of the function just prints -1 regardless of what i put in the parentheses.
So why is this? The documentation seems to indicate that I can pass parameters to controller functions. What am I doing wrong?
Having browsed through some forum posts I have come to the conclusion that I can't pass properties as parameters anways and that would kill this particular implementation. But I am still curious as to why I can't seem to pass a literal.
Beyond this, what is the best method of pre processing database values? Should I be incorporating the controller function into the model? Probably the best way to go about this is to sanitize and standardize the database entries for $HotelCategory after input in the cms. IMHO the most elegant way to do this would be with an accessor function setHotelCategoy. But I am not sure they exist in silverstripe. So I am asking if silverstripe detects such accessor functions.
I assume there is also a way to add a preprocessing function for input values in the getCMSFields method. Something along the lines of:
$fields->addFieldToTab('Root.Hotel', new TextField('HotelCategory', 'Category', preprocessor));
But I don't think this would be as elegant as accessor functions. Either way there has to be a way to turn whatever value I end up storing in the database (either 3.5 or '***+') into images and to do this in the template. So I would either have to be able to split the string into an array or use a string replacement function in the template or use something like a traditional for loop for the float. How would I go about either one of these? Either that or I would have to be able to store a DataObjectSet in $db.
So to sum up this is what I am asking:
1- Why can't I pass a parameter to my controller function?
2- Can I pass a db property as a parameter to a controller function?
3- Does silverstripe have a way of defining accessor functions for db properties? Or how would I add a function to sanitize the inputs, during backend field deceleration.
4- Are there any functions available in the template that would allow for string replacement, an equivalent of the php explode function or a traditional for loop?
5- Can I store a DataObjectSet in the $db.
Thanks.