ModX Revolution Caching Dynamicly Generated Placeholders

468 views Asked by At

How can I cache (using ModX's cacheManager), the dynamic placeholders i am generating here:

// recursive function to generate our un-ordered list of menu items
if(!function_exists('GenerateMenu')){
    function GenerateMenu($level, $parent = 0, $wc, $wi, $we, $cs, $sc, $cl){
        try {
            $lvl = ++$level;
            global $modx;
            // Check for #1, should this be cached, #2 does it already exist in the cache
            $cached = $modx->cacheManager->get('Navigator');
            if($sc && isset($cached)){
                // need to get the placeholders from cache - here somehow!
                return $cached;
            }else{
                // get the site start
                $siteStartId = $modx->getOption('site_start');
                // Set our initial rows array
                $rows = array();
                // Run our query to get our menu items
                $sql = 'Select `id`, `menutitle`, `uri`, `longtitle`, `parent`, `link_attributes`, `class_key`, `content`, `alias`, `introtext`
                                    From `' . $modx->getOption(xPDO::OPT_TABLE_PREFIX) . 'site_content` 
                                    Where `deleted` = 0 AND `hidemenu` = 0 AND `published` = 1 AND `parent` = :parent
                                    Order by `parent`, `menuindex`';
                $query = new xPDOCriteria($modx, $sql, array(':parent' => $parent));
                if ($query->stmt && $query->stmt->execute()) {
                    $rows = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
                }
                // do some cleanup
                unset($query, $sql);
                // make sure we have some rows, and then build our html for the menu
                if($rows){
                    // grab a count of our results
                    $rCt = count($rows);
                    $cls = ($lvl > 1) ? 'sub-item-' . $lvl : 'main-item-' . $lvl;
                    $ret .= '   <ul class="' . $cls . '" id="' . $cls . '-' . $parent . '">' . "\r\n";
                    for($i = 0; $i < $rCt; ++$i){
                        // if this resource is a WebLink, show the content in it, as the URL for the href tag, otherwise, use the resource's URI
                        $url = ($rows[$i]['class_key'] == 'modWebLink') ? $rows[$i]['content'] : '/' . $rows[$i]['uri'];
                        // Check for the site's start id, if true, show a "blank" link, otherwise show the $url
                        $showUrl = ($siteStartId == $rows[$i]['id']) ? '/' : $url;
                        $la = (strlen($rows[$i]['link_attributes']) > 0) ? ' ' . $rows[$i]['link_attributes'] : null;
                        // Set some dynamic placeholders, they can only be used ont he pages that contain this snippet
                        $modx->toPlaceholders(array('Title-' . $rows[$i]['id'] => $rows[$i]['longtitle'],
                                                    'MenuTitle-' . $rows[$i]['id'] => $rows[$i]['menutitle'],
                                                    'URL-' . $rows[$i]['id'] => $showUrl), 
                                                'link');
                        $ret .= '       <li class="' . $cls . '" id="' . $rows[$i]['alias'] . '">' . "\r\n";
                        $ret .= '           <a href="' . $showUrl . '" title="' . $rows[$i]['longtitle'] . '"' . $la . '>' . $rows[$i]['menutitle'] . '</a>' . "\r\n";
                        $ret .= GenerateMenu($lvl, $rows[$i]['id']);
                        // Check for a snippet, and render it
                        $it = $rows[$i]['introtext'];
                        if($cs && IsSnippet($it)){
                            // if we find a snippet in the Summary field, run it, and attach it to our output   
                            preg_match('/\[\[!?(.*)\]\]/', $it, $sm);
                            $ret .= $modx->runSnippet($sm[1]);
                            // clean up
                            unset($sm);
                        }
                        $ret .= '       </li>' . "\r\n";
                    }
                    $ret .= '   </ul>' . "\r\n";
                }
                // clean up
                unset($rows);           
                // Check to see if we should cache it, if so, set it to cache, and apply the length of time it should be cached for: defaults to 2 hours
                if($sc){
                    // NEED TO SET THE PLACEHOLDERS TO CACHE SOMEHOW
                    $modx->cacheManager->set('Navigator', $ret, $cl);
                }
                // return the menu
                return $ret;
            }
        } catch(Exception $e) {
            // If there was an error, make sure to write it out to the modX Error Log
            $modx->log(modX::LOG_LEVEL_ERROR, '[Navigator] Error: ' . $e->getMessage());
            return null;
        }
    }
}
2

There are 2 answers

0
W. Shawn Wilkerson On BEST ANSWER

The easiest solution may be pdoTools which allows you to establish caching at run time.

http://www.shawnwilkerson.com/modx/tags/pdotools/

Also, I do not believe resource placeholders are cached which is the best place to have your items cahced:

            case '+':
                $tagName= substr($tagName, 1 + $tokenOffset);
                $element= new modPlaceholderTag($this->modx);
                $element->set('name', $tagName);
                $element->setTag($outerTag);
                $elementOutput= $element->process($tagPropString);
                break;

From lines 455-461 of https://github.com/modxcms/revolution/blob/master/core/model/modx/modparser.class.php#L455

You may notice the other tag types have:

$element->setCacheable($cacheable);

I cover the parser in Appendix D of my book. I found some issues in it in 2011 which Jason corrected.

1
okyanet On

Move toPlaceholders() to the end of your script and instead cache the array of placeholder data:

// attempt to retrieve placeholders from cache
$placeholders = $modx->cacheManager->get('Navigator');

// if not in cache, run your snippet logic and generate the data
if (empty($placeholders))) {
    $placeholders = array(
        'myplaceholder' => 'placeholder data'
    );
    $modx->cacheManager->set('Navigator', $placeholders, $cl);
}

// set placeholders for use by MODX
$modx->toPlaceholders($placeholders);