Remove direct PDO handling
This commit is contained in:
parent
3a31aca467
commit
efa69f2c1c
7
src/composer.lock
generated
7
src/composer.lock
generated
|
@ -12,7 +12,7 @@
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.bitbadger.solutions/bit-badger/pdo-document",
|
"url": "https://git.bitbadger.solutions/bit-badger/pdo-document",
|
||||||
"reference": "bcca9f5ace2ba32c24412e284ff4789480d9619d"
|
"reference": "2d8f8b6e8728f8fbe0d801e2bc529a678f65ad9b"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
|
@ -32,10 +32,11 @@
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Test\\Unit\\": "./tests/unit",
|
"Test\\Unit\\": "./tests/unit",
|
||||||
"Test\\Integration\\": "./tests/integration"
|
"Test\\Integration\\": "./tests/integration",
|
||||||
|
"Test\\Integration\\SQLite\\": "./tests/integration/sqlite"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"time": "2024-06-05T02:36:58+00:00"
|
"time": "2024-06-08T14:49:52+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "netresearch/jsonmapper",
|
"name": "netresearch/jsonmapper",
|
||||||
|
|
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
namespace FeedReaderCentral;
|
namespace FeedReaderCentral;
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{Configuration, Custom, Definition, DocumentException, Field};
|
||||||
use BitBadger\PDODocument\Custom;
|
|
||||||
use BitBadger\PDODocument\Definition;
|
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\Field;
|
|
||||||
use BitBadger\PDODocument\Mapper\StringMapper;
|
use BitBadger\PDODocument\Mapper\StringMapper;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use PDO;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A centralized place for data access for the application
|
* A centralized place for data access for the application
|
||||||
|
@ -21,18 +16,17 @@ class Data
|
||||||
/**
|
/**
|
||||||
* Create the search index and synchronization triggers for the item table
|
* Create the search index and synchronization triggers for the item table
|
||||||
*
|
*
|
||||||
* @param PDO $pdo The database connection on which these will be created
|
|
||||||
* @throws DocumentException If any is encountered
|
* @throws DocumentException If any is encountered
|
||||||
*/
|
*/
|
||||||
public static function createSearchIndex(PDO $pdo): void
|
public static function createSearchIndex(): void
|
||||||
{
|
{
|
||||||
Custom::nonQuery("CREATE VIRTUAL TABLE item_search USING fts5(content, content='item', content_rowid='id')",
|
Custom::nonQuery("CREATE VIRTUAL TABLE item_search USING fts5(content, content='item', content_rowid='id')",
|
||||||
[], $pdo);
|
[]);
|
||||||
Custom::nonQuery(<<<'SQL'
|
Custom::nonQuery(<<<'SQL'
|
||||||
CREATE TRIGGER item_ai AFTER INSERT ON item BEGIN
|
CREATE TRIGGER item_ai AFTER INSERT ON item BEGIN
|
||||||
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
|
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
|
||||||
END;
|
END;
|
||||||
SQL, [], $pdo);
|
SQL, []);
|
||||||
Custom::nonQuery(<<<'SQL'
|
Custom::nonQuery(<<<'SQL'
|
||||||
CREATE TRIGGER item_au AFTER UPDATE ON item BEGIN
|
CREATE TRIGGER item_au AFTER UPDATE ON item BEGIN
|
||||||
INSERT INTO item_search (
|
INSERT INTO item_search (
|
||||||
|
@ -42,7 +36,7 @@ class Data
|
||||||
);
|
);
|
||||||
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
|
INSERT INTO item_search (rowid, content) VALUES (new.data->>'id', new.data->>'content');
|
||||||
END;
|
END;
|
||||||
SQL, [], $pdo);
|
SQL, []);
|
||||||
Custom::nonQuery(<<<'SQL'
|
Custom::nonQuery(<<<'SQL'
|
||||||
CREATE TRIGGER item_ad AFTER DELETE ON item BEGIN
|
CREATE TRIGGER item_ad AFTER DELETE ON item BEGIN
|
||||||
INSERT INTO item_search (
|
INSERT INTO item_search (
|
||||||
|
@ -51,7 +45,7 @@ class Data
|
||||||
'delete', old.data->>'id', old.data->>'content'
|
'delete', old.data->>'id', old.data->>'content'
|
||||||
);
|
);
|
||||||
END;
|
END;
|
||||||
SQL, [], $pdo);
|
SQL, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,19 +56,18 @@ class Data
|
||||||
public static function ensureDb(): void
|
public static function ensureDb(): void
|
||||||
{
|
{
|
||||||
$tables = Custom::array("SELECT name FROM sqlite_master WHERE type = 'table'", [], new StringMapper('name'));
|
$tables = Custom::array("SELECT name FROM sqlite_master WHERE type = 'table'", [], new StringMapper('name'));
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
if (!in_array(Table::USER, $tables)) {
|
if (!in_array(Table::USER, $tables)) {
|
||||||
Definition::ensureTable(Table::USER, $pdo);
|
Definition::ensureTable(Table::USER);
|
||||||
Definition::ensureFieldIndex(Table::USER, 'email', ['email'], $pdo);
|
Definition::ensureFieldIndex(Table::USER, 'email', ['email']);
|
||||||
}
|
}
|
||||||
if (!in_array(Table::FEED, $tables)) {
|
if (!in_array(Table::FEED, $tables)) {
|
||||||
Definition::ensureTable(Table::FEED, $pdo);
|
Definition::ensureTable(Table::FEED);
|
||||||
Definition::ensureFieldIndex(Table::FEED, 'user', ['user_id'], $pdo);
|
Definition::ensureFieldIndex(Table::FEED, 'user', ['user_id']);
|
||||||
}
|
}
|
||||||
if (!in_array(Table::ITEM, $tables)) {
|
if (!in_array(Table::ITEM, $tables)) {
|
||||||
Definition::ensureTable(Table::ITEM, $pdo);
|
Definition::ensureTable(Table::ITEM);
|
||||||
Definition::ensureFieldIndex(Table::ITEM, 'feed', ['feed_id', 'item_link'], $pdo);
|
Definition::ensureFieldIndex(Table::ITEM, 'feed', ['feed_id', 'item_link']);
|
||||||
self::createSearchIndex($pdo);
|
self::createSearchIndex();
|
||||||
}
|
}
|
||||||
$journalMode = Custom::scalar("PRAGMA journal_mode", [], new StringMapper('journal_mode'));
|
$journalMode = Custom::scalar("PRAGMA journal_mode", [], new StringMapper('journal_mode'));
|
||||||
if ($journalMode <> 'wal') Custom::nonQuery("PRAGMA journal_mode = 'wal'", []);
|
if ($journalMode <> 'wal') Custom::nonQuery("PRAGMA journal_mode = 'wal'", []);
|
||||||
|
|
|
@ -2,19 +2,10 @@
|
||||||
|
|
||||||
namespace FeedReaderCentral;
|
namespace FeedReaderCentral;
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{
|
||||||
use BitBadger\PDODocument\Custom;
|
Configuration, Custom, Document, DocumentException, DocumentList, Exists, Field, Find, Parameters, Patch, Query
|
||||||
use BitBadger\PDODocument\Document;
|
};
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\DocumentList;
|
|
||||||
use BitBadger\PDODocument\Exists;
|
|
||||||
use BitBadger\PDODocument\Field;
|
|
||||||
use BitBadger\PDODocument\Find;
|
|
||||||
use BitBadger\PDODocument\Parameters;
|
|
||||||
use BitBadger\PDODocument\Patch;
|
|
||||||
use BitBadger\PDODocument\Query;
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use PDO;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An RSS or Atom feed
|
* An RSS or Atom feed
|
||||||
|
@ -75,23 +66,22 @@ class Feed
|
||||||
* @param int $feedId The ID of the feed to which these items belong
|
* @param int $feedId The ID of the feed to which these items belong
|
||||||
* @param ParsedFeed $parsed The extracted Atom or RSS feed items
|
* @param ParsedFeed $parsed The extracted Atom or RSS feed items
|
||||||
* @param DateTimeInterface $lastChecked When this feed was last checked (only new items will be added)
|
* @param DateTimeInterface $lastChecked When this feed was last checked (only new items will be added)
|
||||||
* @param PDO $pdo The database connection over which items should be updated
|
|
||||||
* @return array ['ok' => true] if successful, ['error' => message] if not
|
* @return array ['ok' => true] if successful, ['error' => message] if not
|
||||||
*/
|
*/
|
||||||
public static function updateItems(int $feedId, ParsedFeed $parsed, DateTimeInterface $lastChecked, PDO $pdo): array
|
public static function updateItems(int $feedId, ParsedFeed $parsed, DateTimeInterface $lastChecked): array
|
||||||
{
|
{
|
||||||
$results =
|
$results =
|
||||||
array_map(function ($item) use ($pdo, $feedId) {
|
array_map(function ($item) use ($feedId) {
|
||||||
try {
|
try {
|
||||||
$existing = Find::firstByFields(Table::ITEM,
|
$existing = Find::firstByFields(Table::ITEM,
|
||||||
[Field::EQ('feed_id', $feedId), Field::EQ('item_guid', $item->guid)], Item::class);
|
[Field::EQ('feed_id', $feedId), Field::EQ('item_guid', $item->guid)], Item::class);
|
||||||
if ($existing) {
|
if ($existing) {
|
||||||
if ($existing->published_on != $item->publishedOn
|
if ($existing->published_on != $item->publishedOn
|
||||||
|| ($existing->updated_on != ($item->updatedOn ?? ''))) {
|
|| ($existing->updated_on != ($item->updatedOn ?? ''))) {
|
||||||
Patch::byId(Table::ITEM, $existing->id, $item->patchFields(), $pdo);
|
Patch::byId(Table::ITEM, $existing->id, $item->patchFields());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Document::insert(Table::ITEM, Item::fromFeedItem($feedId, $item), $pdo);
|
Document::insert(Table::ITEM, Item::fromFeedItem($feedId, $item));
|
||||||
}
|
}
|
||||||
return ['ok' => true];
|
return ['ok' => true];
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
|
@ -107,10 +97,9 @@ class Feed
|
||||||
* Purge items for a feed
|
* Purge items for a feed
|
||||||
*
|
*
|
||||||
* @param int $feedId The ID of the feed to be purged
|
* @param int $feedId The ID of the feed to be purged
|
||||||
* @param PDO $pdo The database connection on which items should be purged
|
|
||||||
* @return array|string[]|true[] ['ok' => true] if purging was successful, ['error' => message] if not
|
* @return array|string[]|true[] ['ok' => true] if purging was successful, ['error' => message] if not
|
||||||
*/
|
*/
|
||||||
private static function purgeItems(int $feedId, PDO $pdo): array
|
private static function purgeItems(int $feedId): array
|
||||||
{
|
{
|
||||||
if (!array_search(PURGE_TYPE, [self::PURGE_READ, self::PURGE_BY_DAYS, self::PURGE_BY_COUNT])) {
|
if (!array_search(PURGE_TYPE, [self::PURGE_READ, self::PURGE_BY_DAYS, self::PURGE_BY_COUNT])) {
|
||||||
return ['error' => 'Unrecognized purge type ' . PURGE_TYPE];
|
return ['error' => 'Unrecognized purge type ' . PURGE_TYPE];
|
||||||
|
@ -139,7 +128,7 @@ class Feed
|
||||||
SQL;
|
SQL;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Custom::nonQuery($sql, Parameters::addFields($fields, []), $pdo);
|
Custom::nonQuery($sql, Parameters::addFields($fields, []));
|
||||||
return ['ok' => true];
|
return ['ok' => true];
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
return ['error' => "$ex"];
|
return ['error' => "$ex"];
|
||||||
|
@ -151,10 +140,9 @@ class Feed
|
||||||
*
|
*
|
||||||
* @param int $feedId The ID of the feed to be refreshed
|
* @param int $feedId The ID of the feed to be refreshed
|
||||||
* @param string $url The URL of the feed to be refreshed
|
* @param string $url The URL of the feed to be refreshed
|
||||||
* @param PDO $pdo A database connection to use to refresh the feed
|
|
||||||
* @return array|string[]|true[] ['ok' => true] if successful, ['error' => message] if not
|
* @return array|string[]|true[] ['ok' => true] if successful, ['error' => message] if not
|
||||||
*/
|
*/
|
||||||
public static function refreshFeed(int $feedId, string $url, PDO $pdo): array
|
public static function refreshFeed(int $feedId, string $url): array
|
||||||
{
|
{
|
||||||
$feedRetrieval = ParsedFeed::retrieve($url);
|
$feedRetrieval = ParsedFeed::retrieve($url);
|
||||||
if (key_exists('error', $feedRetrieval)) return $feedRetrieval;
|
if (key_exists('error', $feedRetrieval)) return $feedRetrieval;
|
||||||
|
@ -165,7 +153,7 @@ class Feed
|
||||||
if (!$feedDoc) return ['error' => 'Could not derive date last checked for feed'];
|
if (!$feedDoc) return ['error' => 'Could not derive date last checked for feed'];
|
||||||
$lastChecked = date_create_immutable($feedDoc->checked_on ?? WWW_EPOCH);
|
$lastChecked = date_create_immutable($feedDoc->checked_on ?? WWW_EPOCH);
|
||||||
|
|
||||||
$itemUpdate = self::updateItems($feedId, $feed, $lastChecked, $pdo);
|
$itemUpdate = self::updateItems($feedId, $feed, $lastChecked);
|
||||||
if (key_exists('error', $itemUpdate)) return $itemUpdate;
|
if (key_exists('error', $itemUpdate)) return $itemUpdate;
|
||||||
|
|
||||||
$patch = [
|
$patch = [
|
||||||
|
@ -174,12 +162,12 @@ class Feed
|
||||||
'checked_on' => Data::formatDate('now')
|
'checked_on' => Data::formatDate('now')
|
||||||
];
|
];
|
||||||
if ($url == $feed->url) $patch['url'] = $feed->url;
|
if ($url == $feed->url) $patch['url'] = $feed->url;
|
||||||
Patch::byId(Table::FEED, $feedId, $patch, $pdo);
|
Patch::byId(Table::FEED, $feedId, $patch);
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
return ['error' => "$ex"];
|
return ['error' => "$ex"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return PURGE_TYPE == self::PURGE_NONE ? ['ok' => true] : self::purgeItems($feedId, $pdo);
|
return PURGE_TYPE == self::PURGE_NONE ? ['ok' => true] : self::purgeItems($feedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -196,18 +184,17 @@ class Feed
|
||||||
$feed = $feedExtract['ok'];
|
$feed = $feedExtract['ok'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
$fields = [Field::EQ('user_id', $_SESSION[Key::USER_ID]), Field::EQ('url', $feed->url)];
|
$fields = [Field::EQ('user_id', $_SESSION[Key::USER_ID]), Field::EQ('url', $feed->url)];
|
||||||
if (Exists::byFields(Table::FEED, $fields, $pdo)) {
|
if (Exists::byFields(Table::FEED, $fields)) {
|
||||||
return ['error' => "Already subscribed to feed $feed->url"];
|
return ['error' => "Already subscribed to feed $feed->url"];
|
||||||
}
|
}
|
||||||
|
|
||||||
Document::insert(Table::FEED, self::fromParsed($feed), $pdo);
|
Document::insert(Table::FEED, self::fromParsed($feed));
|
||||||
|
|
||||||
$doc = Find::firstByFields(Table::FEED, $fields, static::class);
|
$doc = Find::firstByFields(Table::FEED, $fields, static::class);
|
||||||
if (!$doc) return ['error' => 'Could not retrieve inserted feed'];
|
if (!$doc) return ['error' => 'Could not retrieve inserted feed'];
|
||||||
|
|
||||||
$result = self::updateItems($doc->id, $feed, date_create_immutable(WWW_EPOCH), $pdo);
|
$result = self::updateItems($doc->id, $feed, date_create_immutable(WWW_EPOCH));
|
||||||
if (key_exists('error', $result)) return $result;
|
if (key_exists('error', $result)) return $result;
|
||||||
|
|
||||||
return ['ok' => $doc->id];
|
return ['ok' => $doc->id];
|
||||||
|
@ -226,12 +213,11 @@ class Feed
|
||||||
public static function update(Feed $existing, string $url): array
|
public static function update(Feed $existing, string $url): array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
Patch::byFields(Table::FEED,
|
Patch::byFields(Table::FEED,
|
||||||
[Field::EQ(Configuration::$idField, $existing->id), Field::EQ('user_id', $_SESSION[Key::USER_ID])],
|
[Field::EQ(Configuration::$idField, $existing->id), Field::EQ('user_id', $_SESSION[Key::USER_ID])],
|
||||||
['url' => $url], $pdo);
|
['url' => $url]);
|
||||||
|
|
||||||
return self::refreshFeed($existing->id, $url, $pdo);
|
return self::refreshFeed($existing->id, $url);
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
return ['error' => "$ex"];
|
return ['error' => "$ex"];
|
||||||
}
|
}
|
||||||
|
@ -262,10 +248,9 @@ class Feed
|
||||||
try {
|
try {
|
||||||
$feeds = self::retrieveAll($_SESSION[Key::USER_ID]);
|
$feeds = self::retrieveAll($_SESSION[Key::USER_ID]);
|
||||||
|
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
foreach ($feeds->items() as $feed) {
|
foreach ($feeds->items() as $feed) {
|
||||||
$result = self::refreshFeed($feed->id, $feed->url, $pdo);
|
$result = self::refreshFeed($feed->id, $feed->url);
|
||||||
if (key_exists('error', $result)) $errors[] = $result['error'];
|
if (key_exists('error', $result)) $errors[] = $result['error'];
|
||||||
}
|
}
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
|
@ -284,7 +269,9 @@ class Feed
|
||||||
*/
|
*/
|
||||||
public static function retrieveById(int $feedId): static|false
|
public static function retrieveById(int $feedId): static|false
|
||||||
{
|
{
|
||||||
|
define('PDO_DOC_DEBUG_SQL', true);
|
||||||
$doc = Find::byId(Table::FEED, $feedId, static::class);
|
$doc = Find::byId(Table::FEED, $feedId, static::class);
|
||||||
|
echo "Feed $feedId: " . ($doc ? 'found it' : 'not found');
|
||||||
return $doc && $doc->user_id == $_SESSION[Key::USER_ID] ? $doc : false;
|
return $doc && $doc->user_id == $_SESSION[Key::USER_ID] ? $doc : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,7 @@
|
||||||
|
|
||||||
namespace FeedReaderCentral;
|
namespace FeedReaderCentral;
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{Configuration, Custom, DocumentException, DocumentList, Field, Parameters, Query};
|
||||||
use BitBadger\PDODocument\Custom;
|
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\DocumentList;
|
|
||||||
use BitBadger\PDODocument\Field;
|
|
||||||
use BitBadger\PDODocument\Parameters;
|
|
||||||
use BitBadger\PDODocument\Query;
|
|
||||||
use BitBadger\PDODocument\Mapper\DocumentMapper;
|
use BitBadger\PDODocument\Mapper\DocumentMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,10 +151,9 @@ class ItemList
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$return = $this->returnURL == '' ? '' : '&from=' . urlencode($this->returnURL);
|
$return = $this->returnURL == '' ? '' : '&from=' . urlencode($this->returnURL);
|
||||||
$hasItems = false;
|
|
||||||
echo '<article>';
|
echo '<article>';
|
||||||
|
if ($this->dbList->hasItems()) {
|
||||||
foreach ($this->dbList->items() as $it) {
|
foreach ($this->dbList->items() as $it) {
|
||||||
$hasItems = true;
|
|
||||||
echo '<p>' . hx_get("/item?id=$it->id$return", strip_tags($it->title)) . '<br><small>';
|
echo '<p>' . hx_get("/item?id=$it->id$return", strip_tags($it->title)) . '<br><small>';
|
||||||
if ($this->showIndicators) {
|
if ($this->showIndicators) {
|
||||||
if (!$it->isRead()) echo '<strong>Unread</strong> ';
|
if (!$it->isRead()) echo '<strong>Unread</strong> ';
|
||||||
|
@ -176,7 +169,7 @@ class ItemList
|
||||||
}
|
}
|
||||||
echo '</small>';
|
echo '</small>';
|
||||||
}
|
}
|
||||||
if (!$hasItems) {
|
} else {
|
||||||
echo '<p><em>There are no ' . strtolower($this->itemType) . ' items</em>';
|
echo '<p><em>There are no ' . strtolower($this->itemType) . ' items</em>';
|
||||||
}
|
}
|
||||||
echo '</article>';
|
echo '</article>';
|
||||||
|
|
|
@ -2,14 +2,8 @@
|
||||||
|
|
||||||
namespace FeedReaderCentral;
|
namespace FeedReaderCentral;
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{Configuration, Custom, DocumentException, Field, Parameters, Query};
|
||||||
use BitBadger\PDODocument\Custom;
|
use BitBadger\PDODocument\Mapper\{DocumentMapper, ExistsMapper};
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\Field;
|
|
||||||
use BitBadger\PDODocument\Parameters;
|
|
||||||
use BitBadger\PDODocument\Query;
|
|
||||||
use BitBadger\PDODocument\Mapper\DocumentMapper;
|
|
||||||
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A combined item and feed (used for lists)
|
* A combined item and feed (used for lists)
|
||||||
|
|
|
@ -2,14 +2,8 @@
|
||||||
|
|
||||||
namespace FeedReaderCentral;
|
namespace FeedReaderCentral;
|
||||||
|
|
||||||
use BitBadger\PDODocument\Custom;
|
use BitBadger\PDODocument\{Custom, Document, DocumentException, Field, Find, Parameters, Query};
|
||||||
use BitBadger\PDODocument\Document;
|
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\Field;
|
|
||||||
use BitBadger\PDODocument\Find;
|
|
||||||
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
||||||
use BitBadger\PDODocument\Parameters;
|
|
||||||
use BitBadger\PDODocument\Query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user of Feed Reader Central
|
* A user of Feed Reader Central
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{Custom, Document, DocumentException};
|
||||||
use BitBadger\PDODocument\Custom;
|
use BitBadger\PDODocument\Mapper\{ArrayMapper, ExistsMapper};
|
||||||
use BitBadger\PDODocument\Document;
|
use FeedReaderCentral\{Data, Feed, Item, Table, User};
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\Mapper\ArrayMapper;
|
|
||||||
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
|
||||||
use FeedReaderCentral\Data;
|
|
||||||
use FeedReaderCentral\Feed;
|
|
||||||
use FeedReaderCentral\Item;
|
|
||||||
use FeedReaderCentral\Table;
|
|
||||||
use FeedReaderCentral\User;
|
|
||||||
|
|
||||||
require __DIR__ . '/../cli-start.php';
|
require __DIR__ . '/../cli-start.php';
|
||||||
|
|
||||||
|
@ -68,56 +60,49 @@ function check_status(): void
|
||||||
|
|
||||||
function run_update(): void
|
function run_update(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
} catch (DocumentException $ex) {
|
|
||||||
printfn("ERR: Cannot obtain a connection to the database\n $ex");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$searchExists = Custom::scalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = 'item_ai')", [],
|
$searchExists = Custom::scalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = 'item_ai')", [],
|
||||||
new ExistsMapper());
|
new ExistsMapper());
|
||||||
if ($searchExists) {
|
if ($searchExists) {
|
||||||
printfn('Removing search index...');
|
printfn('Removing search index...');
|
||||||
Custom::nonQuery('DROP TRIGGER item_ai', [], $pdo);
|
Custom::nonQuery('DROP TRIGGER item_ai', []);
|
||||||
Custom::nonQuery('DROP TRIGGER item_au', [], $pdo);
|
Custom::nonQuery('DROP TRIGGER item_au', []);
|
||||||
Custom::nonQuery('DROP TRIGGER item_ad', [], $pdo);
|
Custom::nonQuery('DROP TRIGGER item_ad', []);
|
||||||
Custom::nonQuery('DROP TABLE item_search', [], $pdo);
|
Custom::nonQuery('DROP TABLE item_search', []);
|
||||||
}
|
}
|
||||||
printfn('Moving old tables...');
|
printfn('Moving old tables...');
|
||||||
Custom::nonQuery('ALTER TABLE item RENAME TO old_item', [], $pdo);
|
Custom::nonQuery('ALTER TABLE item RENAME TO old_item', []);
|
||||||
Custom::nonQuery('ALTER TABLE feed RENAME TO old_feed', [], $pdo);
|
Custom::nonQuery('ALTER TABLE feed RENAME TO old_feed', []);
|
||||||
Custom::nonQuery('ALTER TABLE frc_user RENAME TO old_user', [], $pdo);
|
Custom::nonQuery('ALTER TABLE frc_user RENAME TO old_user', []);
|
||||||
printfn('Creating new tables...');
|
printfn('Creating new tables...');
|
||||||
Data::ensureDb();
|
Data::ensureDb();
|
||||||
printfn('Migrating users...');
|
printfn('Migrating users...');
|
||||||
$users = $pdo->query('SELECT * FROM old_user');
|
$users = Custom::list('SELECT * FROM old_user', [], new ArrayMapper());
|
||||||
if (!$users) throw new DocumentException('Could not retrieve users');
|
if (!$users->hasItems()) throw new DocumentException('Could not retrieve users');
|
||||||
while ($user = $users->fetch(PDO::FETCH_ASSOC)) {
|
foreach ($users->items() as $user) {
|
||||||
Document::insert(Table::USER, new User($user['id'], $user['email'], $user['password']), $pdo);
|
Document::insert(Table::USER, new User($user['id'], $user['email'], $user['password']));
|
||||||
}
|
}
|
||||||
printfn('Migrating feeds...');
|
printfn('Migrating feeds...');
|
||||||
$feeds = $pdo->query('SELECT * FROM old_feed');
|
$feeds = Custom::list('SELECT * FROM old_feed', [], new ArrayMapper());
|
||||||
if (!$feeds) throw new DocumentException('Could not retrieve feeds');
|
if (!$feeds->hasItems()) throw new DocumentException('Could not retrieve feeds');
|
||||||
while ($feed = $feeds->fetch(PDO::FETCH_ASSOC)) {
|
foreach ($feeds->items() as $feed) {
|
||||||
Document::insert(Table::FEED,
|
Document::insert(Table::FEED,
|
||||||
new Feed($feed['id'], $feed['user_id'], $feed['url'], $feed['title'], $feed['updated_on'],
|
new Feed($feed['id'], $feed['user_id'], $feed['url'], $feed['title'], $feed['updated_on'],
|
||||||
$feed['checked_on']), $pdo);
|
$feed['checked_on']));
|
||||||
}
|
}
|
||||||
printfn('Migrating items...');
|
printfn('Migrating items...');
|
||||||
$items = $pdo->query('SELECT * FROM old_item');
|
$items = Custom::list('SELECT * FROM old_item', [], new ArrayMapper());
|
||||||
if (!$items) throw new DocumentException('Could not retrieve items');
|
if (!$items->hasItems()) throw new DocumentException('Could not retrieve items');
|
||||||
while ($item = $items->fetch(PDO::FETCH_ASSOC)) {
|
foreach ($items->items() as $item) {
|
||||||
Document::insert(Table::ITEM,
|
Document::insert(Table::ITEM,
|
||||||
new Item($item['id'], $item['feed_id'], $item['title'], $item['item_guid'], $item['item_link'],
|
new Item($item['id'], $item['feed_id'], $item['title'], $item['item_guid'], $item['item_link'],
|
||||||
$item['published_on'], $item['updated_on'], $item['content'], $item['is_read'],
|
$item['published_on'], $item['updated_on'], $item['content'], $item['is_read'],
|
||||||
$item['is_bookmarked']), $pdo);
|
$item['is_bookmarked']));
|
||||||
}
|
}
|
||||||
printfn('Dropping old tables...');
|
printfn('Dropping old tables...');
|
||||||
Custom::nonQuery('DROP TABLE old_item', [], $pdo);
|
Custom::nonQuery('DROP TABLE old_item', []);
|
||||||
Custom::nonQuery('DROP TABLE old_feed', [], $pdo);
|
Custom::nonQuery('DROP TABLE old_feed', []);
|
||||||
Custom::nonQuery('DROP TABLE old_user', [], $pdo);
|
Custom::nonQuery('DROP TABLE old_user', []);
|
||||||
printfn(PHP_EOL. 'Migration complete!');
|
printfn(PHP_EOL. 'Migration complete!');
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
printfn("ERR $ex");
|
printfn("ERR $ex");
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{DocumentException, Find};
|
||||||
use BitBadger\PDODocument\DocumentException;
|
use FeedReaderCentral\{Feed, Table, User};
|
||||||
use BitBadger\PDODocument\Find;
|
|
||||||
use FeedReaderCentral\Feed;
|
|
||||||
use FeedReaderCentral\Table;
|
|
||||||
use FeedReaderCentral\User;
|
|
||||||
|
|
||||||
require __DIR__ . '/../cli-start.php';
|
require __DIR__ . '/../cli-start.php';
|
||||||
|
|
||||||
|
@ -36,17 +32,10 @@ function display_help(): never
|
||||||
|
|
||||||
function refresh_all(): void
|
function refresh_all(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$pdo = Configuration::dbConn();
|
|
||||||
} catch (DocumentException $ex) {
|
|
||||||
printfn("ERR: Cannot obtain a connection to the database\n $ex");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$users = [];
|
$users = [];
|
||||||
iterator_apply(Feed::retrieveAll()->items(), function (Feed $feed) use ($pdo, &$users) {
|
iterator_apply(Feed::retrieveAll()->items(), function (Feed $feed) use (&$users) {
|
||||||
$result = Feed::refreshFeed($feed->id, $feed->url, $pdo);
|
$result = Feed::refreshFeed($feed->id, $feed->url);
|
||||||
$userKey = "$feed->user_id";
|
$userKey = "$feed->user_id";
|
||||||
if (!key_exists($userKey, $users)) $users[$userKey] = Find::byId(Table::USER, $feed->user_id, User::class);
|
if (!key_exists($userKey, $users)) $users[$userKey] = Find::byId(Table::USER, $feed->user_id, User::class);
|
||||||
if (array_key_exists('error', $result)) {
|
if (array_key_exists('error', $result)) {
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
use BitBadger\PDODocument\{Custom, DocumentException};
|
||||||
use BitBadger\PDODocument\Custom;
|
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
||||||
use BitBadger\PDODocument\DocumentException;
|
|
||||||
use BitBadger\PDODocument\Mapper\CountMapper;
|
|
||||||
use FeedReaderCentral\Data;
|
use FeedReaderCentral\Data;
|
||||||
|
|
||||||
require __DIR__ . '/../cli-start.php';
|
require __DIR__ . '/../cli-start.php';
|
||||||
|
@ -39,21 +37,14 @@ function display_help(): never
|
||||||
function rebuild_index(): void
|
function rebuild_index(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdo = Configuration::dbConn();
|
$hasIndex = Custom::scalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = 'item_ai')", [],
|
||||||
} catch (DocumentException $ex) {
|
new ExistsMapper());
|
||||||
printfn("ERR: Cannot obtain a connection to the database\n $ex");
|
if (!$hasIndex) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$hasIndex = Custom::scalar("SELECT COUNT(*) FROM sqlite_master WHERE name = 'item_ai'", [], new CountMapper(),
|
|
||||||
$pdo);
|
|
||||||
if ($hasIndex == 0) {
|
|
||||||
printfn('Creating search index....');
|
printfn('Creating search index....');
|
||||||
Data::createSearchIndex($pdo);
|
Data::createSearchIndex();
|
||||||
}
|
}
|
||||||
printfn('Rebuilding search index...');
|
printfn('Rebuilding search index...');
|
||||||
Custom::nonQuery("INSERT INTO item_search (item_search) VALUES ('rebuild')", [], $pdo);
|
Custom::nonQuery("INSERT INTO item_search (item_search) VALUES ('rebuild')", []);
|
||||||
printfn(PHP_EOL . 'Search index rebuilt');
|
printfn(PHP_EOL . 'Search index rebuilt');
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
printfn("$ex");
|
printfn("$ex");
|
||||||
|
|
|
@ -1,17 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use BitBadger\PDODocument\DocumentException;
|
use BitBadger\PDODocument\{Count, Custom, Delete, DocumentException, Field, Parameters, Patch, Query};
|
||||||
use BitBadger\PDODocument\Field;
|
use FeedReaderCentral\{Security, Table, User};
|
||||||
use BitBadger\PDODocument\Query;
|
|
||||||
use BitBadger\PDODocument\Configuration;
|
|
||||||
use BitBadger\PDODocument\Count;
|
|
||||||
use BitBadger\PDODocument\Custom;
|
|
||||||
use BitBadger\PDODocument\Delete;
|
|
||||||
use BitBadger\PDODocument\Parameters;
|
|
||||||
use BitBadger\PDODocument\Patch;
|
|
||||||
use FeedReaderCentral\Security;
|
|
||||||
use FeedReaderCentral\Table;
|
|
||||||
use FeedReaderCentral\User;
|
|
||||||
|
|
||||||
require __DIR__ . '/../cli-start.php';
|
require __DIR__ . '/../cli-start.php';
|
||||||
|
|
||||||
|
@ -158,13 +148,6 @@ function set_password(string $email, string $password): void
|
||||||
*/
|
*/
|
||||||
function delete_user(string $email): void
|
function delete_user(string $email): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$db = Configuration::dbConn();
|
|
||||||
} catch (DocumentException $ex) {
|
|
||||||
printfn("ERR: Cannot obtain a connection to the database\n $ex");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$displayUser = display_user($email);
|
$displayUser = display_user($email);
|
||||||
|
|
||||||
|
@ -176,7 +159,7 @@ function delete_user(string $email): void
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$feedCount = Count::byFields(Table::FEED, [Field::EQ('user_id', $user->id)], $db);
|
$feedCount = Count::byFields(Table::FEED, [Field::EQ('user_id', $user->id)]);
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
printfn("$ex");
|
printfn("$ex");
|
||||||
return;
|
return;
|
||||||
|
@ -192,9 +175,9 @@ function delete_user(string $email): void
|
||||||
$fields = [Field::EQ('user_id', $user->id, '@user')];
|
$fields = [Field::EQ('user_id', $user->id, '@user')];
|
||||||
Custom::nonQuery(
|
Custom::nonQuery(
|
||||||
'DELETE FROM ' . Table::ITEM . " WHERE data->>'feed_id' IN (SELECT data->>'id' FROM " . Table::FEED
|
'DELETE FROM ' . Table::ITEM . " WHERE data->>'feed_id' IN (SELECT data->>'id' FROM " . Table::FEED
|
||||||
. ' WHERE ' . Query::whereByFields($fields) . ')', Parameters::addFields($fields, []), $db);
|
. ' WHERE ' . Query::whereByFields($fields) . ')', Parameters::addFields($fields, []));
|
||||||
Delete::byFields(Table::FEED, $fields, $db);
|
Delete::byFields(Table::FEED, $fields);
|
||||||
Delete::byId(Table::USER, $user->id, $db);
|
Delete::byId(Table::USER, $user->id);
|
||||||
|
|
||||||
printfn('%s deleted successfully', init_cap($displayUser));
|
printfn('%s deleted successfully', init_cap($displayUser));
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
|
@ -202,8 +185,6 @@ function delete_user(string $email): void
|
||||||
}
|
}
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
printfn("$ex");
|
printfn("$ex");
|
||||||
} finally {
|
|
||||||
$db->close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,13 +195,6 @@ function migrate_single_user(): void
|
||||||
{
|
{
|
||||||
global $argv;
|
global $argv;
|
||||||
|
|
||||||
try {
|
|
||||||
$db = Configuration::dbConn();
|
|
||||||
} catch (DocumentException $ex) {
|
|
||||||
printfn("ERR: Cannot obtain a connection to the database\n $ex");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!$single = User::findByEmail(Security::SINGLE_USER_EMAIL)) {
|
if (!$single = User::findByEmail(Security::SINGLE_USER_EMAIL)) {
|
||||||
printfn('There is no single-user mode user to be migrated');
|
printfn('There is no single-user mode user to be migrated');
|
||||||
|
@ -228,12 +202,10 @@ function migrate_single_user(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
Patch::byId(Table::USER, $single->id,
|
Patch::byId(Table::USER, $single->id,
|
||||||
['email' => $argv[2], 'password' => password_hash($argv[3], Security::PW_ALGORITHM)], $db);
|
['email' => $argv[2], 'password' => password_hash($argv[3], Security::PW_ALGORITHM)]);
|
||||||
|
|
||||||
printfn('The single user has been moved to "%s", with password "%s"', $argv[2], $argv[3]);
|
printfn('The single user has been moved to "%s", with password "%s"', $argv[2], $argv[3]);
|
||||||
} catch (DocumentException $ex) {
|
} catch (DocumentException $ex) {
|
||||||
printfn("$ex");
|
printfn("$ex");
|
||||||
} finally {
|
|
||||||
$db->close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user