Daniel J. Summers 610ab67475 Move domain items up to lib
- WIP on converting more complex queries
2024-05-31 22:57:24 -04:00

148 lines
5.7 KiB
PHP

<?php
namespace FeedReaderCentral;
use BitBadger\Documents\DocumentException;
use BitBadger\Documents\SQLite\Configuration;
use BitBadger\Documents\SQLite\Custom;
use BitBadger\Documents\SQLite\Definition;
use BitBadger\Documents\StringMapper;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;
use SQLite3;
/**
* A centralized place for data access for the application
*/
class Data {
/**
* Obtain a new connection to the database
* @return SQLite3 A new connection to the database
*/
public static function getConnection(): SQLite3 {
$db = new SQLite3(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'data', DATABASE_NAME]));
$db->exec('PRAGMA foreign_keys = ON;');
return $db;
}
/**
* Create the search index and synchronization triggers for the item table
*
* @param SQLite3 $db The database connection on which these will be created
* @throws DocumentException If any is encountered
*/
public static function createSearchIndex(SQLite3 $db): void
{
Custom::nonQuery("CREATE VIRTUAL TABLE item_search USING fts5(content, content='item', content_rowid='id')",
[], $db);
Custom::nonQuery(<<<'SQL'
CREATE TRIGGER item_ai AFTER INSERT ON item BEGIN
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
END;
SQL, [], $db);
Custom::nonQuery(<<<'SQL'
CREATE TRIGGER item_au AFTER UPDATE ON item BEGIN
INSERT INTO item_search (
item_search, rowid, content
) VALUES (
'delete', old.data->>'id', old.data->>'content'
);
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
END;
SQL, [], $db);
Custom::nonQuery(<<<'SQL'
CREATE TRIGGER item_ad AFTER DELETE ON item BEGIN
INSERT INTO item_search (
item_search, rowid, content
) VALUES (
'delete', old.data->>'id', old.data->>'content'
);
END;
SQL, [], $db);
}
/**
* Make sure the expected tables exist
*
* @throws DocumentException If any is encountered
*/
public static function ensureDb(): void
{
$tables = Custom::array("SELECT name FROM sqlite_master WHERE type = 'table'", [], new StringMapper('name'));
$db = Configuration::dbConn();
// $tables = array();
// $tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type = 'table'");
// while ($table = $tableQuery->fetchArray(SQLITE3_NUM)) $tables[] = $table[0];
if (!in_array(Table::USER, $tables)) {
Definition::ensureTable(Table::USER, $db);
Definition::ensureFieldIndex(Table::USER, 'email', ['email'], $db);
// $db->exec(<<<'SQL'
// CREATE TABLE frc_user (
// id INTEGER NOT NULL PRIMARY KEY,
// email TEXT NOT NULL,
// password TEXT NOT NULL)
// SQL);
// $db->exec('CREATE INDEX idx_user_email ON frc_user (email)');
}
if (!in_array(Table::FEED, $tables)) {
Definition::ensureTable(Table::FEED, $db);
Definition::ensureFieldIndex(Table::FEED, 'user', ['user_id'], $db);
// $db->exec(<<<'SQL'
// CREATE TABLE feed (
// id INTEGER NOT NULL PRIMARY KEY,
// user_id INTEGER NOT NULL,
// url TEXT NOT NULL,
// title TEXT,
// updated_on TEXT,
// checked_on TEXT,
// FOREIGN KEY (user_id) REFERENCES frc_user (id))
// SQL);
}
if (!in_array(Table::ITEM, $tables)) {
Definition::ensureTable(Table::ITEM, $db);
Definition::ensureFieldIndex(Table::ITEM, 'feed', ['feed_id', 'item_link'], $db);
// $db->exec(<<<'SQL'
// CREATE TABLE item (
// id INTEGER NOT NULL PRIMARY KEY,
// feed_id INTEGER NOT NULL,
// title TEXT NOT NULL,
// item_guid TEXT NOT NULL,
// item_link TEXT NOT NULL,
// published_on TEXT NOT NULL,
// updated_on TEXT,
// content TEXT NOT NULL,
// is_read BOOLEAN NOT NULL DEFAULT 0,
// is_bookmarked BOOLEAN NOT NULL DEFAULT 0,
// FOREIGN KEY (feed_id) REFERENCES feed (id))
// SQL);
self::createSearchIndex($db);
}
$db->close();
}
/**
* Parse/format a date/time from a string
*
* @param ?string $value The date/time to be parsed and formatted
* @return string|null The date/time in `DateTimeInterface::ATOM` format, or `null` if the input cannot be parsed
*/
public static function formatDate(?string $value): ?string {
try {
return $value ? (new DateTimeImmutable($value))->format(DateTimeInterface::ATOM) : null;
} catch (Exception) {
return null;
}
}
/**
* Return the last SQLite error message as a result array
*
* @param SQLite3 $db The database connection on which the error has occurred
* @return string[] ['error' => message] for last SQLite error message
*/
public static function error(SQLite3 $db): array {
return ['error' => 'SQLite error: ' . $db->lastErrorMsg()];
}
}