Fix insert statements; other minor tweaks
This commit is contained in:
parent
edc9a218b7
commit
3dc314b2e3
@ -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": "*",
|
||||
|
46
src/composer.lock
generated
46
src/composer.lock
generated
@ -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": {
|
||||
|
@ -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'];
|
||||
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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 `<entry>` 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'));
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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 '<h1>Your Feeds</h1><article><p class=action_buttons>' . hx_get('/feed/?id=new', 'Add Feed') . '</p>';
|
||||
echo '<h1>Your Feeds</h1><article><p class=action_buttons>' . hx_get('/feed/?id=-1', 'Add Feed') . '</p>';
|
||||
foreach ($feeds->items() as /** @var Feed $feed */ $feed) {
|
||||
$item = Table::ITEM;
|
||||
$counts = Custom::single(<<<SQL
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
use BitBadger\PDODocument\Configuration;
|
||||
use FeedReaderCentral\{Key, Security, User};
|
||||
|
||||
require 'app-config.php';
|
||||
@ -134,6 +135,7 @@ function frc_redirect(string $value): never
|
||||
}
|
||||
session_commit();
|
||||
header("Location: $value", true, 303);
|
||||
Configuration::resetPDO();
|
||||
die();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user