Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Customising the CMS /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Sorting a DataObjectSet with lazy loading


Go to End


2 Posts   1542 Views

Avatar
KatB

Community Member, 105 Posts

16 November 2015 at 7:08pm

I have a table with a version number that I would like to sort on (eg. 1. 10). However, it is not a straightforward integer or string as 1.10 is bigger than 1.9. To sort my data, I will need to access the result to sort it. However, with lazy loading, I can't get access to the data until I step through it in a foreach loop or in a template. How can I get access to the data outside of a foreach loop?

Avatar
KatB

Community Member, 105 Posts

23 November 2015 at 2:49pm

I've figured it out. I'm writing up my answer in case anyone else ever needs to do this and can do the same thing, but faster. My answer is unlikely to fit anyone else 100% and I'm sure it can be improved on. I'm sure I'm using classes and methods that are deprecated. I'm even sure there are bugs in it. It is software, after all. But it is a great place to start. I'm totally happy for comments that improve on it.

Background: I have a Software object that has many Release children. I wanted to sort the Release children in descending version order to display.

So, in my Software object I have a function to sort Release children. Something like this can be easily Googled.

public function compareVersionDesc($release1, $release2){
$result = 0;
$version1 = $release1->Version;
$version2 = $release2->Version;

$parts1 = explode('.', $version1);
$parts2 = explode('.', $version2);

for($i=0;$i<count($parts2);$i++) {

if ($i < count($parts1)) {

if (intval($parts2[$i]) > intval($parts1[$i])) {
return 1;
}
else if (intval($parts2[$i]) < intval($parts1[$i])) {
return -1;
}

}
else {

$result = 1;
}

}

return $result;
}

The subsequent method in the Software class uses SQLQuery to get the data out of the database. I'm sure this method can be improved with an SQLQuery->addWhere method call, to limit the Releases at SQL level, rather than at the level I do it here. There are two interesting parts here, firstly, the data that is pulled out of the database is easily converted back into it's original object and secondly, the usort is called with the $this element in the line "if (usort($releaseList, array($this, "compareVersionDesc"))){"

public function getSortedVersions(){

$sqlQuery = new SQLQuery();

$sqlQuery->setFrom('Release');

$result = $sqlQuery->execute();

$releaseList = array();

foreach ($result as $row){

if ($row[SoftwareID] == $this->ID) {
$release = new Release($row);
array_push($releaseList, $release);
}
}

if (usort($releaseList, array($this, "compareVersionDesc"))){
return $releaseList;
}

return NULL;
}

I then use getSortedVersions() when calling for the Release children of the Software object in my controller.

In some ways it would be really good to briefly turn off lazy loading so we don't have to turn to SQLQuery.