diff --git a/src/composer.lock b/src/composer.lock index 40870d5..d929ee6 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -12,7 +12,7 @@ "source": { "type": "git", "url": "https://git.bitbadger.solutions/bit-badger/pdo-document", - "reference": "afc5d8009507a297c8169be93aee1085e652eedc" + "reference": "bcca9f5ace2ba32c24412e284ff4789480d9619d" }, "require": { "ext-pdo": "*", @@ -35,7 +35,7 @@ "Test\\Integration\\": "./tests/integration" } }, - "time": "2024-06-04T12:10:57+00:00" + "time": "2024-06-05T02:36:58+00:00" }, { "name": "netresearch/jsonmapper", diff --git a/src/lib/Data.php b/src/lib/Data.php index 83599e0..435c1e8 100644 --- a/src/lib/Data.php +++ b/src/lib/Data.php @@ -83,12 +83,13 @@ class Data /** * Create a JSON field comparison to find bookmarked items * + * @param bool $value The flag to set (optional; defaults to true) * @param string $qualifier The table qualifier to include (optional; defaults to no qualifier) * @return Field A field that will find bookmarked items */ - public static function bookmarkField(string $qualifier = ''): Field + public static function bookmarkField(bool $value = true, string $qualifier = ''): Field { - $bookField = Field::EQ('is_bookmarked', 1, '@book'); + $bookField = Field::EQ('is_bookmarked', ($value ? 1 : 0), ':book'); $bookField->qualifier = $qualifier; return $bookField; } @@ -102,7 +103,7 @@ class Data */ public static function feedField(int $feedId, string $qualifier = ''): Field { - $feedField = Field::EQ(Configuration::$idField, $feedId, '@feed'); + $feedField = Field::EQ(Configuration::$idField, $feedId, ':feed'); $feedField->qualifier = $qualifier; return $feedField; } @@ -115,7 +116,7 @@ class Data */ public static function unreadField(string $qualifier = ''): Field { - $readField = Field::EQ('is_read', 0, '@read'); + $readField = Field::EQ('is_read', 0, ':read'); $readField->qualifier = $qualifier; return $readField; } @@ -128,7 +129,7 @@ class Data */ public static function userIdField(string $qualifier = ''): Field { - $userField = Field::EQ('user_id', $_SESSION[Key::USER_ID], '@user'); + $userField = Field::EQ('user_id', $_SESSION[Key::USER_ID], ':user'); $userField->qualifier = $qualifier; return $userField; } diff --git a/src/lib/Feed.php b/src/lib/Feed.php index ea6d887..ea2aa83 100644 --- a/src/lib/Feed.php +++ b/src/lib/Feed.php @@ -116,25 +116,25 @@ class Feed return ['error' => 'Unrecognized purge type ' . PURGE_TYPE]; } - $fields = [Field::EQ('feed_id', $feedId, '@feed'), Field::EQ('is_bookmarked', 0, '@book')]; + $fields = [Field::EQ('feed_id', $feedId, ':feed'), Data::bookmarkField(false)]; $sql = Query\Delete::byFields(Table::ITEM, $fields); if (PURGE_TYPE == self::PURGE_READ) { - $readField = Field::EQ('is_read', 1, '@read'); + $readField = Field::EQ('is_read', 1, ':read'); $fields[] = $readField; $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)"; + $fields[] = Field::EQ('', Data::formatDate('-' . PURGE_NUMBER . ' day'), ':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'); + $fields[] = Field::EQ('', PURGE_NUMBER, ':keep'); $id = Configuration::$idField; $table = Table::ITEM; $sql .= ' ' . <<>'$id' IN ( SELECT data->>'$id' FROM $table - WHERE data->>'feed_id' = @feed + WHERE data->>'feed_id' = :feed ORDER BY date(coalesce(data->>'updated_on', data->>'published_on')) DESC - LIMIT -1 OFFSET @keep + LIMIT -1 OFFSET :keep ) SQL; } @@ -204,7 +204,7 @@ class Feed Document::insert(Table::FEED, self::fromParsed($feed), $pdo); - $doc = Find::firstByFields(Table::FEED, $fields, self::class); + $doc = Find::firstByFields(Table::FEED, $fields, static::class); if (!$doc) return ['error' => 'Could not retrieve inserted feed']; $result = self::updateItems($doc->id, $feed, date_create_immutable(WWW_EPOCH), $pdo); diff --git a/src/lib/ItemList.php b/src/lib/ItemList.php index 116c9fb..63b226b 100644 --- a/src/lib/ItemList.php +++ b/src/lib/ItemList.php @@ -58,7 +58,7 @@ class ItemList try { $this->dbList = Custom::list( ItemWithFeed::SELECT_WITH_FEED . ' WHERE ' - . Query::whereByFields(array_filter($allFields, fn($it) => $it->paramName <> '@search')) + . Query::whereByFields(array_filter($allFields, fn($it) => $it->paramName <> ':search')) . $searchWhere, Parameters::addFields($allFields, []), new DocumentMapper(ItemWithFeed::class)); } catch (DocumentException $ex) { @@ -73,7 +73,7 @@ class ItemList */ public static function allBookmarked(): static { - $list = new static('Bookmarked', '/?bookmarked', [Data::bookmarkField(Table::ITEM)]); + $list = new static('Bookmarked', '/?bookmarked', [Data::bookmarkField(qualifier: Table::ITEM)]); $list->linkFeed = true; return $list; } @@ -124,7 +124,7 @@ class ItemList public static function bookmarkedForFeed(int $feedId): static { return new static('Bookmarked', "/feed/items?id=$feedId&bookmarked", - [Data::feedField($feedId, Table::FEED), Data::bookmarkField(Table::ITEM)]); + [Data::feedField($feedId, Table::FEED), Data::bookmarkField(qualifier: Table::ITEM)]); } /** @@ -136,12 +136,12 @@ class ItemList */ public static function matchingSearch(string $search, bool $isBookmarked): static { - $fields = [Field::EQ('content', $search, '@search')]; - if ($isBookmarked) $fields[] = Data::bookmarkField(Table::ITEM); + $fields = [Field::EQ('content', $search, ':search')]; + if ($isBookmarked) $fields[] = Data::bookmarkField(qualifier: Table::ITEM); $list = new static('Matching' . ($isBookmarked ? ' Bookmarked' : ''), "/search?search=$search&items=" . ($isBookmarked ? 'bookmarked' : 'all'), $fields, ' AND ' . Table::ITEM . ".data->>'" . Configuration::$idField . "' IN " - . '(SELECT ROWID FROM item_search WHERE content MATCH @search)'); + . '(SELECT ROWID FROM item_search WHERE content MATCH :search)'); $list->showIndicators = true; $list->displayFeed = true; return $list; diff --git a/src/lib/ItemWithFeed.php b/src/lib/ItemWithFeed.php index 362b860..3447225 100644 --- a/src/lib/ItemWithFeed.php +++ b/src/lib/ItemWithFeed.php @@ -36,9 +36,9 @@ class ItemWithFeed extends Item */ private static function idAndUserFields(int $id): array { - $idField = Field::EQ(Configuration::$idField, $id, '@id'); + $idField = Field::EQ(Configuration::$idField, $id, ':id'); $idField->qualifier = Table::ITEM; - $userField = Field::EQ('user_id', $_SESSION[Key::USER_ID], '@user'); + $userField = Field::EQ('user_id', $_SESSION[Key::USER_ID], ':user'); $userField->qualifier = Table::FEED; return [$idField, $userField]; } diff --git a/src/lib/User.php b/src/lib/User.php index 57f541b..7223e5d 100644 --- a/src/lib/User.php +++ b/src/lib/User.php @@ -57,7 +57,7 @@ class User */ public static function hasBookmarks(): bool { - $fields = [Data::userIdField(Table::FEED), Data::bookmarkField(Table::ITEM)]; + $fields = [Data::userIdField(Table::FEED), Data::bookmarkField(true, Table::ITEM)]; return Custom::scalar(Query\Exists::query(ItemWithFeed::FROM_WITH_JOIN, Query::whereByFields($fields)), Parameters::addFields($fields, []), new ExistsMapper()); } diff --git a/src/public/feeds.php b/src/public/feeds.php index 13763a3..3506e05 100644 --- a/src/public/feeds.php +++ b/src/public/feeds.php @@ -19,33 +19,31 @@ include '../start.php'; FeedReaderCentral\Security::verifyUser(); -$field = Field::EQ('user_id', $_SESSION[Key::USER_ID], '@user'); +$field = Field::EQ('user_id', $_SESSION[Key::USER_ID], ':user'); $feeds = Custom::list(Query\Find::byFields(Table::FEED, [$field]) . " ORDER BY lower(data->>'title')", $field->appendParameter([]), new DocumentMapper(Feed::class)); -page_head('Your Feeds'); ?> -

Your Feeds

-
-

items() as /** @var Feed $feed */ $feed) { - $item = Table::ITEM; - $counts = Custom::single(<<>'feed_id' = @feed) AS total, - (SELECT COUNT(*) FROM $item WHERE data->>'feed_id' = @feed AND data->>'is_read' = '0') AS unread, - (SELECT COUNT(*) FROM $item WHERE data->>'feed_id' = @feed AND data->>'is_bookmarked' = '1') AS marked - SQL, ['@feed' => $feed->id], new ArrayMapper()) ?? ['total' => 0, 'unread' => 0, 'marked' => 0]; ?> -

title)?>
- Last Updated updated_on)?> • - As of checked_on)?>
- id", 'Edit')?> • Read - 0 ? hx_get("/feed/items?id=$feed->id&unread", 'Unread') : 'Unread'?> - () | - 0 ? hx_get("/feed/items?id=$feed->id", 'All') : 'All'?> () | - 0 ? hx_get("/feed/items?id=$feed->id&bookmarked", 'Bookmarked') : 'Bookmarked'?> - () • - id?> hx-delete=/feed/?id=id?> - hx-confirm="Are you sure you want to delete “title)?>”? This will remove the feed and all its items, including unread and bookmarked.">Delete -
-

Your Feeds

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

'; +foreach ($feeds->items() as /** @var Feed $feed */ $feed) { + $item = Table::ITEM; + $counts = Custom::single(<<>'feed_id' = :feed) AS total, + (SELECT COUNT(*) FROM $item WHERE data->>'feed_id' = :feed AND data->>'is_read' = 0) AS unread, + (SELECT COUNT(*) FROM $item WHERE data->>'feed_id' = :feed AND data->>'is_bookmarked' = 1) AS marked + SQL, [':feed' => $feed->id], new ArrayMapper()) ?? ['total' => 0, 'unread' => 0, 'marked' => 0]; + echo '

' . htmlentities($feed->title) . '
' + . 'Last Updated ' . date_time($feed->updated_on) . ' • ' + . 'As of ' . date_time($feed->checked_on) . '
' . hx_get("/feed/?id=$feed->id", 'Edit') . ' • ' + . 'Read ' . ($counts['unread'] > 0 ? hx_get("/feed/items?id=$feed->id&unread", 'Unread') : 'Unread') + . " ({$counts['unread']}) | " + . ($counts['total'] > 0 ? hx_get("/feed/items?id=$feed->id", 'All') : 'All') . " ({$counts['total']}) | " + . ($counts['marked'] > 0 ? hx_get("/feed/items?id=$feed->id&bookmarked", 'Bookmarked') : 'Bookmarked') + . " ({$counts['marked']}) • " + . "id hx-delete=/feed/?id=$feed->id " + . ' hx-confirm="Are you sure you want to delete “' . htmlspecialchars($feed->title) + . '”? This will remove the feed and all its items, including unread and bookmarked.">Delete' + . '
'; +} +echo '

'; page_foot(); diff --git a/src/public/index.php b/src/public/index.php index 3f9688e..741dada 100644 --- a/src/public/index.php +++ b/src/public/index.php @@ -21,6 +21,7 @@ if (key_exists('refresh', $_GET)) { add_error(nl2br($refreshResult['error'])); } } +//const PDO_DOC_DEBUG_SQL = true; $list = match (true) { key_exists('bookmarked', $_GET) => ItemList::allBookmarked(), diff --git a/src/start.php b/src/start.php index 336cf32..b73be31 100644 --- a/src/start.php +++ b/src/start.php @@ -12,7 +12,7 @@ session_start([ 'cookie_httponly' => true, 'cookie_samesite' => 'Strict']); -//const DOC_DEBUG_SQL = true; +//const PDO_DOC_DEBUG_SQL = true; /** * Add a message to be displayed at the top of the page diff --git a/src/util/db-update.php b/src/util/db-update.php index 3440a40..73917e5 100644 --- a/src/util/db-update.php +++ b/src/util/db-update.php @@ -16,6 +16,8 @@ require __DIR__ . '/../cli-start.php'; cli_title('DATABASE UPDATE'); +//const PDO_DOC_DEBUG_SQL = true; + if ($argc < 2) display_help(); switch ($argv[1]) {