classes/AutoIDDatabase.php
<?php
declare(strict_types=1);
namespace Bnomei;
use Kirby\Cache\FileCache;
use Kirby\Cms\Pages;
use Kirby\Cms\Field;
use Kirby\Database\Database;
use Kirby\Database\Db;
use Kirby\Toolkit\A;
use Kirby\Toolkit\F;
final class AutoIDDatabase
{
/** @var self */
private static $singleton;
/** @var array */
private $options;
/** @var Database */
private $database;
/**
* @var int
*/
private $count;
public function __construct(array $options = [])
{
$this->options = array_merge([
'template' => realpath(__DIR__ . '/../') . '/autoid-v2-6-0.sqlite',
'target' => self::cacheFolder() . '/autoid-v2-6-0.sqlite',
], $options);
$target = $this->options['target'];
if (! F::exists($target)) {
F::copy($this->options['template'], $target);
}
$this->database = new Database([
'type' => 'sqlite',
'database' => $target,
]);
}
public function databaseFile(): string
{
return $this->options['target'];
}
public function database(): Database
{
return $this->database;
}
public function count(): int
{
if (! is_null($this->count)) {
// fastest
return $this->count;
}
// faster
$this->count = intval($this->database->query('SELECT count(*) as count FROM AUTOID')->first()->count);
return $this->count;
// slow
// return count($this->database->query('SELECT rowid FROM AUTOID'));
}
public function find($autoid): ?AutoIDItem
{
if (is_a($autoid, Field::class)) {
$autoid = (string) $autoid->value();
}
foreach ($this->database->query("SELECT * FROM AUTOID WHERE autoid = '${autoid}'") as $obj) {
return new AutoIDItem($obj);
}
return null;
}
public function findByID($objectid): ?AutoIDItem
{
if (is_a($objectid, Field::class)) {
$objectid = (string) $objectid->value();
}
list($page, $filename, $structure) = $this->pageFilenameFromPath($objectid);
foreach ($this->database->query("SELECT * FROM AUTOID WHERE page = '${page}' AND filename = '${filename}' AND structure = '${structure}'") as $obj) {
return new AutoIDItem($obj);
}
return null;
}
public function findByDiruri(string $objectid): ?AutoIDItem
{
foreach ($this->database->query("SELECT * FROM AUTOID WHERE diruri = '${objectid}'") as $obj) {
return new AutoIDItem($obj);
}
return null;
}
public function findByTemplate(string $template, string $rootId = ''): Pages
{
$rootId = ltrim($rootId, '/');
if (strlen($rootId) > 0) {
$rootId = " AND page LIKE '${rootId}%' AND page != '${rootId}'";
}
$results = new \Kirby\Cms\Pages;
$str = "SELECT * FROM AUTOID WHERE template = '${template}'" . $rootId;
foreach ($this->database->query($str) as $obj) {
$results->add((new AutoIDItem($obj))->toObject());
}
return $results;
}
public function exists($autoid): bool
{
if (is_a($autoid, Field::class)) {
$autoid = (string) $autoid->value();
}
return $this->find($autoid) !== null;
}
public function modified($autoid): ?int
{
if (is_array($autoid)) {
return $this->modifiedByArray($autoid);
}
if (is_a($autoid, Field::class)) {
$autoid = (string) $autoid->value();
}
$find = $this->find($autoid);
return $find ? $find->modified() : null;
}
public function modifiedByArray(array $autoids): ?int
{
$modified = null;
$list = implode(', ', array_map(static function ($autoid) {
return "'${autoid}'";
}, $autoids));
foreach ($this->database->query("SELECT MAX(modified) as maxmod FROM AUTOID WHERE autoid IN (${list})") as $obj) {
$modified = intval($obj->maxmod);
}
return $modified === 0 ? null : $modified;
}
public function insertOrUpdate(AutoIDItem $item): void
{
// remove all entries for item in db
$current = $this->find($item->autoid());
if ($current && $current->id() !== $item->id()) {
$this->deleteByID($current->id());
}
// remove all with same page AND file props (even if empty)
$this->deleteByID($item->id());
// remove with same autoid
$this->delete($item->autoid());
// enter a new single entry
$this->database->query("
INSERT INTO AUTOID
(autoid, modified, page, filename, structure, kind, template, diruri)
VALUES
('{$item->autoid}', {$item->modified}, '{$item->page}', '{$item->filename}', '{$item->structure}', '{$item->kind}', '{$item->template}', '{$item->diruri}')
");
$this->count = null;
}
public function delete($autoid): void
{
if (is_a($autoid, AutoIDItem::class)) {
$autoid = (string) $autoid->autoid();
} elseif (is_a($autoid, Field::class)) {
$autoid = (string) $autoid->value();
}
if (! is_string($autoid)) {
return;
}
if (strlen(trim($autoid)) === 0) {
return;
}
$this->database->query("DELETE FROM AUTOID WHERE autoid = '${autoid}'");
$this->count = null;
}
public function deleteByDiruri($objectid): void
{
if (is_a($objectid, Field::class)) {
$objectid = (string) $objectid->value();
}
$str = "DELETE FROM AUTOID WHERE diruri LIKE '${objectid}%'";
$this->database->query($str);
$this->count = null;
}
public function deleteByID($objectid): void
{
if (is_a($objectid, Field::class)) {
$objectid = (string) $objectid->value();
}
list($page, $filename, $structure) = $this->pageFilenameFromPath($objectid);
if (strlen($structure) > 0) {
// remove structure by autoid since path to object is not unique
$this->delete($structure);
} else {
$str = "DELETE FROM AUTOID WHERE page = '${page}' AND filename = '${filename}'";
$this->database->query($str);
$this->count = null;
}
}
public function flush(): void
{
$this->database->query("DELETE FROM AUTOID WHERE autoid != ''");
$this->count = null;
}
private function pageFilenameFromPath(string $objectid): array
{
$page = '';
$filename = '';
$structure = '';
if (pathinfo($objectid, PATHINFO_EXTENSION)) {
$pathinfo = pathinfo($objectid);
$page = $pathinfo['dirname'];
$filename = $pathinfo['basename'];
} else {
$pathinfo = pathinfo($objectid);
$page = $pathinfo['dirname'] === '.' ? $pathinfo['basename'] : $pathinfo['dirname'] . '/' . $pathinfo['basename'];
$structure = strpos($page, '#') !== false ? explode('#', $page)[1] : '';
}
return [$page, $filename, $structure];
}
public static function singleton(array $options = []): self
{
if (self::$singleton) {
return self::$singleton;
}
self::$singleton = new self($options);
return self::$singleton;
}
public static function cacheFolder(): string
{
$cache = kirby()->cache('bnomei.autoid');
if (is_a($cache, FileCache::class)) {
return A::get($cache->options(), 'root') . '/' . A::get($cache->options(), 'prefix');
}
// @codeCoverageIgnoreStart
return kirby()->roots()->cache();
// @codeCoverageIgnoreEnd
}
}