@halftrainedharry
No, the custom snippet get_events doesn’t set the total number of events.
I’ve looked at the code for snippet.pdopage.php and it looks like the expected parameter is $total.
But can’t figure how to set this in the get_events snippet.
I suspect the method ot figure the total recordd count is along the lines of the example in the docs
$total = $xpdo->getCount('Box',array(
But am unsure how this can be generated in my get_events
shippet.
The get_events
snippet is below, any further help would be appreeciated.
<?php
/**
*
* [[get_events? id=`4`]]
*
* TODO: add option for &parent - to filter events on resources using event_parent template. if parent is set, ignore family/month/year with if/else?
* TODO: add parameter for btn_text? some comps are 'load more matches' some 'show more events'
* TODO: querystring no longer used - if snippet call specifies &family, &month or &year it should ignore anything set in the querystring?
*
* &id - NOT REQUIRED - EVENT DETAIL SHOWN VIA TEMPLATE event_detail.tpl :: int, id of single event, not required, no default - can be set by snippet call or CustomRequest URI
*
* &family - can be int set in snippet call, or injected into the $_GET prameters by CustomConfig based on the URI path
* &parent - can be int set in snippet call, or injected into the $_GET prameters by CustomConfig based on the URI path
* &month - can be string set in snippet call, or injected into the $_GET prameters by CustomConfig based on the URI path
*
* &title - string, for heading tag, default: Upcoming Events
*
* &filter - int, show filter dropdown on output, default 0
* &experience - int, id of experience to limit event list to only events with that experience, default: null
* &max_records - int, max records, default 3
*
*
* &mLC_tpl - chunk for migxLoopCollection, default: event_card.tpl
* &mLC_tpl_wrapper - chunk for migxLoopCollection, default: event_card_wrapper.tpl
* &grid_tpl?? - to frame grid output?? default: event_grid__home.tpl
*
* &debug - int, sets debug=1 for, default 0 - flag for migxLoopCollection call
*
* NOTE: CustomConfig will work to the URI path supplied inc. part paths eg. /whats-on/football/scotland-men/ where full path
* is /whats-on/football/scotland-men/2023-11-19/scotland-v-norway/1574/
*
*/
/* CustomRequest
- available parameters
[family] => football
[parent] => scotland-men
[date] => 2023-11-19
[slug] => scotland-v-norway
[id] => 1574
- URI Format `/whats-on/family/parent/date/slug/id`
- usage eg. $_GET['slug'];
*/
// pdoPage tests - pdoPage passes its parametetrs through to get_events which is set with &element=`get_events`
if(!empty($limit)) $temp = 'limit: ' . $limit;
if(!empty($offset)) $temp = 'offset: ' . $offset;
echo '<div><h2>temp</h2>' . $temp . '</div>';
// if(!empty($temp)) return $temp;
// basic setup
// silent exit if we don't have modx
if(empty($modx) || !($modx instanceof modX)) return;
// check we have id else return error - not an error, id indicates retreive event detail instead of event list
// if (empty($id)) return '[get_events] error: &id not set';
// NOT REQUIRED yet? - get details for specific event if id is set, otherwise show list
// $id = isset($id) ? intval($id) : '';
$id = !empty($id) ? $id : $_GET['event_id'];
// if family, parent or date set in snippet call prioritise the given value and cast it to int, else use CustomRequest $_GET value
$family = isset($family) ? (int)$family : $_GET['family']; // return '$family: ' . $family;
$parent = isset($parent) ? (int)$parent : $_GET['parent'];
$date = isset($date) ? $date : $_GET['date'];
// if title is set use it, otherwise set default
$title = isset($title) ? $title : 'Upcoming Events';
// if filter set make sure it's an integer, otherwise set default
$filter = isset($filter) ? intval($filter) : 0;
// if max_records set make sure it's an integer, otherwise set default
$max_records = isset($max_records) ? intval($max_records) : 3;
$debug = isset($debug) ? intval($debug) : 0;
// deal with templates
// check if &tpl specified, else set default
// TODO: may need to configure switch for row template eg. if parent=football - can't do this in snippet, can only send one tpl to migxLoopCollection
$mLC_tpl = isset($mLC_tpl) ? $mLC_tpl : 'event_card.tpl';
// check if &tpl specified, else set default
$mlc_tpl_wrapper = isset($mlc_tpl_wrapper) ? $mlc_tpl_wrapper : 'event_card_wrapper.tpl';
// set the grid tpl
$grid_tpl = isset($grid_tpl) ? $grid_tpl : 'event_grid__home.tpl';
// configure where statement
// configure WHERE options - default statement (event must be published - requires comma to separate filter options to be added) and array for filter options
$where = '{"published":1},';
$arr_where = [];
// TODO: if we have $id or $_GET['id] task is to return the specified event?
if (isset($family) && $family == 'any') {
// family is any so do nothing here, JSON for WHERE statement does not work with wildcard as % eg. $arr_where[] = '{"family":"%"}';
} elseif (isset($family)) {
// at this point $family can be either int set in snippet call or string from CustomRequest URI
// if it's not an int lookup the family id from family slug (string) from CustomRequest URI and cast it to int
if(!is_int($family)) {
$family = $modx->runSnippet('get__object_field', array(
'class' => 'VenueFamily',
'where_key' => 'slug',
'where_value' => $family,
'field' => 'id',
));
// cast value retreived to int
$family = (int)$family;
}
// we now have $family as int
// lookup friendly family name which may be required in .tpl as +subtitle
$family_name = $modx->runSnippet('get__object_field', array(
'class' => 'VenueFamily',
'where_key' => 'id',
'where_value' => $family,
'field' => 'name',
));
// family slug is used by event_card__home.tpl to build link to experiences page
$family_slug = $modx->runSnippet('get__object_field', array(
'class' => 'VenueFamily',
'where_key' => 'id',
'where_value' => $family,
'field' => 'slug',
));
// construct JSON for WHERE statement
$arr_where[] = '{"family":"' . $family . '"}';
}
/* $parent was elseif but needs to be if to chain uri parameters into query */
if (isset($parent) && $parent == 'all') {
// parent is any so do nothing here, JSON for WHERE statement does not work with wildcard as % eg. $arr_where[] = '{"parent":"%"}';
} elseif (isset($parent)) {
// at this point $parent can be either int set in snippet call or string from CustomRequest URI
// if it's not an int lookup the family id from family slug (string) from CustomRequest URI and cast it to int
if(!is_int($parent)) {
$parent = $modx->runSnippet('get__object_field', array(
'class' => 'VenueParent',
'where_key' => 'slug',
'where_value' => $parent,
'field' => 'id',
));
// cast value retreived to int
$parent = (int)$parent;
}
// we now have $parent as int
// lookup friendly parent name which may be required in .tpl as +subtitle
$parent_name = $modx->runSnippet('get__object_field', array(
'class' => 'VenueParent',
'where_key' => 'id',
'where_value' => $parent,
'field' => 'name',
));
// construct JSON for WHERE statement
$arr_where[] = '{"parent":"' . $parent . '"}';
}
// deal with dates
// if we have $date (eg. 2023-11-23) from CustomRequest split it into year, month and day
if(isset($date)) {
// split the date string to an array
$dateParts = explode("-", $date);
// assign array elements to variables
list($year, $month, $day) = $dateParts;
// can now work with the $year, $month, $day variables as required
}
// validate month and year if they are both set and add to the where statement
if(isset($month) && isset($year)) {
// cast values to int
$month = (int)$month;
$year = (int)$year;
// get current year
$current_year = date('Y');
// check if month number is integer from 1-12 and year is the current_year year or current_year + 4
if ((is_int($month) && $month >= 1 && $month <= 12) && (is_int($year) && $year >= $current_year && $year <= $current_year + 4)) {
// find days in given month, this is required below
$days = cal_days_in_month(CAL_GREGORIAN, $month, $year);
// construct JSON for WHERE statement
// where needs start and end date - start is always 01, end is $days - notice the gte and lte operators change below
$arr_where[] = '{"date:>=":"' . $year . '-' . $month . '-01"}';
$arr_where[] = '{"date:<=":"' . $year . '-' . $month . '-' . $days .'"}';
} else {
// TODO: add error message here
// echo 'invalid date, showing all events';
}
} else {
// limit to future events with start date of >= current date
// get today's date in format YYYY-MM-DD (ref. https://www.php.net/manual/en/datetime.format.php)
$today = date('Y-m-d');
// add date limit to JSON for WHERE statement
$arr_where[] = '{"date:>=":"' . $today . '"}';
}
// construct where statement to limit events returned by to those with experience available if &experience is set
// - this is used by experience_detail.tpl to show upcoming events with this experience type
if(isset($experience)) {
// DEBUG: return 'exp: ' . $experience;
// cast value to int
$experience = intval($experience);
// need function to get concatenated rel_event_ids, then run: $c->where([ "id:IN" => array('5','6','7'), (eg. from getexperience_packages.php)
$c = $modx->newQuery('VenueExperiencePackage');
$c->select(array(
// don't need all fields // 'VenueExperiencePackage.*',
// don't need id either // 'VenueExperiencePackage.id',
'event_ids' => 'GROUP_CONCAT(DISTINCT rel_event_ids SEPARATOR "||")'
));
$c->where(array(
'rel_experience' => $experience
));
// prepare the sql statement
$c->prepare();
$sql = $c->toSQL(); // DEBUG: return '<div>' . $sql . '</div>';
// run the query
$query = $modx->query($sql);
// get the single row with concatenated event_ids
$row = $query->fetch(PDO::FETCH_ASSOC); // DEBUG return print_r($row); // eg. Array ( [event_ids] => 16||17||17||remove_this )
// explode the $row['event_ids'] to an array we can sanitize
$arr_event_ids = explode('||', $row['event_ids']); // DEBUG return print_r($arr_event_ids);
// loop through the array check the value is an integer, otherwise remove it from the array
foreach ($arr_event_ids as $key => $value) {
if (!ctype_digit((string)$value)) {
unset($arr_event_ids[$key]);
}
}
// DEBUG return print_r($arr_event_ids);
// remove duplicate values from the array
$arr_event_ids = array_unique($arr_event_ids);
// OPTIONAL
// - reindex the array to have consecutive integer keys
// $arr_event_ids = array_values($arr_event_ids);
// - sort the array in ascending order
// sort($arr_event_ids);
$event_ids = implode(",", $arr_event_ids); // return print_r($event_ids);
// finally, add the event_ids to the where statement to limit the events returned to those offering the specified experience
$arr_where[] = '{"id:IN":[' . $event_ids . ']}';
/*
if (is_int($experience)) {
// construct JSON for WHERE statement
$arr_where[] = '{"rel_experience:LIKE":"' . $experience . '||%"}'; // check if 'id||' is at the beginning
$arr_where[] = '{"OR:rel_experience:LIKE":"%||' . $experience . '||%"}'; // check if '||id||' is in the middle
$arr_where[] = '{"OR:rel_experience:LIKE":"%||' . $experience . '"}'; // check if '||id' is at the end
$arr_where[] = '{"OR:rel_experience:=":"' . $experience . '"}'; // check if 'id' is the only value
}
*/
}
// if we have a custom $arr_where append the values to the default $where statement
if (isset($arr_where) && !empty($arr_where)) {
$where .= implode(",", $arr_where);
} else {
// DEBUG
// $str_where = "No querystring";
}
// DEBUG
// echo 'str_where: ' . $str_where . '<hr>';
// echo 'where: ' . $where . '<hr>';
// create arr_output_props
$arr_output_props = [];
/**
* run migxLoop Collection to retreive the data
* - NOTE: must specify selectfields for joins otherwise the related fields are not retreived
*/
/* deal with limit and offset
- if using manual query: $c->limit($limit, $offset);
- but we're using migxLoopCollection - so check for and include &limit and &offset
*/
$mLC_output = $modx->runSnippet('migxLoopCollection',array(
'packageName' => 'venue',
'classname' => 'VenueEvent',
// retreive data
// note - comma after final brace is ok, the query still works
// 'joins' => '[{"alias":"Family"},{"alias":"Parent"},]',
'joins' => '[{"alias":"Family","selectfields":"name,img_src"},{"alias":"Parent","selectfields":"name,img_src"}]',
'where' => '['.$where.']',
'sortConfig' => '[{"sortby":"date"},"sortdir":"ASC"]',
'limit' => $limit,
'offset' => $offset,
'totalvar' => $totalVar,
// templates
'tpl' => $mLC_tpl,
// 'outputSeparator' => '<hr>',
'wrapperTpl' => $mlc_tpl_wrapper,
'debug' => $debug
));
// add the data to arr_output_props
if(!empty($mLC_output)) {
$arr_output_props['event_cards__row'] = $mLC_output;
} else {
// NOTE: pass empty array to getChunk below to avoid error - PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function modX::parseChunk()
$arr_output_props['event_cards__row'] = $modx->getChunk('filter_err__no_results.tpl', array());
}
// populate the arr_output_props used for filters
$arr_output_props['family'] = $family;
$arr_output_props['parent'] = $parent;
$arr_output_props['date'] = $date;
// if friendly names are set add them to arr_output_props
if(!empty($family_name)) {
$arr_output_props['family_name'] = $family_name;
}
if(!empty($parent_name)) {
$arr_output_props['parent_name'] = $parent_name;
}
if(!empty($family_slug)) {
$arr_output_props['family_slug'] = $family_slug;
}
if(!empty($parent_slug)) {
$arr_output_props['parent_slug'] = $parent_slug;
}
// create subtitle - prioritise date, then parent_name, else use family_name
if(!empty($date)) {
// create subtitle as Monthname YYYY
// convert $date to DateTime object $obj_date
$obj_date = DateTime::createFromFormat('Y-m-d', $date);
// check if $obj_date is a valid DateTime object
if ($obj_date instanceof DateTime) {
// set $subtitle to Monthname YYYY
$subtitle = $obj_date->format('F') . ' ' . $obj_date->format('Y');
}
} elseif(!empty($parent_name)) {
$subtitle = $parent_name;
} elseif(!empty($family_name)) {
$subtitle = $family_name;
} else {
$subtitle = '';
}
// add subtitle to arr_put_props
$arr_output_props['subtitle'] = $subtitle;
// populate the arr_output_props used for &grid_tpl wrapper
$arr_output_props['title'] = $title;
$arr_output_props['filter'] = $filter;
// generate and return the final grid output - use getChunk to allow tags to be parsed eg. for :htmlentities
$output .= $modx->getChunk($grid_tpl, $arr_output_props);
return $output;
exit;
/* *********************************************************************** */
/* *************************** OLD CODE BELOW **************************** */
/* *********************************************************************** */
/**
*
*
* the native modx way - unfinished
*
*
*
*/
// include the venue package
$base_path = $modx->getOption('core_path') . 'components/venue/';
$modx->addPackage('venue', $base_path . 'model/');
// configure the query
/*
* need condition to figure if &id set
* &where=`{"date:>":"[[!GetDate:date=`%Y-%m-%d`]]"}`
*
* if user selects a month, use it's number in the where clause as
* if $month {
*
* }
* if a user selects
*
*/
$c = $modx->newQuery('VenueEvent');
$c->where(["published" => 1]);
$c->sortby('date','ASC');
$events = $modx->getCollectionGraph('VenueEvent', $c);
// create output as array of formatted items - this could apply to multiple event but we only have one event when working from specific VenueEvent ID
$output = '';
// check events collection has content
if (count($events) === 0) {
$err_msg ='[get_events] event $id not found, not published or contains no published events';
$modx->log(modX::LOG_LEVEL_ERROR, $err_msg);
return $err_msg;
}
foreach ($events as $event) {
print_r($event); // exhausts memory
// $output .= $modx->getChunk('wrap_event.tpl', $event);
}
// implode formatted array and return as single block of formatted products
return $output;
/*
OLD NOTES
*/
// query the products
//$products = $modx->getCollectionGraph('product', '{"option":{}}', ["published" => 1, "option.published" => 1]);
// product is the class to use from the catalog scheme
// join product options table
// WHERE product.published = 1 AND option.published = 1
/*
// class
$class = 'product';
//criteria
$c = $modx->newQuery($class);
// example: $crit->bindGraph('{"modUserProfile":{"internalKey":{}}}');
$c->bindGraph('{"option":{}}', ["published" => 1, "option.published" => 1]);
$c->where('published', 1);
$c->where('option.published', 1);
$c->sortby('name','ASC');
$c->sortby('option.name','ASC');
// $products = $modx->getCollectionGraph($class, $c); // produces same output as line below
$products = $modx->getCollectionGraph($class, '{ "option":{} }', $crit);
// print_r($products);
echo 'SQL: ' . $c->toSql(); // empty
*/