dynamically view blocktype instances: Is this possible?

Permalink
I have thousands of instances of a self-made blocktype called videoStreams. I have another self-made block called filterStreams. The user uses the filterStream block to add a bunch of videoStream blocks to a page, based on a filter, like “top 5 trending” or “most viewed”, etc. The data in the filterStream instances are used to create queries on the videoStream filter. Seems pretty straight forward.

Currently I am simply getting the data and outputting

so, something like this, in the view of filterStream.php

$q = "select * from btvideo_stream order by ${filterBy} desc limit ${selectCount}";
$results = $db->getArray($q);
foreach ($results as $result) {
    echo $result['title'] . "<br>";
}


What I am trying to do is (ideally) is call some function that returns an object of the particular blocktype instance(s), like the fantasy code below

foreach ($results as $result) {
$instance = BlockType::getByInstanceID(‘video_stream’,$result['bID']); 
echo $instance->getRenderedContent();
}


Basically, I already have the blocktype instances, not associated with any page… and I want to choose dynamically which instances the filterStream will show, with all the formatting, js, templates etc, etc that are part of that block, and I want to do so without having to call something like a $page->addBlock() for each query.

But I can not see how this could be done with existing C57 tools

It would be really useful to have a function that returned an array of objects that are the instances themselves for a given blockType. Does this exist and I am simply not finding it?

ntisithoj
 
Juha replied on at Permalink Reply
Juha
Untested, but you could try something like:

// Edit: Ignore this, I forgot you need to filter them first
$blockList = new BlockTypeList;
$blockList->filter( 'btHandle', 'video_stream' );
$blocks = $blockList->get();


Alternatively, if you have a list of block IDs, you should be able to instantiate them by calling (again, untested):

$block = Block::getByID( $blockID );
$block->display();
ntisithoj replied on at Permalink Reply
ntisithoj
Thanks for the reply…

The problen with the alt
$block = Block::getByID( $blockID );
$block->display();


is that this is not returning the instance of the correct block. The reason is because there is no relationship between block instances and blocks without adding it to a page.


To get the instance you need some relationship like..


SELECT 
btyoutubeplus.title, 
blocks.bName, 
blocks.bID, 
btyoutubeplus.bID 
FROM 
btyoutubeplus 
INNER JOIN blocks ON btyoutubeplus.bID = blocks.bID WHERE blocks.btID = 40


This is showing that the ONLY way to get the instance of a blocktype is if the instance is mapped to a page, because ‘blocks’ only has a record of this. In this query, 40 = the blocktype ID for my custom block… but the table has hundreds of instances… but only one that has been mapped to a page. So this query returns 1 result… but even with that result I can’t

What I am trying to do is get an object array back of all thise unlisted blocks. But I can not find any relationship between the instances of btYoutubePlus data and blocktypes (without going through blocks)

For the one block is does return, your example works great! thanks :)

it seems like the short-cut here is when my cron jobs populate my c5 table with the btYoutubeContent, I need al also so a $page->addBlock() to some dummy-page that will never actually be seen, as it will have thousands of blocks pasted to it
ntisithoj replied on at Permalink Reply
ntisithoj
I tried your idea with the following …

in the cron job I pull all the videe metadata down from youtube, populate the blocks table while at the same time adding it to an invisible page


videosResponse = $youtube->videos->listVideos('snippet,contentDetails,player,recordingDetails,statistics,status,topicDetails', array(
        'id' => $videoIds,
    ));
if (!$cms->isRunThroughCommandLineInterface()) {
        $cms->shutdown();
} else {
        $_REQUEST['auth'] = Job::generateAuth();
        $page = Page::getByPath('/beta/demo');
        $block = BlockType::getByID(40);
        foreach ($videosResponse['items'] as $videoResult) {
            print "inserting [".$videoResult['snippet']['title']."] \n";
            $data = array(
                'videoID' => $videoResult['id'],
                'title' => mysql_real_escape_string($videoResult['snippet']['title']),
                'timestamp' => strtotime($publishedAt)



then in my controller I get the ones I want…

$q = "select bID from btYoutubePlus";
$nVid = $db->GetAll($q, array());
$this->set('nVid', $nVid);


and in my view I show them

foreach($nVids as $nVid) {
        $block = Block::getByID( $nVid );
        $block->display();
}


works (more or less) like a charm…. just wish I didn’t have to have that hidden page with a bazillion blocks on it.

Thanks for the tip!
Juha replied on at Permalink Best Answer Reply
Juha
Oh, I missed the big picture in your first post but great that you got it working!

Out of curiosity, are the video stream blocks used as standalone blocks somewhere, or are they used just to render the content filtered by filter stream blocks?

Also, here is an alternative way to display blocks without having an actual block instance in the db, in case you didn't know:

$mapBlock = BlockType::getByHandle( 'google_map' );
$i = 0;
foreach ( $maps as $map ) {
   $mapBlock->controller->zoom = $map->zoom;
   $mapBlock->controller->width = $map->width;
   $mapBlock->controller->height = $map->height;
   $mapBlock->controller->bID = $c->cID + $i; // Hackity-hack
   $mapBlock->controller->latitude = $map->lat;
   $mapBlock->controller->longitude = $map->lng;
   $mapBlock->render();
   $i++;
}
ntisithoj replied on at Permalink Reply
ntisithoj
This is great! No, I did not know about this.. I am new to C5, so there is a lot I am still discovering. This looks like a much more elegant solution.

Yes, these are in a block. I have a block called ‘filterStream’ which is just a collection of filter parameters to be use in a query. The filterBlock makes the query and then populates the block with the block->display()... so to answer your question: they are stand alone blocks filled with rendered content.

Thanks!
Juha replied on at Permalink Reply
Juha
Sorry to keep poking at you, but I'm still curious about one thing:

I am probably missing something (or a lot), but to me it sounds like you are using the table for videoStream blocks more like a basic data storage, that is populated when the cron job is run. And if I understood correctly, the only thing you use the actual videoStream blocks is to programmatically render some HTML. That is, the videoStream blocks are never added to a page using the normal "Add block" mechanism.

If that's the case, wouldn't it be easier to forget the videoStream block and just do whatever you do in the videoStream's view file in the filterStream's view file, inside the foreach loop?
ntisithoj replied on at Permalink Reply
ntisithoj
Totally legit question.

Well, the BIG idea when I started this was to have a ‘could content manager’ similar to the filemanager. In the filemanager you have assets that are stored and you can do all sorts of nifty stuff with them… and it is 99% metadata. Same idea, but with 100% metadata. You would be able to place a particular ‘could asset’ (video in this case) on a page, tweak metadata, thumbnails, etc, etc.

My first attempt at this was quite beyond my abilities currently,so I opted for this alternative solution I am using now. However, I want to keep them as asset-able datatype just in case one day I do have the knowledge and the time (or better yet, the team) to build the an asset manager for ‘cloud content’. In this scenario addBlock() is necessary

But another point is, it seemed ‘logical’ to put the logic on how to render the video in a block that had it’s own view, js, css,, templates, etc.

For example, my videos have banners, events, etc, so I get to put all that code in the block space and of the videoStream, and the filterBlock can be abstracted out to be useful for other content. So I could have “show latest | show highest rated” and “videos | images | posts” etc as option in the filterStream

This was my thinking… which may not necessarily be the best approach, given my limited knowledge on C5… so, I am more than open to suggestions :)
Juha replied on at Permalink Reply
Juha
Ah, that clears things up. :)

I'm far from a C5 expert myself, so I don't have any solid suggestions.

If you're going with blocks, then most of them are pretty simple things database-wise, so even if they end up being the wrong choice for you, migration to another system should be fairly easy. That said, using them as views wouldn't probably been my first choice, but if it works for you... *shrug* You just need to mind the possible overhead they may create (database hits, for example) compared to simpler solutions.

The nice thing about C5 is that it lets you do things pretty much any way you want, which can also be a bad thing because it's not always clear if the way you chose is the "C5 way" of doing things. Also, the documentation for doing some of the more advanced things in 5.7 is currently a bit lacking, which doesn't help.

Speaking of advanced things: Because all the guides to making custom data objects for Concrete5 are outdated, I've been planning to put a (very) simple product management package I've made on GitHub, once I polish it off. Maybe it will save someone a few hours of reading through C5's source code to figure out how different pieces fit together.

Anyway, best of luck to you with your project!
ntisithoj replied on at Permalink Reply
ntisithoj
I agree with all your points... what I ended up doing was my 'second way'. The first way would be to handle 'cloud assets' like the filemanager handle files.

please let me know when you relase your product (if you have a mailing list, please add me: jeff.milton@gmail.com). Sounds like something I would def use!
Juha replied on at Permalink Reply
Juha
Yeah, I will. Don't hold your breath, though. I'm really busy at the moment, so it may take some time before I get around tweaking it again.