ORIENT/modules/cms/classes/AutoDatasource.php

262 lines
7.7 KiB
PHP

<?php namespace Cms\Classes;
use Cache;
use Exception;
use October\Rain\Halcyon\Processors\Processor;
use October\Rain\Halcyon\Datasource\Datasource;
use October\Rain\Halcyon\Exception\DeleteFileException;
use October\Rain\Halcyon\Datasource\DatasourceInterface;
/**
* Datasource that loads from other data sources automatically
*/
class AutoDatasource extends Datasource implements DatasourceInterface
{
/**
* @var array The available datasource instances
*/
protected $datasources = [];
/**
* @var array Local cache of paths available in the datasources
*/
protected $pathCache = [];
/**
* Create a new datasource instance.
*
* @param array $datasources Array of datasources to utilize. Lower indexes = higher priority
* @return void
*/
public function __construct($datasources)
{
$this->datasources = $datasources;
$this->populateCache();
$this->postProcessor = new Processor;
}
/**
* Populate the local cache of paths available in each datasource
*
* @param boolean $refresh Default false, set to true to force the cache to be rebuilt
* @return void
*/
protected function populateCache($refresh = false)
{
$pathCache = [];
foreach ($this->datasources as $datasource) {
// Remove any existing cache data
if ($refresh) {
Cache::forget($datasource->getPathsCacheKey);
}
// Load the cache
$pathCache[] = Cache::rememberForever($datasource->getPathsCacheKey(), function () use ($datasource) {
return $datasource->getAvailablePaths();
});
}
$this->pathCache = $pathCache;
}
/**
* Returns the datasource instances being used internally
*
* @return array
*/
public function getDatasources()
{
return $this->datasources;
}
/**
* Get the appropriate datasource for the provided path
*
* @param string $path
* @return Datasource
*/
protected function getDatasourceForPath($path)
{
// Default to the last datasource provided
$datasourceIndex = count($this->datasources) - 1;
foreach ($this->pathCache as $i => $paths) {
if (in_array($path, $paths)) {
$datasourceIndex = $i;
}
}
return $this->datasources[$datasourceIndex];
}
/**
* Helper to make file path.
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @return string
*/
protected function makeFilePath($dirName, $fileName, $extension)
{
return $dirName . '/' . $fileName . '.' . $extension;
}
/**
* Returns a single template.
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @return mixed
*/
public function selectOne($dirName, $fileName, $extension)
{
return $this->getDatasourceForPath($this->makeFilePath($dirName, $fileName, $extension))->selectOne($dirName, $fileName, $extension);
}
/**
* Returns all templates.
*
* @param string $dirName
* @param array $options Array of options, [
* 'columns' => ['fileName', 'mtime', 'content'], // Only return specific columns
* 'extensions' => ['htm', 'md', 'twig'], // Extensions to search for
* 'fileMatch' => '*gr[ae]y', // Shell matching pattern to match the filename against using the fnmatch function
* 'orders' => false // Not implemented
* 'limit' => false // Not implemented
* 'offset' => false // Not implemented
* ];
* @return array
*/
public function select($dirName, array $options = [])
{
// Initialize result set
$sourceResults = [];
foreach ($this->datasources as $datasource) {
$sourceResults = array_merge($sourceResults, $datasource->select($dirName, $options));
}
// Remove duplicate results prioritizing results from earlier datasources
// Reverse the order of the source results so that keyBy prioritizes
// earlier results rather than later results
$results = array_values(collect(array_reverse($sourceResults))->keyBy('fileName')->all());
return $results;
}
/**
* Creates a new template.
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @param string $content
* @return bool
*/
public function insert($dirName, $fileName, $extension, $content)
{
// @TODO: Implement this
}
/**
* Updates an existing template.
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @param string $content
* @param string $oldFileName Defaults to null
* @param string $oldExtension Defaults to null
* @return int
*/
public function update($dirName, $fileName, $extension, $content, $oldFileName = null, $oldExtension = null)
{
// @TODO: Implement this
}
/**
* Run a delete statement against the datasource.
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @return int
*/
public function delete($dirName, $fileName, $extension)
{
// Initial implementation, forces delete on every datasource
$exceptionCount = 0;
try {
foreach ($this->datasources as $datasource) {
$datasource->delete($dirName, $fileName, $extension);
}
// Refresh the cache
$this->populateCache(true);
}
catch (Exception $ex) {
// Only throw exception if content couldn't be removed from any datasource
$exceptionCount++;
if ($exceptionCount >= count($this->datasources)) {
throw (new DeleteFileException)->setInvalidPath($this->makeFilePath($dirName, $fileName, $extension));
}
}
}
/**
* Return the last modified date of an object
*
* @param string $dirName
* @param string $fileName
* @param string $extension
* @return int
*/
public function lastModified($dirName, $fileName, $extension)
{
return $this->getDatasourceForPath($this->makeFilePath($dirName, $fileName, $extension))->lastModified($dirName, $fileName, $extension);
}
/**
* Generate a cache key unique to this datasource.
*
* @param string $name
* @return string
*/
public function makeCacheKey($name = '')
{
$key = '';
foreach ($this->datasources as $datasource) {
$key .= $datasource->makeCacheKey($name) . '-';
}
$key .= $name;
return crc32($key);
}
/**
* Generate a paths cache key unique to this datasource
*
* @return string
*/
public function getPathsCacheKey()
{
return 'halcyon-datastore-auto';
}
/**
* Get all available paths within this datastore
*
* @return array $paths ['path/to/file1.md', 'path/to/file2.md']
*/
public function getAvailablePaths()
{
$paths = [];
foreach ($this->datasources as $datasource) {
$paths = array_merge($paths, $datasource->getAvailablePaths());
}
return array_unique($paths);
}
}