Sorting blocks by custom block value before displaying

Permalink 2 users found helpful
Hi!

Is there a way to sort a number of blocks, added to an area, using code?

I have an area that contains a custom block. In that block there is a value of "date". So I want to grab the "date"-value from each block and the sort the blocks by the month value and then display them in that order in the area.

<div class="sidebar-right-add">
<?php    
     $a = new GlobalArea('calender');
     //$a->display($c);
     $blocks = $a->getAreaBlocksArray($c);
     //Some kind of sorting here
     $a->display($blocks);
?>
</div>


This is what I had in mind. Is it doable?

Thanks
Mr Lindau

View Replies: View Best Answer
Remo replied on at Permalink Reply
Remo
I'd start by checking the code of the parent classes of Area (or GlobalArea in your case). In concrete5, you can easily override core classes and add your code there.
MrLindau replied on at Permalink Reply
Hi Remo!

I didn't get a word of what you just said. =)
Would you care to explain a bit more?

Thanks
Remo replied on at Permalink Reply
Remo
You know about OOP? Object-Oriented-Programming?
Otherwise I'm going to use more words you're not familiar with (:
MrLindau replied on at Permalink Reply
I'm not that good at it. But I understand the basics of it.
So that's the classes you are talking about. I thought of css and didn't get that. sorry. But you can treat me like a noob if you will ;)

I thought it could be done by something like usort and that one didn't have to look in the corefiles, but I guess I was wrong.
JohntheFish replied on at Permalink Reply
JohntheFish
I would be tempted to leave the area class alone and do this in the theme where the area is output.

Copy an exiting page type php file to create a new page type. Rather than outputting the (Main?) area in one go, get the list of blocks in the area, sort them, and then loop through the sorted block list to output each in turn. Only do it outside of edit mode so it doesn't cause cause issue when editing.

There is enough documentation and howtos on outputting areas that you should be able to get the basics of listing blocks and outputting them worked out from googling and reading docs.

To start with, get it working without the sorting - it should look no different to outputting the area did before you start hacking it about. Then add the sorting to the list of blocks.
MrLindau replied on at Permalink Reply
I got this far. It sorts the blocks and displays them in the right order. But is there a way to get this to sort in edit mode, and sustain the ability to add/edit block? I'm looking for a way to display the block in the right order and keep the area-functions (add/edit).

<div class="sidebar-right-add">
<?php    
     $a = new GlobalArea('calender');
     $blocks = $a->getAreaBlocksArray($c);
     function cmp($a, $b){ 
         $a = strtotime($a->instance->field_2_date_value);
         $b = strtotime($b->instance->field_2_date_value);
         if($a ==  $b){ return 0 ; } 
    return ($a < $b) ? -1 : 1;
     }
     uasort($blocks, 'cmp');
     //This shows the blocks in the right order, but the area obviously isn't working and blocks are not editable as they do not refer to the area $a
     foreach ($blocks as $b){$b->display();}
     //This displays the blocks but not in the right order, but they are editable
     $a->display($blocks);
JohntheFish replied on at Permalink Reply
JohntheFish
I seem to remember there is a method on the area object to display a single block. It may be better in the foreach loop.

For editing, you could put all your new code in the else part of an isEditMode() check, then in edit mode just output the area as normal.

if ($c->isEditMode()) {
  //display area the usual way
} else {
  // sort blocks and output sorted
}
MrLindau replied on at Permalink Reply
I thought of the option to have different views if you are logged in or not but that doesn't look good.

I looked at all the methods but I couldn't find one. Is there more documentation then this page?

http://www.concrete5.org/documentation/developers/pages/areas...
JohntheFish replied on at Permalink Reply
JohntheFish
Its not logged in or not, its only when the page is actually in edit mode. So when just viewing a page, everyone gets the sorted view.

For more info on the area class, the source is at:
concrete/core/models/area.php

Looking at line 522, it seems that the display function will accept an alternate array of blocks as an optional second parameter, so perhaps you could feed it back your sorted array.
MrLindau replied on at Permalink Reply
It looks as though it should take the sorted $blocks array and just display it but maybe I'm not clear on how to implement the second variable $alternateBlockArray.

The Method in line 522 says:
function display(&$c, $alternateBlockArray = null)



When I show the ArrayKeys before I run the sorting it looks like this:
Array
(
    [0] => 0
    [1] => 1
    [2] => 2
    [3] => 3
)


And after the sorting it looks, as it should, like this:
Array
(
    [0] => 1
    [1] => 0
    [2] => 2
    [3] => 3
)


And this it the $blocks array that is shown.

An as far as I can understand that should be:
$a->display($blocks);


But that only shows the unsorted array. How do I get it to ignore the $c and us my own array?
I tried this but no luck, so I guess I'm doing it wrong.
$a->display(null,$blocks);


Could it be that the method sorts the array as it is ordered in the db, and ignores the input array order? Or do I implement the method the wrong way?
JohntheFish replied on at Permalink Reply
JohntheFish
Having never played with this, I am just guessing:
$a->display($c, $sorted_array);


However, this can't be a widely used feature, so I have no idea whether the above is correct or even if the underlying code actually works.

PS. The area class is the one @Remo suggested overriding if you really want to get deep into it.
MrLindau replied on at Permalink Reply
Nope, that didn't work, it just displayed the blocks in the unsorted order.

Can I make an own area.php by putting the file in the models directory ,at the top level and not in core, and extend this class and make my own method?
MrLindau replied on at Permalink Reply
I manged to get it working. I put the area.php in models and extended it. Put a copy of the display() method in and all I did was to change this line from:

function display(&$c, $alternateBlockArray = null) {}


to:
function displaySorted($c,$alternateBlockArray) {}


and it worked. the part with "=null" looks a bit suspicious...
TheRealSean replied on at Permalink Reply
TheRealSean
Thanks for the tip, I'm currently looking to do something very similar, with the blocks organised by alphabetical values, and also noticing the $alternateBlockArray = null bit causes an issue?

I don't know what's going on there but your work around fixes my issue :)

Thanks
Sean
TheRealSean replied on at Permalink Reply
TheRealSean
I don't know if I am doing something wrong? but I think I may have got around the variable being passed through as null.

I was using this on a single page, I did notice that if I used Page::getCurrentPage() instead of $c then I was able to pass through an array?

In my example I have created a glossary, with custom blocks that have a Category, this category can be passed through to the page and filters the blocks.

<?php
    //If I don't add the following the array appears to go through as null, 
$c = Page::getCurrentPage();
                $a = new Area('Main');
                //Then get a category if we have one to get?
                $glossary = $_GET['glossary'];
                //create an array of the blocks that appear in that area
                $blocksArray = $a->getAreaBlocksArray($c);
                $amendedBlockArray = null;
                foreach($blocksArray as $block){
                    if(!empty($glossary) && $glossary == $block->getInstance()->glossary_category_value  ){
                        //Only display blocks that match the category
                        //so build up an array of those blocks only
                        $amendedBlockArray[] = $block;
                    }

**Edit, when I added this as a single page I needed the Page::getCurrentPage() today I notice that the single page is using the default page type (instead of the view?) but the default.php works with the default $c.

I'm really not to sure what's going on, maybe a cache issue?
kirkroberts replied on at Permalink Reply
kirkroberts
MrLindau, this was a HUGE help to me! Thanks for including your code near the top of the thread.
The freaky thing was I even had the same variable name "field_2_date_value" so all I had to do was switch the comparison sign in your cmp function so the blocks showed from most recent to oldest. Added in a check for $c->isEditMode() and I was done.
Hope you got yours sorted out the way you wanted.
BinaryFold4 replied on at Permalink Best Answer Reply
BinaryFold4
Hi,

Thanks for this thread. It was really useful helping me solve my problem.

I needed to get blocks to read a date in a text field and then display the blocks with the most recent block/date at the top. Using the info in this thread and a little Googling I can up with the following which seems to work perfectly. The blocks appear in their natural order in edit mode but date order on the public page.

Ali

<?php
    $a = new Area('Main Content');
    $blocks = $a->getAreaBlocksArray($c);
    if (!$c->isEditMode())
    {
        function cmp($a, $b)
        {
            $a = strtotime($a->instance->field_4_textbox_text);
            $b = strtotime($b->instance->field_4_textbox_text);
            if($a ==  $b){ return 0 ; }
            return ($a < $b) ? -1 : 1;
        }
        uasort($blocks, 'cmp');
        $reversed = array_reverse($blocks);
        foreach ($reversed as $b)
JohntheFish replied on at Permalink Reply
JohntheFish
Thats a nice clean summary of everything that has proceeded. As nothing above had been marked best answer, the honour is yours.