Advanced Block Development: Part Four

Wrapping Up

Andrew starts by discussing some details about cache lifetime and then segues into...

Blocks and Databases

You can find the db.xml that Andrew is running through in concrete/blocks/survey/db.xml. If you look in the survey block's db.xml, you can see how most blocks are structured.

The file has a basic definition of a block, which should be familiar if you've built a block before:

<table name="btSurvey">
    <field name="bID" type="I">
        <key ></key>
        <unsigned ></unsigned>
    </field>
    <field name="question" type="C" size="255">
        <default value=""></default>
    </field>
    <field name="requiresRegistration" type="I">
        <default value="0"></default>
    </field>
</table>

Since this is a more complicated block, it also includes some tables to store the auxilary data, like the btSurveyOptions table, which has its own primary key, but also the bID of the original block it is stored on in order to establish a relation of the two tables.

<table name="btSurveyOptions">
    <field name="optionID" type="I">
        <key></key>
        <unsigned></unsigned>
        <autoincrement></autoincrement>
    </field>
    <field name="bID" type="I" ></field>    
    <field name="optionName" type="C" size="255" ></field>
    <field name="displayOrder" type="I">
        <default value="0"></default>
    </field>
</table>

If you check out the block's controller, you can see how the database queries are built. Concrete5 uses the ADODB database library, but it does not have a full ORM / CRUD system.So you will need to write database queries in order to retrieve or update data.

Here is a link to the ADODB manual. Here is a simple select statement from hasVoted in the survey block's controller:

        $db = Loader::db();
        $v = array($u->getUserID(), $this->bID, $this->cID);
        $q = "select count(resultID) as total from btSurveyResults where uID = ? and bID = ? AND cID = ?";
        $result = $db->getOne($q,$v);

It uses the loader to get a connection to the database, sets an array of data from the object, assigns a query to a string, then gets the first result from the database that matches that query. In this case the database should only have one result for this query. The main thing to notice here is the order of the items in the array, and then the order of the column = ? pairs in the query string. ADODB will use the query string, then apply the values in the array to the question marks, so be sure that your array is in the right order on more elaborate queries or you will have data problems.

Typically you want to use the ADODB library's Get... functions for SELECT stuff and Execute() for INSERT or UPDATE.

Blocks and Actions

As Andrew points out, the main difference between block actions and page actions is the way the function is named in the controller. So for instance posting to $this->action('action') would call function action_action() in a block controller and not function action() as it would in a page controller.

Delete and Duplicate

A block will clean its own table up, but for the same reasons relational data doesn't get created automagically, you need to write some queries. So for example, if the survey block only had btSurvey to look after, it would clean itself up. But since it has supporting data, this needs to be cleaned up first. You can check out delete in the survey block controller. In general, you delete any rows from your supporting data "where bID = the bID of this block", then run parent::delete() to get rid of the row from your block's table.

The same thing goes for duplicate, except your queries will be more elaborate. Like Andrew says: don't forget this, or copied blocks and pages featuring this block will be a mess.

"Not Documented"

Andrew mentions that you can set $btIncludeAll = true and the block will not version. This basically defeats one of the key features of concrete5, but it might have its place.

Blocks Outside of View Context

It is possible to use Loader::block('block_handle') to load the controller for a block, which doesn't actually load a block, but it does make the class of your block available. This is particularly useful if your block controller has some static methods and you can then do things like BlockHandleBlockController::someFunction($argument)

Conclusion

Andrew wraps up by reviewing some concepts from basic block development and looks up this example about using ajax. He also mentions the "Mobile Theme Switcher" add-on. This is part of the core as of 5.6.

Hopefully this has provided you with some insight on how to create more interesting, interactive blocks within concrete5.


Recent Discussions on this Topic

Custom Block View Tutorial

Attached is a white paper on creating custom block views. I am finding many C5 users do not understand this concept and even hesitate to modify purchased addons. I hope this is helpful. Please PM me if you have any additions or corrections and I …

Building a block with library image input?

I'm trying to build a block that has the ability to pull an image from the library (or, since I'm desperate, from anywhere, even a remote URL), but I can't figure it out, and I'm new to PHP... Does anyone have any pointers? I tried reading http://phple…

Check it out: custom block creator!

*** This package is now available on the marketplace: http://www.concrete5.org/marketplace/addons/designer-content/ *** I've made a "custom block creator" which aims to solve the problem of allowing you (as the site designer) to specify exactly how som…

Adding smarter DISQUS to your concrete5

Guys, I've just finished figuring out a smarter way to implement Disqus, a third-party commenting system Sometime, you don't bother users to register on your site, but comment using Facebook, twitter or other social ID. Disqus is a fine comment s…

Check if block is empty

Hi guys, I need some help with this: I need to check if a block is empty (essentially the 'C' field). If the 'C' (content?) field is empty I want serve a conditional accordingly. I have tried the following unsuccessfully; [code] $block = Block::getBy…