From 3dc314b2e35b3e1111f35fca5bf42c30eec7c98c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sun, 9 Jun 2024 18:01:04 -0400 Subject: [PATCH] Fix insert statements; other minor tweaks --- src/composer.json | 8 +---- src/composer.lock | 46 ++++++++++++++++-------- src/lib/Feed.php | 27 +++++++------- src/lib/Item.php | 49 ++++++++++++++++++------- src/lib/ParsedItem.php | 76 ++++++++++++++------------------------- src/lib/User.php | 7 ++-- src/public/feed/index.php | 2 +- src/public/feeds.php | 2 +- src/start.php | 2 ++ 9 files changed, 117 insertions(+), 102 deletions(-) diff --git a/src/composer.json b/src/composer.json index 6920e7c..6eab05b 100644 --- a/src/composer.json +++ b/src/composer.json @@ -1,14 +1,8 @@ { "name": "bit-badger/feed-reader-central", "minimum-stability": "dev", - "repositories": [ - { - "type": "vcs", - "url": "https://git.bitbadger.solutions/bit-badger/pdo-document" - } - ], "require": { - "bit-badger/pdo-document": "dev-develop", + "bit-badger/pdo-document": "^1", "ext-curl": "*", "ext-dom": "*", "ext-pdo": "*", diff --git a/src/composer.lock b/src/composer.lock index 7edaf93..32c046f 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,19 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e76207a80fdea0c9dc753a55e6f66bf0", + "content-hash": "f074a197e429ac24507becc14e0d99c3", "packages": [ { "name": "bit-badger/pdo-document", - "version": "dev-develop", + "version": "v1.0.0-alpha1", "source": { "type": "git", "url": "https://git.bitbadger.solutions/bit-badger/pdo-document", - "reference": "a10ecbb1cdfdf6dd8ab3c1884b09d9ba987b7ff5" + "reference": "f784f3e52cc1e4691fa347eefc82a2e4587c7f38" }, "require": { "ext-pdo": "*", - "netresearch/jsonmapper": "^4" + "netresearch/jsonmapper": "^4", + "php": ">=8.3" }, "require-dev": { "phpunit/phpunit": "^11" @@ -25,18 +26,35 @@ "autoload": { "psr-4": { "BitBadger\\PDODocument\\": "./src", - "BitBadger\\PDODocument\\Mapper\\": "./src/Mapper", - "BitBadger\\PDODocument\\Query\\": "./src/Query" + "BitBadger\\PDODocument\\Query\\": "./src/Query", + "BitBadger\\PDODocument\\Mapper\\": "./src/Mapper" } }, - "autoload-dev": { - "psr-4": { - "Test\\Unit\\": "./tests/unit", - "Test\\Integration\\": "./tests/integration", - "Test\\Integration\\SQLite\\": "./tests/integration/sqlite" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel J. Summers", + "email": "daniel@bitbadger.solutions", + "homepage": "https://bitbadger.solutions", + "role": "Developer" } + ], + "description": "Treat SQLite (and soon PostgreSQL) as a document store", + "keywords": [ + "database", + "document", + "pdo", + "sqlite" + ], + "support": { + "email": "daniel@bitbadger.solutions", + "rss": "https://git.bitbadger.solutions/bit-badger/pdo-document.rss", + "source": "https://git.bitbadger.solutions/bit-badger/pdo-document" }, - "time": "2024-06-08T16:57:13+00:00" + "time": "2024-06-08T23:58:45+00:00" }, { "name": "netresearch/jsonmapper", @@ -93,9 +111,7 @@ "packages-dev": [], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "bit-badger/pdo-document": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/lib/Feed.php b/src/lib/Feed.php index d567d76..d6445d7 100644 --- a/src/lib/Feed.php +++ b/src/lib/Feed.php @@ -3,7 +3,7 @@ namespace FeedReaderCentral; use BitBadger\PDODocument\{ - Configuration, Custom, Document, DocumentException, DocumentList, Exists, Field, Find, Parameters, Patch, Query + Configuration, Custom, DocumentException, DocumentList, Exists, Field, Find, Parameters, Patch, Query }; use DateTimeInterface; @@ -50,14 +50,12 @@ class Feed */ public static function fromParsed(ParsedFeed $parsed): static { - $it = new static(); - $it->user_id = $_SESSION[Key::USER_ID]; - $it->url = $parsed->url; - $it->title = $parsed->title; - $it->updated_on = $parsed->updatedOn; - $it->checked_on = Data::formatDate('now'); - - return $it; + return new static( + user_id: $_SESSION[Key::USER_ID], + url: $parsed->url, + title: $parsed->title, + updated_on: $parsed->updatedOn, + checked_on: Data::formatDate('now')); } /** @@ -78,10 +76,10 @@ class Feed if ($existing) { if ($existing->published_on != $item->publishedOn || ($existing->updated_on != ($item->updatedOn ?? ''))) { - Patch::byId(Table::ITEM, $existing->id, $item->patchFields()); + Item::update($existing->id, $item); } } else { - Document::insert(Table::ITEM, Item::fromFeedItem($feedId, $item)); + Item::add($feedId, $item); } return ['ok' => true]; } catch (DocumentException $ex) { @@ -113,7 +111,7 @@ class Feed $sql .= ' AND ' . Query::whereByFields([$readField]); } elseif (PURGE_TYPE == self::PURGE_BY_DAYS) { $fields[] = Field::EQ('', Data::formatDate('-' . PURGE_NUMBER . ' day'), ':oldest'); - $sql .= " AND date(coalesce(data->>'updated_on', data->>'published_on)) < date(:oldest)"; + $sql .= " AND date(coalesce(data->>'updated_on', data->>'published_on')) < date(:oldest)"; } elseif (PURGE_TYPE == self::PURGE_BY_COUNT) { $fields[] = Field::EQ('', PURGE_NUMBER, ':keep'); $id = Configuration::$idField; @@ -189,7 +187,10 @@ class Feed return ['error' => "Already subscribed to feed $feed->url"]; } - Document::insert(Table::FEED, self::fromParsed($feed)); + Custom::nonQuery(<<<'SQL' + INSERT INTO feed (data) + VALUES (json_set(:data, '$.id', (SELECT coalesce(max(data->>'id'), 0) + 1 FROM feed))) + SQL, Parameters::json(':data', self::fromParsed($feed))); $doc = Find::firstByFields(Table::FEED, $fields, static::class); if (!$doc) return ['error' => 'Could not retrieve inserted feed']; diff --git a/src/lib/Item.php b/src/lib/Item.php index 79f8062..ecbd7e2 100644 --- a/src/lib/Item.php +++ b/src/lib/Item.php @@ -2,6 +2,8 @@ namespace FeedReaderCentral; +use BitBadger\PDODocument\{Custom, DocumentException, Parameters, Patch}; + /** * An item from a feed */ @@ -47,21 +49,42 @@ class Item } /** - * Create an item document from a parsed feed item + * Add an item * - * @param int $feedId The ID of the feed to which this item belongs - * @param ParsedItem $item The parsed feed item - * @return static The item document + * @param int $feedId The ID of the feed to which the item belongs + * @param ParsedItem $parsed The parsed item from the feed XML + * @throws DocumentException If any is encountered */ - public static function fromFeedItem(int $feedId, ParsedItem $item): static + public static function add(int $feedId, ParsedItem $parsed): void { - return new static( - feed_id: $feedId, - title: $item->title, - item_guid: $item->guid, - item_link: $item->link, - published_on: $item->publishedOn, - updated_on: $item->updatedOn, - content: $item->content); + Custom::nonQuery(<<<'SQL' + INSERT INTO item (data) + VALUES (json_set(:data, '$.id', (SELECT coalesce(max(data->>'id'), 0) + 1 FROM item))) + SQL, Parameters::json(':data', new static( + feed_id: $feedId, + title: $parsed->title, + item_guid: $parsed->guid, + item_link: $parsed->link, + published_on: $parsed->publishedOn, + updated_on: $parsed->updatedOn, + content: $parsed->content))); + } + + /** + * Update an item + * + * @param int $id The ID of the item to be updated + * @param ParsedItem $parsed The parsed item from the feed XML + * @throws DocumentException If any is encountered + */ + public static function update(int $id, ParsedItem $parsed): void + { + Patch::byId(Table::ITEM, $id, [ + 'title' => $parsed->title, + 'published_on' => $parsed->publishedOn, + 'updated_on' => $parsed->updatedOn, + 'content' => $parsed->content, + 'is_read' => 0 + ]); } } diff --git a/src/lib/ParsedItem.php b/src/lib/ParsedItem.php index 5d94350..7287506 100644 --- a/src/lib/ParsedItem.php +++ b/src/lib/ParsedItem.php @@ -9,39 +9,19 @@ use DOMNode; */ class ParsedItem { - /** @var string The title of the feed item */ - public string $title = ''; - - /** @var string The unique ID for the feed item */ - public string $guid = ''; - - /** @var string The link to the original content */ - public string $link = ''; - - /** @var string When this item was published */ - public string $publishedOn = ''; - - /** @var ?string When this item was last updated */ - public ?string $updatedOn = null; - - /** @var string The content for the item */ - public string $content = ''; - /** - * Get the fields needed to update the item in the database + * Constructor * - * @return array The fields needed tu update an item + * @param string $guid The unique ID for the feed item + * @param string $title The title of the feed item + * @param string $link The link to the original content + * @param string $publishedOn When this item was published + * @param string|null $updatedOn When this item was last updated + * @param string $content The content for the item */ - public function patchFields(): array - { - return [ - 'title' => $this->title, - 'published_on' => $this->publishedOn, - 'updated_on' => $this->updatedOn, - 'content' => $this->content, - 'is_read' => 0 - ]; - } + private function __construct(public string $guid = '', public string $title = '', public string $link = '', + public string $publishedOn = '', public ?string $updatedOn = null, + public string $content = '') { } /** * Construct a feed item from an Atom feed's `` tag @@ -64,15 +44,13 @@ class ParsedItem } if ($link == '' && str_starts_with($guid, 'http')) $link = $guid; - $item = new static(); - $item->guid = $guid; - $item->title = ParsedFeed::atomValue($node, 'title'); - $item->link = $link; - $item->publishedOn = Data::formatDate(ParsedFeed::atomValue($node, 'published')); - $item->updatedOn = Data::formatDate(ParsedFeed::atomValue($node, 'updated')); - $item->content = ParsedFeed::atomValue($node, 'content'); - - return $item; + return new static( + guid: $guid, + title: ParsedFeed::atomValue($node, 'title'), + link: $link, + publishedOn: Data::formatDate(ParsedFeed::atomValue($node, 'published')), + updatedOn: Data::formatDate(ParsedFeed::atomValue($node, 'updated')), + content: ParsedFeed::atomValue($node, 'content')); } /** @@ -87,16 +65,14 @@ class ParsedItem $updNodes = $node->getElementsByTagNameNS(ParsedFeed::ATOM_NS, 'updated'); $encNodes = $node->getElementsByTagNameNS(ParsedFeed::CONTENT_NS, 'encoded'); - $item = new static(); - $item->guid = $itemGuid == 'guid not found' ? ParsedFeed::rssValue($node, 'link') : $itemGuid; - $item->title = ParsedFeed::rssValue($node, 'title'); - $item->link = ParsedFeed::rssValue($node, 'link'); - $item->publishedOn = Data::formatDate(ParsedFeed::rssValue($node, 'pubDate')); - $item->updatedOn = Data::formatDate($updNodes->length > 0 ? $updNodes->item(0)->textContent : null); - $item->content = $encNodes->length > 0 - ? $encNodes->item(0)->textContent - : ParsedFeed::rssValue($node, 'description'); - - return $item; + return new static( + guid: $itemGuid == 'guid not found' ? ParsedFeed::rssValue($node, 'link') : $itemGuid, + title: ParsedFeed::rssValue($node, 'title'), + link: ParsedFeed::rssValue($node, 'link'), + publishedOn: Data::formatDate(ParsedFeed::rssValue($node, 'pubDate')), + updatedOn: Data::formatDate($updNodes->length > 0 ? $updNodes->item(0)->textContent : null), + content: $encNodes->length > 0 + ? $encNodes->item(0)->textContent + : ParsedFeed::rssValue($node, 'description')); } } diff --git a/src/lib/User.php b/src/lib/User.php index 4549ce2..69d1823 100644 --- a/src/lib/User.php +++ b/src/lib/User.php @@ -2,7 +2,7 @@ namespace FeedReaderCentral; -use BitBadger\PDODocument\{Custom, Document, DocumentException, Field, Find, Parameters, Query}; +use BitBadger\PDODocument\{Custom, DocumentException, Field, Find, Parameters, Query}; use BitBadger\PDODocument\Mapper\ExistsMapper; /** @@ -40,7 +40,10 @@ class User */ public static function add(string $email, string $password): void { - Document::insert(Table::USER, new User(email: $email, password: $password)); + Custom::nonQuery(<<<'SQL' + INSERT INTO user (data) + VALUES (json_set(:data, '$.id', (SELECT coalesce(max(data->>'id'), 0) + 1 FROM user))) + SQL, Parameters::json(':data', new User(email: $email, password: $password))); } /** diff --git a/src/public/feed/index.php b/src/public/feed/index.php index 2caaaf3..d7e29e2 100644 --- a/src/public/feed/index.php +++ b/src/public/feed/index.php @@ -29,7 +29,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'DELETE') { if ($_SERVER['REQUEST_METHOD'] == 'POST') { try { - $isNew = $_POST['id'] == 'new'; + $isNew = $_POST['id'] == '-1'; if ($isNew) { $result = Feed::add($_POST['url']); } else { diff --git a/src/public/feeds.php b/src/public/feeds.php index 2abb3a4..8cd5cfc 100644 --- a/src/public/feeds.php +++ b/src/public/feeds.php @@ -19,7 +19,7 @@ $feeds = Custom::list(Query\Find::byFields(Table::FEED, [$field]) . " ORDER BY l $field->appendParameter([]), new DocumentMapper(Feed::class)); page_head('Your Feeds'); -echo '

Your Feeds

' . hx_get('/feed/?id=new', 'Add Feed') . '

'; +echo '

Your Feeds

' . hx_get('/feed/?id=-1', 'Add Feed') . '

'; foreach ($feeds->items() as /** @var Feed $feed */ $feed) { $item = Table::ITEM; $counts = Custom::single(<<