WIP on adding feed items (#4)

This commit is contained in:
Daniel J. Summers 2024-04-09 23:14:53 -04:00
parent 5c92c8c7d6
commit 0530ed0dc9
3 changed files with 86 additions and 9 deletions

View File

@ -51,12 +51,14 @@ class Data {
id INTEGER NOT NULL PRIMARY KEY, id INTEGER NOT NULL PRIMARY KEY,
feed_id INTEGER NOT NULL, feed_id INTEGER NOT NULL,
title TEXT NOT NULL, title TEXT NOT NULL,
item_guid TEXT NOT NULL,
item_link TEXT NOT NULL,
published_on TEXT NOT NULL, published_on TEXT NOT NULL,
updated_on TEXT, updated_on TEXT,
content TEXT NOT NULL, content TEXT NOT NULL,
is_encoded BOOLEAN NOT NULL, is_encoded BOOLEAN NOT NULL,
is_read BOOLEAN NOT NULL, is_read BOOLEAN NOT NULL DEFAULT 0,
is_bookmarked BOOLEAN NOT NULL, is_bookmarked BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY (feed_id) REFERENCES feed (id)) FOREIGN KEY (feed_id) REFERENCES feed (id))
SQL; SQL;
$db->exec($query); $db->exec($query);
@ -109,7 +111,7 @@ class Data {
if ($updatedOn) { if ($updatedOn) {
try { try {
$updated = (new DateTimeImmutable($updatedOn))->format(DateTimeInterface::ATOM); $updated = (new DateTimeImmutable($updatedOn))->format(DateTimeInterface::ATOM);
} catch (Exception $ex) { } catch (Exception) {
$updated = null; $updated = null;
} }
} else { } else {
@ -123,11 +125,48 @@ class Data {
$query->bindValue(':updated', $updated); $query->bindValue(':updated', $updated);
$query->bindValue(':checked', (new DateTimeImmutable())->format(DateTimeInterface::ATOM)); $query->bindValue(':checked', (new DateTimeImmutable())->format(DateTimeInterface::ATOM));
$result = $query->execute(); $result = $query->execute();
if ($result) { return $result ? $db->lastInsertRowID() : -1;
$idQuery = $db->prepare('SELECT last_insert_rowid()'); }
$idResult = $idQuery->execute();
if ($idResult) return $idResult->fetchArray(SQLITE3_NUM)[0]; /**
} * Does a feed item already exist?
return -1; * @param int $feedId The ID of the feed to which the item belongs
* @param string $guid The GUID from the RSS feed, uniquely identifying the item
* @return bool True if the item exists, false if not
*/
public static function itemExists(int $feedId, string $guid): bool {
$db = self::getConnection();
$query = $db->prepare('SELECT COUNT(*) FROM item WHERE feed_id = :feed AND item_guid = :guid');
$query->bindValue(':feed', $feedId);
$query->bindValue(':guid', $guid);
$result = $query->execute();
return $result && $result->fetchArray(SQLITE3_NUM)[0] == 1;
}
/**
* Add a feed item
* @param int $feedId The ID of the feed to which the item should be added
* @param string $guid The GUID from the RSS feed (uses link if `<guid>` not specified)
* @param string $link The link to this item
* @param string $title The title of the item
* @param string $published The date/time the item was published
* @param string $content The content of the item
* @param bool $isEncoded Whether the content has HTML (true) or is plaintext (false)
* @throws Exception If the published date is not valid
*/
public static function addItem(int $feedId, string $guid, string $link, string $title, string $published,
string $content, bool $isEncoded): void {
$db = self::getConnection();
$query = $db->prepare(
'INSERT INTO item (feed_id, item_guid, item_link, title, published_on, content, is_encoded)'
. ' VALUES (:feed, :guid, :link, :title, :published, :content, :encoded)');
$query->bindValue(':feed', $feedId);
$query->bindValue(':guid', $guid);
$query->bindValue(':link', $link);
$query->bindValue(':title', $title);
$query->bindValue(':published', (new DateTimeImmutable($published))->format(DateTimeInterface::ATOM));
$query->bindValue(':content', $content);
$query->bindValue(':encoded', $isEncoded);
$query->execute();
} }
} }

View File

@ -52,6 +52,40 @@ class Feed {
return $result; return $result;
} }
/**
* Update a feed's items
* @param int $feedId The ID of the feed to which these items belong
* @param SimpleXMLElement $channel The RSS feed items
* @return array [ 'ok' => true ] if successful, [ 'error' => message ] if not
*/
public static function updateItems(int $feedId, SimpleXMLElement $channel): array {
try {
for ($i = 0; $i < sizeof($channel->item); $i++) {
$item = $channel->item[$i];
$itemGuid = (string)$item->guid ? $item->guid : $item->link;
$isNew = !Data::itemExists($feedId, $itemGuid);
if ($isNew) {
$title = (string)$item->title;
$link = (string)$item->link;
$published = (string)$item->pubDate;
// TODO: why is this getting all encoded content, and not just the one for the current item?
$encodedContent = $item->xpath('//content:encoded');
if ($encodedContent) {
$content = (string) $encodedContent[$i];
$isEncoded = true;
} else {
$content = $item->description;
$isEncoded = false;
}
Data::addItem($feedId, $itemGuid, $link, $title, $published, $content, $isEncoded);
} // TODO: else check updated date; may want to return that from the isNew check instead
}
} catch (Exception $ex) {
return [ 'error' => $ex->getMessage() ];
}
return [ 'ok', true ];
}
/** /**
* Add an RSS feed * Add an RSS feed
* @param string $url The URL of the RSS feed to add * @param string $url The URL of the RSS feed to add
@ -64,6 +98,9 @@ class Feed {
$channel = $feed['ok']->channel; $channel = $feed['ok']->channel;
$feedId = Data::addFeed($feed['url'], (string) $channel->title, (string) $channel->lastBuildDate); $feedId = Data::addFeed($feed['url'], (string) $channel->title, (string) $channel->lastBuildDate);
$result = self::updateItems($feedId, $channel);
if (array_key_exists('error', $result)) return $result;
return [ 'ok' => true ]; return [ 'ok' => true ];
} }
} }

View File

@ -12,6 +12,7 @@ Security::verifyUser();
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// TODO: get feed, add if new, reject if existing but not owned by this user, update otherwise // TODO: get feed, add if new, reject if existing but not owned by this user, update otherwise
$result = Feed::add($_POST['url']); $result = Feed::add($_POST['url']);
echo '<pre>'; var_dump($result); echo '</pre>';
$feed = [ 'id' => $_POST['id'], 'url' => $_POST['url'] ]; $feed = [ 'id' => $_POST['id'], 'url' => $_POST['url'] ];
$title = 'TODO'; $title = 'TODO';
} else { } else {