Move files, complete PHP migration
The application works the same way as the F# version
This commit is contained in:
25
src/lib/Domain/History.php
Normal file
25
src/lib/Domain/History.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* A record of an action taken on a request
|
||||
*/
|
||||
class History implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @param string $asOf The date/time this entry was made
|
||||
* @param RequestAction $action The action taken for this history entry
|
||||
* @param string|null $text The text for this history entry (optional)
|
||||
*/
|
||||
public function __construct(public string $asOf, public RequestAction $action, public ?string $text = null) { }
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
$values = ['asOf' => $this->asOf, 'action' => $this->action->value];
|
||||
if (isset($this->text)) $values['text'] = $this->text;
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
30
src/lib/Domain/Note.php
Normal file
30
src/lib/Domain/Note.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
use BitBadger\PDODocument\DocumentException;
|
||||
|
||||
/**
|
||||
* A note entered on a prayer request
|
||||
*/
|
||||
class Note
|
||||
{
|
||||
/**
|
||||
* @param string $asOf The date/time this note was recorded
|
||||
* @param string $text The text of the note
|
||||
*/
|
||||
public function __construct(public string $asOf, public string $text) { }
|
||||
|
||||
/**
|
||||
* Retrieve notes for a given request
|
||||
*
|
||||
* @param string $id The ID of the request for which notes should be retrieved
|
||||
* @return array|Note[] The notes for the request, or an empty array if the request was not found
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byRequestId(string $id): array
|
||||
{
|
||||
$req = Request::byId($id);
|
||||
return $req ? $req->notes : [];
|
||||
}
|
||||
}
|
||||
41
src/lib/Domain/Recurrence.php
Normal file
41
src/lib/Domain/Recurrence.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
use DateInterval;
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* The recurrence for a prayer request
|
||||
*/
|
||||
class Recurrence implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @param RecurrencePeriod $period The recurrence period
|
||||
* @param int|null $interval How many of the periods will pass before the request is visible again
|
||||
*/
|
||||
public function __construct(public RecurrencePeriod $period, public ?int $interval = null) { }
|
||||
|
||||
/**
|
||||
* Get the date/time interval for this recurrence
|
||||
*
|
||||
* @return DateInterval The interval matching the recurrence
|
||||
*/
|
||||
public function interval(): DateInterval
|
||||
{
|
||||
$period = match ($this->period) {
|
||||
RecurrencePeriod::Immediate => 'T0S',
|
||||
RecurrencePeriod::Hours => "T{$this->interval}H",
|
||||
RecurrencePeriod::Days => "{$this->interval}D",
|
||||
RecurrencePeriod::Weeks => ($this->interval * 7) . 'D'
|
||||
};
|
||||
return new DateInterval("P$period");
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
$values = ['period' => $this->period->value];
|
||||
if (isset($this->interval)) $values['interval'] = $this->interval;
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
21
src/lib/Domain/RecurrencePeriod.php
Normal file
21
src/lib/Domain/RecurrencePeriod.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
/**
|
||||
* The type of recurrence a request can have
|
||||
*/
|
||||
enum RecurrencePeriod: string
|
||||
{
|
||||
/** Requests, once prayed, are available again immediately */
|
||||
case Immediate = 'Immediate';
|
||||
|
||||
/** Requests, once prayed, appear again in a number of hours */
|
||||
case Hours = 'Hours';
|
||||
|
||||
/** Requests, once prayed, appear again in a number of days */
|
||||
case Days = 'Days';
|
||||
|
||||
/** Requests, once prayed, appear again in a number of weeks */
|
||||
case Weeks = 'Weeks';
|
||||
}
|
||||
207
src/lib/Domain/Request.php
Normal file
207
src/lib/Domain/Request.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
use BitBadger\PDODocument\{Custom, DocumentException, DocumentList, Find, Mapper\DocumentMapper};
|
||||
use DateTimeImmutable;
|
||||
use Exception;
|
||||
use JsonSerializable;
|
||||
use MyPrayerJournal\Table;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
/**
|
||||
* A prayer request
|
||||
*/
|
||||
class Request implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @param string $id The ID for the request
|
||||
* @param string $enteredOn The date/time this request was originally entered
|
||||
* @param string $userId The ID of the user to whom this request belongs
|
||||
* @param string|null $snoozedUntil The date/time the snooze expires for this request (null = not snoozed)
|
||||
* @param string|null $showAfter The date/time the current recurrence period is over (null = immediate)
|
||||
* @param Recurrence $recurrence The recurrence for this request
|
||||
* @param History[] $history The history of this request
|
||||
* @param Note[] $notes Notes regarding this request
|
||||
* @throws Exception If the ID generation fails
|
||||
*/
|
||||
public function __construct(public string $id = '', public string $enteredOn = '', public string $userId = '',
|
||||
public ?string $snoozedUntil = null, public ?string $showAfter = null,
|
||||
public Recurrence $recurrence = new Recurrence(RecurrencePeriod::Immediate),
|
||||
public array $history = [], public array $notes = [])
|
||||
{
|
||||
if ($id == '') {
|
||||
$this->id = (new Cuid2())->toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current text for this request
|
||||
*
|
||||
* @return string The most recent text for the request
|
||||
*/
|
||||
public function currentText(): string
|
||||
{
|
||||
foreach ($this->history as $hist) if (isset($hist->text)) return $hist->text;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date/time this request was last marked as prayed
|
||||
*
|
||||
* @return string|null The date/time this request was last marked as prayed
|
||||
*/
|
||||
public function lastPrayed(): ?string
|
||||
{
|
||||
foreach ($this->history as $hist) if ($hist->action == RequestAction::Prayed) return $hist->asOf;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has this request been answered?
|
||||
*
|
||||
* @return bool True if the request is answered, false if not
|
||||
*/
|
||||
public function isAnswered(): bool
|
||||
{
|
||||
return $this->history[0]->action == RequestAction::Answered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request currently snoozed?
|
||||
*
|
||||
* @return bool True if the request is snoozed, false if not
|
||||
* @throws Exception If the snoozed until date/time is not valid
|
||||
*/
|
||||
public function isSnoozed(): bool
|
||||
{
|
||||
return isset($this->snoozedUntil) && new DateTimeImmutable($this->snoozedUntil) > new DateTimeImmutable('now');
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request currently not shown due to recurrence?
|
||||
*
|
||||
* @return bool True if the request is pending, false if not
|
||||
* @throws Exception If the snoozed or show-after date/times are not valid
|
||||
*/
|
||||
public function isPending(): bool
|
||||
{
|
||||
return !$this->isSnoozed()
|
||||
&& isset($this->showAfter)
|
||||
&& new DateTimeImmutable($this->showAfter) > new DateTimeImmutable('now');
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
$values = [
|
||||
'id' => $this->id,
|
||||
'enteredOn' => $this->enteredOn,
|
||||
'userId' => $this->userId,
|
||||
'recurrence' => $this->recurrence,
|
||||
'history' => $this->history,
|
||||
'notes' => $this->notes
|
||||
];
|
||||
if (isset($this->snoozedUntil)) $values['snoozedUntil'] = $this->snoozedUntil;
|
||||
if (isset($this->showAfter)) $values['showAfter'] = $this->showAfter;
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a request by its ID
|
||||
*
|
||||
* @param string $id The ID of the request
|
||||
* @return Request|false The request if it is found and belongs to the current user, false if not
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byId(string $id): Request|false
|
||||
{
|
||||
$req = Find::byId(Table::REQUEST, $id, self::class);
|
||||
return ($req && $req->userId == $_SESSION['user_id']) ? $req : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's active journal requests
|
||||
*
|
||||
* @return DocumentList<Request> The requests for the user's journal
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function forJournal(): DocumentList
|
||||
{
|
||||
$table = Table::REQUEST;
|
||||
return Custom::list(<<<SQL
|
||||
SELECT data, (
|
||||
SELECT h.value->>'asOf' as_of
|
||||
FROM $table i LEFT JOIN json_each(i.data, '$.history') h
|
||||
WHERE r.data->>'id' = i.data->>'id' AND h.value->>'action' = 'Prayed'
|
||||
LIMIT 1) last_prayed
|
||||
FROM $table r
|
||||
WHERE data->>'userId' = :userId
|
||||
AND data->>'$.history[0].action' <> 'Answered'
|
||||
AND (data->>'snoozedUntil' IS NULL OR data->>'snoozedUntil' < datetime('now'))
|
||||
AND (data->>'showAfter' IS NULL OR data->>'showAfter' < datetime('now'))
|
||||
ORDER BY coalesce(last_prayed, data->>'snoozedUntil', data->>'showAfter', data->>'$.history[0].asOf')
|
||||
SQL, [':userId' => $_SESSION['user_id']], new DocumentMapper(self::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get either the user's active or answered requests
|
||||
*
|
||||
* @param bool $active True to retrieve active requests, false to retrieve answered requests
|
||||
* @param bool $snoozed True to retrieve only snoozed requests
|
||||
* @return DocumentList<Request> The requests matching the criteria
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
private static function forUser(bool $active = true, bool $snoozed = false): DocumentList
|
||||
{
|
||||
$table = Table::REQUEST;
|
||||
$op = $active ? '<>' : '=';
|
||||
$extra = $snoozed ? "AND datetime(data->>'snoozedUntil') > datetime('now')" : '';
|
||||
$order = $active
|
||||
? "coalesce(data->>'snoozedUntil', data->>'showAfter', last_prayed, data->>'$.history[0].asOf')"
|
||||
: "data->>'$.history[0].asOf' DESC";
|
||||
return Custom::list(<<<SQL
|
||||
SELECT data, (
|
||||
SELECT h.value->>'asOf' as_of
|
||||
FROM $table i LEFT JOIN json_each(i.data, '$.history') h
|
||||
WHERE r.data->>'id' = i.data->>'id' AND h.value->>'action' = 'Prayed'
|
||||
LIMIT 1) last_prayed
|
||||
FROM $table r
|
||||
WHERE data->>'userId' = :userId
|
||||
AND data->>'$.history[0].action' $op 'Answered' $extra
|
||||
ORDER BY $order
|
||||
SQL, [':userId' => $_SESSION['user_id']], new DocumentMapper(self::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of active requests for a user
|
||||
*
|
||||
* @return DocumentList<Request> The user's active requests
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function active(): DocumentList
|
||||
{
|
||||
return self::forUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of answered requests for a user
|
||||
*
|
||||
* @return DocumentList<Request> The user's answered requests
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function answered(): DocumentList
|
||||
{
|
||||
return self::forUser(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of snoozed requests for a user
|
||||
*
|
||||
* @return DocumentList<Request> The user's snoozed requests
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function snoozed(): DocumentList
|
||||
{
|
||||
return self::forUser(snoozed: true);
|
||||
}
|
||||
}
|
||||
21
src/lib/Domain/RequestAction.php
Normal file
21
src/lib/Domain/RequestAction.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace MyPrayerJournal\Domain;
|
||||
|
||||
/**
|
||||
* An action taken on a prayer request
|
||||
*/
|
||||
enum RequestAction: string
|
||||
{
|
||||
/** The request was created */
|
||||
case Created = 'Created';
|
||||
|
||||
/** The request was marked as having been prayed for */
|
||||
case Prayed = 'Prayed';
|
||||
|
||||
/** The request was updated */
|
||||
case Updated = 'Updated';
|
||||
|
||||
/** The request was marked as answered */
|
||||
case Answered = 'Answered';
|
||||
}
|
||||
Reference in New Issue
Block a user