I've got a block that relies on a fairly flaky third party service to get data to render so, when it does encounter a problem, I'd like to display an error message, rather than throw an exception and not render the page.
Easy enough to do until you come to block/page caching. The data has a long lifetime so, when found, it's fine to cache everything. When it isn't, though, the page is cached with the error message in place. As such, I need to tell the CMS not to save the block or page output to the cache.
Example code (within block controller):
public function view() {
try {
$this->set ('data', $this->getData());
} catch (\Exception $e) {
\Log::addError ('Blockname Error: '.$e->getMessage(), [$e]);
$this->render ('error');
}
}
Within the catch block I've tried both $this->btCacheBlockOutput = true;
and \Cache::disableAll();
but neither works. Is there a way to tell C5 not to cache anything on the current request?
The BlockController in the concrete folder has these protected variables set as standard :
protected $btCacheBlockRecord = true;
protected $btCacheBlockOutput = false;
protected $btCacheBlockOutputOnPost = false;
protected $btCacheBlockOutputForRegisteredUsers = false;
So if you set all these on your block controller.php on false, it should not cache your block.
class Controller extends BlockController
{
protected $btCacheBlockRecord = false;
protected $btCacheBlockOutput = false;
protected $btCacheBlockOutputOnPost = false;
protected $btCacheBlockOutputForRegisteredUsers = false;
public function view(){
.....
This will disable the caching of the block (even if the third party connection succeeds).
A different solution is to save the data received from the third party in the database (for example as a json string) and load the data from the database if the third party connection fails... if the third party connection succeeds, you can update the record in the database.
Depending of the answer of the third party service you can set the conditions.
Example:
//service returns on OK:
//array('status' => 'ok')
//service returns on NOT OK:
//array('status' => 'nok')
public function view() {
$data = $this->getData();
if($data['status'] == 'ok'){
//save data to database in a config value using concrete Config class
$configDatabase = \Core::make('config/database');
$configDatabase->save('thirdpartyanswer', $data);
$this->set('data', $data);
}else{
//getData function returned error or nok status
//get data from concrete Config class
$configDatabase = \Core::make('config/database');
if($configDatabase->get('thirdpartyanswer')) != ''){
//set data as "old" data from config
$this->set('data', $configDatabase->get('thirdpartyanswer'));
}else{
//no data in config -> set error and nothing else
$this->set('error', 'An error occured contacting the third party');
}
}
}
(The code above has not been tested)
More information on storing config values :
https://documentation.concrete5.org/developers/packages/storing-configuration-values
* Edit *
A third option is to purge the cache for that specific page if the call fails.
At the top of your blockcontroller :
use \Concrete\Core\Cache\Page\PageCache;
use Page;
In the 'if' when the call to the API fails :
$currentPage = Page::getCurrentPage();
$cache = PageCache::getLibrary();
$cache->purge($currentPage);
Found at : https://www.concrete5.org/community/forums/5-7-discussion/programmatically-expiring-pages-from-cache