Data-loading

The data-loading takes place at the block level: each block knows if it needs to fetch any data and, if so, from where, what parameters to add to filter the results, and what “dataloader” object will do the fetching (see below). All corresponding methods are implemented by extending from class GD_Template_Processor_BlocksBase:

class GD_Template_Processor_SectionBlocks extends GD_Template_Processor_BlocksBase {

	function get_dataload_source($template_id, $atts) {

		switch ($template_id) {

			case GD_TEMPLATE_BLOCK_AUTHORHIGHLIGHTS_SCROLL_LIST:

				global $gd_template_settingsmanager, $author;
				$url = get_author_posts_url($author);
				$page_id = $gd_template_settingsmanager->get_block_page($template_id, GD_SETTINGS_HIERARCHY_AUTHOR);
				$ret = GD_TemplateManager_Utils::add_tab($url, $page_id);
				return add_query_arg(GD_URLPARAM_FORMAT, GD_TEMPLATEFORMAT_LIST, $ret);
		}
	
		return parent::get_dataload_source($template_id, $atts);
	}

	protected function get_dataload_query_args($template_id, $atts) {

		$ret = parent::get_dataload_query_args($template_id, $atts);
		
		switch ($template_id) {

			case GD_TEMPLATE_BLOCK_AUTHORHIGHLIGHTS_SCROLL_LIST:

				global $author;
				$ret['author'] = $author;
				$ret['cat'] = 55;
				break;
		}

		return $ret;
	}

	function get_dataloader($template_id) {

		switch ($template_id) {

			case GD_TEMPLATE_BLOCK_AUTHORHIGHLIGHTS_SCROLL_LIST:
			
				return GD_DATALOADER_POSTLIST;
		}

		return parent::get_dataloader($template_id);
	}

	// ...
}
new GD_Template_Processor_SectionBlocks();

(Source) A block defining from where to fetch its data, what args to add to the query, and what type of object it is

A dataloader is an object extending from class GD_DataLoader, which knows how to fetch the data from the database, and allows the block to set the args in the query to filter the results.

define ('GD_DATALOADER_POSTLIST', 'post-list');

class GD_DataLoader_PostList extends GD_DataLoader_PostListBase {

	function get_name() {
    
		return GD_DATALOADER_POSTLIST;
	}
	
	function get_query($vars = array()) {
	
		$query = parent::get_query($vars);

		if ($cat = $vars['cat']) $query['cat'] = $cat;
		if ($author = $vars['author']) $query['author'] = $author;

		$query['orderby'] = isset($vars['orderby']) ? $vars['orderby'] : 'date';
		$query['order'] = isset($vars['order']) ? $vars['order'] : 'DESC';

		return $query;
	}
	
	function execute_query($query) {

		return get_posts($query);
	}
}
new GD_DataLoader_PostList();

(Source) A dataloader object, which fetches the data from the database

The block can also specify from what domain to fetch its data and, even better, this attribute can be injected externally, so the block is completely unaware from where it is fetching its data:

class GD_Template_Processor_SectionBlocks extends GD_Template_Processor_BlocksBase {

	function get_dataloadsource_domain($template_id, $atts) {

		switch ($template_id) {
			
			case GD_TEMPLATE_BLOCK_AUTHORHIGHLIGHTS_SCROLL_LIST:
				
				return 'https://demo.getpop.org';
		}

		return parent::get_dataloadsource_domain($template_id, $atts);
	}

	// ...
}

class GetPoP_Template_Processor_CustomBlockGroups extends GD_Template_Processor_ListBlockGroupsBase {

	function init_atts_blockgroup_block($blockgroup, $blockgroup_block, &$blockgroup_block_atts, $blockgroup_atts) {

		if ($blockgroup == GD_TEMPLATE_BLOCKGROUP_AUTHORHIGHLIGHTS_SCROLL && $blockgroup_block == GD_TEMPLATE_BLOCK_AUTHORHIGHLIGHTS_SCROLL_LIST) {

			$this->add_att($blockgroup_block, $blockgroup_block_atts, 'dataloadsource-domain', 'https://demo.getpop.org');
		}

		return parent::init_atts_blockgroup_block($blockgroup, $blockgroup_block, $blockgroup_block_atts, $blockgroup_atts);
	}

	// ...
}

(Above) A block defining from what domain to fetch data; (Below) Dependency-injection of the same attribute

Additionally, a block must also define what variables of input/output are needed, traveling back and forth between the front-end and the back-end. These variables help keep the block state, eg: for doing pagination. The required variables are configured in an object called “iohandler”, extending from class GD_DataLoad_IOHandler:

class GD_EM_Template_Processor_SectionBlocks extends GD_Template_Processor_SectionBlocksBase {

	protected function get_iohandler($template_id) {

		switch ($template_id) {

			case GD_TEMPLATE_BLOCK_EVENTSCALENDAR_CALENDAR:
			
				return GD_DATALOAD_IOHANDLER_CALENDAR;
		}
		
		return parent::get_iohandler($template_id);
	}
}
new GD_EM_Template_Processor_SectionBlocks();

(Source) The block defines its input/output handler object

define ('GD_DATALOAD_IOHANDLER_CALENDAR', 'calendar');
class GD_DataLoad_IOHandler_Calendar extends GD_DataLoad_IOHandler_List {

	function get_name() {
    
		return GD_DATALOAD_IOHANDLER_CALENDAR;
	}

	function get_vars($atts, $iohandler_atts) {
    
		$ret = parent::get_vars($atts, $iohandler_atts);
		
		$today = time();
		$year = $atts[GD_URLPARAM_YEAR] ? $atts[GD_URLPARAM_YEAR] : date('Y', $today);
		$month = $atts[GD_URLPARAM_MONTH] ? $atts[GD_URLPARAM_MONTH] : date('n', $today);

		$ret[GD_URLPARAM_YEAR] = $year;
		$ret[GD_URLPARAM_MONTH] = $month;

		$from = date('Y-m-01', strtotime($year.'-'.$month.'-01'));
		$to = date('Y-m-t', strtotime($from));
		$ret['scope'] = array($from, $to);

		return $ret;
	}	
	
	function get_params($checkpoint, $dataset, $vars_atts, $iohandler_atts, $executed = null, $atts) {
	
		$ret = parent::get_params($checkpoint, $dataset, $vars_atts, $iohandler_atts, $executed, $atts);

		$vars = $this->get_vars($vars_atts, $iohandler_atts);		
		$ret[GD_DATALOAD_VISIBLEPARAMS][GD_URLPARAM_YEAR] = $vars[GD_URLPARAM_YEAR];
		$ret[GD_DATALOAD_VISIBLEPARAMS][GD_URLPARAM_MONTH] = $vars[GD_URLPARAM_MONTH];

		return $ret;
	}
}
new GD_DataLoad_IOHandler_Calendar();

(Source) An input/output handler object, obtaining required variables from the request, and setting them again on the response back to the front-end

Even though the block defines what objects will be loaded, of what type, and how to filter them, it is up to all of its inner modules to indicate what data-fields they will need from the queried object. The implementation on retrieving the object’s data, given the specified data-field, is done in an object called “fieldProcessor”.

class GD_Template_Processor_PreviewUserLayoutsBase extends GD_Template_Processor_PreviewObjectLayoutsBase {

	function get_data_fields($template_id, $atts) {

		return array('name', 'url', 'description');
	}
}

(Source) A module requiring certain data fields from the queried object

{{#with itemObject}}
	<div class="layout user-layout preview {{../class}}" {{#generateId context=../.}}{{../id}}{{/generateId}}>
		<a href="{{url}}" title="{{{name}}}">{{{name}}}</a>
		<p class="description {{../../classes.short-description}}">{{{description}}}</p>
	</div>
{{/with}}

(Source) Javascript template to display the queried object properties

define ('GD_DATALOAD_FIELDPROCESSOR_USERS', 'users');
 
class GD_DataLoad_FieldProcessor_Users extends GD_DataLoad_FieldProcessor {

	function get_name() {
	
		return GD_DATALOAD_FIELDPROCESSOR_USERS;
	}
	
	function get_value($resultitem, $field) {
	
		$user = $resultitem;
		switch ($field) {
		
			case 'name' :				
				$value = esc_attr($user->display_name);
				break;

			case 'email' :
				$value = $user->user_email;
				break;
		
			case 'url' :
				$value = get_author_posts_url($user->ID);
				break;

			case 'description' :				
				$value = $user->description;
				break;

			// ...																							
		}

		return $value;
	}
}
new GD_DataLoad_FieldProcessor_Users();

(Source) A fieldProcessor to fetch users’ data, which knows how to load the object data for each data-field

It is possible to change the object domain, and start loading data from a different object type. For instance, let’s say that we start loading a list of posts and, for each, we want to show an associated location’s information (eg: its address and coordinates). For this, we must switch from the object domain ‘posts’ to the object domain ‘locations’ to fetch the corresponding data. A module can do exactly that: specify to load an inner-module using a different dataLoader to process it; this other dataLoader will then be used, starting from that inner-module, all the way down the line, or until changing the object domain once again. (This can be done unlimited times).

class GD_Template_Processor_MapScriptsBase extends GD_Template_ProcessorBase {

	function get_subcomponent_modules($template_id) {
	
		return array(
			'locations' => array(
				'modules' => array(GD_TEMPLATE_MAP_SCRIPT_MARKERS),
				'dataloader' => GD_DATALOADER_LOCATIONLIST
			)
		);
	}

	// ...
}

(Source) Changing the object domain, by specifying a different dataLoader to be used for all ids stored under the object field ‘locations’

The PoP engine will obtain, from all blocks and subsequent inner modules, which are all the required fields and under what object domains, query only those required fields from the database, and send back the corresponding values to the front-end and nothing else. It will recreate the data as it is from the database into the front-end, keeping the relationships among all different object domains; it fetches each object only once from the database, and sends it back only once to the front-end (eg: if two different posts share the same author, the author’s data will be fetched and sent just once).

The results are sent to the front-end in the produced JSON code: under key ‘database’ is the database object, containing the data for all objects queried in the request; under key ‘dataset’ are the ids of the result objects, for each block; and under key ‘settings’=>’db-keys’ is the top-most object domain (“posts”, “users”, “locations”, etc) for each block.

json

Viewing the response’s JSON code in Chrome’s Console. Pay attention how under key ‘database’ the object data is relational, and objects from different domains have also been fetched: the post’s author (with id 851) and the post’s location (with id 23494); and under key ‘dataset’ it indicates the result object ids for each block (posts with ids 23787, 23784, 23644 for the main block)


Welcome to the PoP framework!
Break the information monopoly

the PoP framework is open source software which aims to decentralize the content flow and break the information monopoly from large internet corporations. Read more.


Sign up to our newsletter: