diff --git a/api/composer.json b/api/composer.json
index 4e00ddc..e1058f7 100755
--- a/api/composer.json
+++ b/api/composer.json
@@ -1,14 +1,8 @@
{
"require": {
- "local/api.endpoints": "1.0.0-dev",
"reflect/plugin-rules": "^1.5",
- "victorwesterlund/xenum": "^1.1"
+ "victorwesterlund/xenum": "dev-master",
+ "victorwesterlund/libmysqldriver": "dev-master"
},
- "minimum-stability": "dev",
- "repositories": [
- {
- "type": "path",
- "url": "src/packages/Endpoints"
- }
- ]
+ "minimum-stability": "dev"
}
diff --git a/api/composer.lock b/api/composer.lock
index aaeccbf..2b6ee6b 100755
--- a/api/composer.lock
+++ b/api/composer.lock
@@ -4,33 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "9da96ba90ef20d885034442b30dce0a3",
+ "content-hash": "13ba5cc60bab24ac8ef5b1018fe4249b",
"packages": [
- {
- "name": "local/api.endpoints",
- "version": "1.0.0-dev",
- "dist": {
- "type": "path",
- "url": "src/packages/Endpoints",
- "reference": "89b7b9a4cc504abddb4aeec8e05a95c9d9087575"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "VLW\\API\\": "src/"
- }
- },
- "authors": [
- {
- "name": "Victor Westerlund",
- "email": "victor@vlw.se"
- }
- ],
- "description": "Endpoint pathmappings for VLW API",
- "transport-options": {
- "relative": true
- }
- },
{
"name": "reflect/plugin-rules",
"version": "1.5.0",
@@ -68,9 +43,47 @@
},
"time": "2024-01-17T11:07:44+00:00"
},
+ {
+ "name": "victorwesterlund/libmysqldriver",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/VictorWesterlund/php-libmysqldriver.git",
+ "reference": "adc2fda90a3b8308e8a9df202d5ec418a9220ff8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/VictorWesterlund/php-libmysqldriver/zipball/adc2fda90a3b8308e8a9df202d5ec418a9220ff8",
+ "reference": "adc2fda90a3b8308e8a9df202d5ec418a9220ff8",
+ "shasum": ""
+ },
+ "default-branch": true,
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "libmysqldriver\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "Victor Westerlund",
+ "email": "victor.vesterlund@gmail.com"
+ }
+ ],
+ "description": "Abstraction library for common mysqli features",
+ "support": {
+ "issues": "https://github.com/VictorWesterlund/php-libmysqldriver/issues",
+ "source": "https://github.com/VictorWesterlund/php-libmysqldriver/tree/3.6.1"
+ },
+ "time": "2024-04-29T08:17:12+00:00"
+ },
{
"name": "victorwesterlund/xenum",
- "version": "1.1.1",
+ "version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/VictorWesterlund/php-xenum.git",
@@ -82,6 +95,7 @@
"reference": "8972f06f42abd1f382807a67e937d5564bb89699",
"shasum": ""
},
+ "default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
@@ -110,7 +124,8 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
- "local/api.endpoints": 20
+ "victorwesterlund/xenum": 20,
+ "victorwesterlund/libmysqldriver": 20
},
"prefer-stable": false,
"prefer-lowest": false,
diff --git a/api/endpoints/coffee/GET.php b/api/endpoints/coffee/GET.php
deleted file mode 100755
index 4ee9144..0000000
--- a/api/endpoints/coffee/GET.php
+++ /dev/null
@@ -1,39 +0,0 @@
-db->for(CoffeeModel::TABLE)
- ->order([CoffeeModel::DATE_TIMESTAMP_CREATED->value => "DESC"])
- ->limit(self::LIST_LIMIT)
- ->select([
- CoffeeModel::ID->value,
- CoffeeModel::DATE_TIMESTAMP_CREATED->value
- ]);
-
- return parent::is_mysqli_result($resp)
- ? new Response($resp->fetch_all(MYSQLI_ASSOC))
- : $this->resp_database_error();
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/coffee/POST.php b/api/endpoints/coffee/POST.php
deleted file mode 100755
index 5643942..0000000
--- a/api/endpoints/coffee/POST.php
+++ /dev/null
@@ -1,36 +0,0 @@
-db->for(CoffeeModel::TABLE)
- ->insert([
- CoffeeModel::ID->value => $id,
- CoffeeModel::DATE_TIMESTAMP_CREATED->value => time(),
- ]);
-
- // Return 201 Created and entity id if successful
- return $insert ? new Response($id, 201) : $this->resp_database_error();
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/media/GET.php b/api/endpoints/media/GET.php
deleted file mode 100755
index bda4957..0000000
--- a/api/endpoints/media/GET.php
+++ /dev/null
@@ -1,95 +0,0 @@
-ruleset = new Ruleset(strict: true);
-
- $this->ruleset->GET([
- (new Rules(MediaModel::ID->value))
- ->required()
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
-
- (new Rules(self::GET_DISPOSITION_KEY))
- ->type(Type::ENUM, MediaDispositionEnum::values())
- ->default(MediaDispositionEnum::METADATA->value)
- ]);
- }
-
- // # Helper methods
-
- private function fetch_srcset(string $id): array {
- $resp = $this->db->for(WorkTagsModel::TABLE)
- ->where([WorkTagsModel::ANCHOR->value => $id])
- ->select(WorkTagsModel::NAME->value);
-
- return parent::is_mysqli_result($resp) ? $resp->fetch_all(MYSQLI_ASSOC) : [];
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
- public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- $resp = $this->db->for(MediaModel::TABLE)
- ->where([MediaModel::ID->value => $_GET[MediaModel::ID->value]])
- ->select([
- MediaModel::ID->value,
- MediaModel::NAME->value,
- MediaModel::TYPE->value,
- MediaModel::MIME->value,
- MediaModel::EXTENSION->value,
- MediaModel::SRCSET->value,
- MediaModel::DATE_TIMESTAMP_CREATED->value,
- ]);
-
- // Bail out if something went wrong retrieving rows from the database
- if (!parent::is_mysqli_result($resp)) {
- return $this->resp_database_error();
- }
-
- $media = $resp->fetch_assoc();
- $test = true;
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/media/POST.php b/api/endpoints/media/POST.php
deleted file mode 100755
index 1bd7916..0000000
--- a/api/endpoints/media/POST.php
+++ /dev/null
@@ -1,117 +0,0 @@
-ruleset = new Ruleset(strict: true);
-
- $this->ruleset->POST([
- (new Rules(MediaModel::ID->value))
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(parent::gen_uuid4()),
-
- (new Rules(MediaModel::NAME->value))
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(null),
-
- (new Rules(MediaModel::TYPE->value))
- ->type(Type::ENUM, MediaTypeEnum::values())
- ->default(null),
-
- (new Rules(MediaModel::EXTENSION->value))
- ->type(Type::STRING)
- ->min(3)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(null),
-
- (new Rules(MediaModel::MIME->value))
- ->type(Type::STRING)
- ->min(3)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(null),
-
- (new Rules(MediaModel::SRCSET->value))
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(null)
- ]);
- }
-
- // # Helper methods
-
- // Returns true if an srcset exists for provided key
- private static function media_srcset_exists(): bool {
- // No srcet get parameter has been set
- if (empty($_POST[MediaModel::SRCSET->value])) {
- return true;
- }
-
- // Check if the provided srcset exists by calling the srcset endpoint
- return Call("media/srcset?id={$_POST[MediaModel::SRCSET->value]}", Method::GET)->ok;
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
- public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Bail out if an srcset doesn't exist
- if (!self::media_srcset_exists()) {
- return new Response("No media srcset exists with id '{$_POST[MediaModel::SRCSET->value]}'", 404);
- }
-
- $insert = $this->db->for(MediaModel::TABLE)
- ->insert([
- MediaModel::ID->value => $_POST[MediaModel::ID->value],
- MediaModel::NAME->value => $_POST[MediaModel::NAME->value],
- MediaModel::MIME->value => $_POST[MediaModel::MIME->value],
- // Strip dots from extension string if set
- MediaModel::EXTENSION->value => $_POST[MediaModel::EXTENSION->value]
- ? str_replace(".", "", $_POST[MediaModel::EXTENSION->value])
- : null,
- MediaModel::SRCSET->value => $_POST[MediaModel::SRCSET->value],
- MediaModel::DATE_TIMESTAMP_CREATED->value => time()
- ]);
-
- // Return media id if insert was successful
- return $insert
- ? new Response($_POST[MediaModel::ID->value], 201)
- : $this->resp_database_error();
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/media/srcset/GET.php b/api/endpoints/media/srcset/GET.php
deleted file mode 100755
index f9a0599..0000000
--- a/api/endpoints/media/srcset/GET.php
+++ /dev/null
@@ -1,106 +0,0 @@
-ruleset = new Ruleset(strict: true);
-
- $this->ruleset->GET([
- (new Rules(MediaSrcsetModel::ID->value))
- ->required()
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ]);
- }
-
- // # Helper methods
-
- // Get metadata for the requested srcset
- private function get_srcset(): array|false {
- $srcset = $this->db->for(MediaSrcsetModel::TABLE)
- ->where([MediaSrcsetModel::ID->value => $_GET[MediaSrcsetModel::ID->value]])
- ->select([MediaSrcsetModel::ANCHOR_DEFAULT->value]);
-
- // Something went wrong retrieving rows from the database
- if (!parent::is_mysqli_result($srcset)) {
- return false;
- }
-
- // Return assoc array of srcset data if it exists
- return $srcset->num_rows === 1 ? $srcset->fetch_assoc() : false;
- }
-
- // Get all media entities that are part of the requested srcset
- private function get_srcset_media(): mysqli_result|false {
- $media = $this->db->for(MediaModel::TABLE)
- ->where([MediaModel::SRCSET->value => $_GET[MediaSrcsetModel::ID->value]])
- ->select([
- MediaModel::ID->value,
- MediaModel::TYPE->value,
- MediaModel::MIME->value,
- MediaModel::EXTENSION->value
- ]);
-
- return parent::is_mysqli_result($media) ? $media : false;
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
- public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Get srcset data
- $srcset = $this->get_srcset();
- if (!$srcset) {
- return new Response("No media srcset exist with id '{$_GET[MediaSrcsetModel::ID->value]}'", 404);
- }
-
- $media = $this->get_srcset_media();
- if (!$media) {
- return new Response("Failed to fetch srcset media", 500);
- }
-
- $media_entities = $media->fetch_all(MYSQLI_ASSOC);
-
- // This is the id of the media entity that is considered the default or "fallback"
- $srcet_default_media_id = $srcset[MediaSrcsetModel::ANCHOR_DEFAULT->value];
-
- // Return assoc array of all media entities that are in this srcset
- return new Response([
- // Return default media entity separately from the rest of the srcset as an assoc array
- "default" => array_filter($media_entities, fn(array $entity) => $entity[MediaModel::ID->value] === $srcet_default_media_id)[0],
- // Return all media that isn't default as array of assoc arrays
- "srcset" => array_filter($media_entities, fn(array $entity) => $entity[MediaModel::ID->value] !== $srcet_default_media_id)
- ]);
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/media/srcset/POST.php b/api/endpoints/media/srcset/POST.php
deleted file mode 100755
index 609624a..0000000
--- a/api/endpoints/media/srcset/POST.php
+++ /dev/null
@@ -1,55 +0,0 @@
-code !== 404) {
- // Wow a UUID4 collision... buy a lottery ticket
- if ($srcset_existing->code === 200) {
- return $this->main();
- }
-
- // Failed to get srcset
- return new Response("Something went wrong when checking if the srcset exists", 500);
- }
-
- // Create new srcset entity
- $insert = $this->db->for(MediaSrcsetModel::TABLE)
- ->insert([
- MediaSrcsetModel::ID->value => $id
- ]);
-
- // Return created srcset id if successful
- return $insert
- ? new Response($id, 201)
- : $this->resp_database_error();
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/messages/POST.php b/api/endpoints/messages/POST.php
index e2a7758..5e52b17 100755
--- a/api/endpoints/messages/POST.php
+++ b/api/endpoints/messages/POST.php
@@ -6,20 +6,16 @@
use ReflectRules\Rules;
use ReflectRules\Ruleset;
- use Reflect\Method;
- use function Reflect\Call;
-
use VLW\API\Databases\VLWdb\VLWdb;
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
require_once Path::root("src/databases/VLWdb.php");
- require_once Path::root("src/databases/models/Messages.php");
+ require_once Path::root("src/databases/models/Messages/Messages.php");
class POST_Messages extends VLWdb {
protected Ruleset $ruleset;
public function __construct() {
- parent::__construct();
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->POST([
@@ -34,46 +30,19 @@
->min(1)
->max(parent::MYSQL_TEXT_MAX_LENGTH)
]);
- }
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- //return new Response(["hello" => "maybe"], 500);
+ // Use copy of request body as entity
+ $entity = $_POST;
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
+ $entity[MessagesModel::ID->value] = parent::gen_uuid4();
+ $entity[MessagesModel::DATE_CREATED->value] = time();
- // Generate UUID for entity
- $id = parent::gen_uuid4();
-
- // Attempt to create new entity
- $insert = $this->db->for(MessagesModel::TABLE)
- ->insert([
- MessagesModel::ID->value => $id,
- MessagesModel::EMAIL->value => $_POST["email"],
- MessagesModel::MESSAGE->value => $_POST["message"],
- MessagesModel::DATE_TIMESTAMP_CREATED->value => time(),
- ]);
-
- // Bail out if insert failed
- if (!$insert) {
- return $this->resp_database_error();
- }
-
- // Return 201 Created and entity id
- return new Response($id, 201);
+ return $this->db->for(MessagesModel::TABLE)->insert($entity) === true
+ ? new Response($entity[MessagesModel::ID->value], 201)
+ : new Response("Failed to create message", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/releases/POST.php b/api/endpoints/releases/POST.php
deleted file mode 100755
index 7160ada..0000000
--- a/api/endpoints/releases/POST.php
+++ /dev/null
@@ -1,223 +0,0 @@
-ruleset = new Ruleset(strict: true);
-
- $this->ruleset->POST([
- (new Rules(ReleasesPostModel::GITHUB_USER->value))
- ->required()
- ->type(Type::STRING)
- ->min(1),
-
- (new Rules(ReleasesPostModel::GITHUB_REPO->value))
- ->required()
- ->type(Type::STRING)
- ->min(1),
-
- (new Rules(ReleasesPostModel::GITHUB_TAG->value))
- ->required()
- ->type(Type::STRING)
- ->type(Type::NUMBER)
- ->min(1)
- ]);
-
- $this->curl = curl_init();
-
- curl_setopt($this->curl, CURLOPT_USERAGENT, $_ENV["github"]["user_agent"]);
- curl_setopt($this->curl, CURLOPT_HEADER, true);
- curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($this->curl, CURLOPT_HTTPHEADER, [
- "Accept" => "application/vnd.github+json",
- "Authorization" => "token {$_ENV["github"]["api_key"]}",
- "X-GitHub-Api-Version" => "2022-11-28"
- ]);
- }
-
- // # GitHub
-
- // Generate HTML from a GitHub "auto-generate" release body
- protected static function gh_auto_release_md_to_html(string $md): string {
- $output = "";
-
- // Parse each line of markdown
- $lines = explode(PHP_EOL, $md);
-
- foreach ($lines as $i => $line) {
- // Ignore header line from releases
- if ($i < 1) continue;
-
- // Replace all URLs with HTMLAnchor tags, they will be PRs
- $links = [];
- preg_match_all(self::REGEX_URL, $line, $links, PREG_UNMATCHED_AS_NULL);
- foreach ($links as $i => $link) {
- if (empty($link)) continue;
-
- // Last crumb from link pathname will be the PR id
- $pr_id = explode("/", $link[$i]);
- $pr_id = end($pr_id);
-
- $line = str_replace($link, "{$pr_id}", $line);
- }
-
- // Replace all at-handles with links to GitHub user profiles
- $handles = [];
- preg_match_all(self::REGEX_HANDLE, $line, $handles, PREG_UNMATCHED_AS_NULL);
- foreach ($handles as $i => $handle) {
- if (empty($handle)) continue;
-
- // GitHub user URL without the "@"
- $url = "https://github.com/" . substr($handle[$i], 1);
-
- $line = str_replace($handle, "{$handle[$i]}", $line);
- }
-
- $output .= "
{$line}
";
- }
-
- return $output;
- }
-
- // Return fully qualified URL to GitHub API releases endpoint
- private static function get_url(): string {
- return implode("/", [
- self::GITHUB_API,
- "repos",
- $_POST[ReleasesPostModel::GITHUB_USER->value],
- $_POST[ReleasesPostModel::GITHUB_REPO->value],
- "releases",
- "tags",
- $_POST[ReleasesPostModel::GITHUB_TAG->value],
- ]);
- }
-
- // Fetch release information from GitHub API
- private function fetch_release_data(): array {
- $url = self::get_url();
- curl_setopt($this->curl, CURLOPT_URL, self::get_url());
-
- $resp = curl_exec($this->curl);
-
- $header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
- $header = substr($resp, 0, $header_size);
- $body = substr($resp, $header_size);
-
- return json_decode($body, true);
- }
-
- // # Sup
-
- private function create_link_to_release_page(string $id, string $href): Response {
- return Call("work/actions?id={$id}", Method::POST, [
- WorkActionsModel::DISPLAY_TEXT->value => "Release details",
- WorkActionsModel::HREF->value => $href,
- WorkActionsModel::EXTERNAL->value => true
- ]);
- }
-
- // Create a tag for entity
- private function create_tag(string $id, WorkTagsNameEnum $tag): Response {
- return Call("work/tags?id={$id}", Method::POST, [
- // Set "RELEASE" tag on new entity
- WorkTagsModel::NAME->value => $tag->value
- ]);
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- $data = $this->fetch_release_data();
- if (!$data) {
- return new Response("Failed to fetch release data", 500);
- }
-
-
- // Transform repo name to lowercase for summary title
- $title = strtolower($_POST["repo"]);
-
- // Use repo name and tag name as heading for summary
- $summary = "Release {$title}@{$data["name"]}
";
- // Append HTML-ified release notes from GitHub to summary
- $summary .= self::gh_auto_release_md_to_html($data["body"]);
-
- $date_published = new \DateTime($data["published_at"], new \DateTimeZone("UTC"));
-
- // Create work entity
- $work_entity = Call("work", Method::POST, [
- WorkModel::SUMMARY->value => $summary,
- // Convert time created to Unix timestamp for work endpoint
- WorkModel::DATE_TIMESTAMP_CREATED->value => $date_published->format("U"),
- ]);
-
- // Bail out if creating the work entity failed
- if (!$work_entity->ok) {
- return new Response("Failed to create work entity for release", 500);
- }
-
- $work_entity_id = $work_entity->output();
-
- // Create entity tags for release
- $tags = [
- WorkTagsNameEnum::VLW,
- WorkTagsNameEnum::RELEASE
- ];
- foreach ($tags as $tag) {
- // Create entity tag for release or exit if failed to create
- if (!$this->create_tag($work_entity_id, $tag)->ok) {
- return new Response("Failed to create {$tag->name} tag for release entity", 500);
- }
- }
-
- // Create link to release page on GitHub
- if (!$this->create_link_to_release_page($work_entity_id, $data["html_url"])) {
- return new Response("Failed to create link to release page on GitHub", 500);
- }
-
- return new Response($work_entity_id, 201);
- }
- }
\ No newline at end of file
diff --git a/api/endpoints/search/GET.php b/api/endpoints/search/GET.php
index 3a39896..5c2c895 100755
--- a/api/endpoints/search/GET.php
+++ b/api/endpoints/search/GET.php
@@ -11,11 +11,10 @@
use VLW\API\Databases\VLWdb\VLWdb;
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
- use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
+ require_once Path::root("src/Endpoints.php");
require_once Path::root("src/databases/VLWdb.php");
- require_once Path::root("src/databases/models/Work.php");
- require_once Path::root("src/databases/models/WorkActions.php");
+ require_once Path::root("src/databases/models/Work/Work.php");
class GET_Search extends VLWdb {
const GET_QUERY = "q";
@@ -23,118 +22,35 @@
protected Ruleset $ruleset;
public function __construct() {
- parent::__construct();
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
(new Rules(self::GET_QUERY))
->required()
->type(Type::STRING)
- ->min(2)
+ ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
]);
+
+ parent::__construct($this->ruleset);
}
- // Return an SQL string from array for use in prepared statements
- private static function array_to_wildcard_sql(array $columns): string {
- $sql = array_map(fn(string $column): string => "{$column} LIKE CONCAT('%', ?, '%')", $columns);
-
- return implode(" OR ", $sql);
- }
-
- // Return chained AND statements from array for use in prepared statements
- private static function array_to_and_statement(array $keys): string {
- $sql = array_map(fn(string $k): string => "{$k} = ?", $keys);
-
- return implode(" AND ", $sql);
- }
-
- // Wildcard search columns in table with query string from query string
- // This has to be implemented manually until "libmysqldriver/MySQL" supports wildcard SELECT
- private function search(string $table, array $columns, array $conditions = null): array {
- // Create CSV from columns array
- $columns_concat = implode(",", $columns);
-
- // Create SQL LIKE wildcard statement for each column.
- $where = self::array_to_wildcard_sql($columns);
-
- // Create array of values from query string for each colum
- $values = array_fill(0, count($columns), $_GET[self::GET_QUERY]);
-
- if ($conditions) {
- $conditions_sql = self::array_to_and_statement(array_keys($conditions));
-
- // Wrap positive where statements and prepare new group of conditions
- // WHERE () AND ()
- $where = "({$where}) AND ({$conditions_sql})";
-
- // Append values from conditions statements to prepared statement
- array_push($values, ...array_values($conditions));
- }
-
- // Order the rows by the array index of $colums received
- $rows = $this->db->exec("SELECT {$columns_concat} FROM {$table} WHERE {$where} ORDER BY {$columns_concat}", $values);
- // Return results as assoc or empty array
- return parent::is_mysqli_result($rows) ? $rows->fetch_all(MYSQLI_ASSOC) : [];
- }
-
- // Search work table
- private function search_work(): array {
- $search = [
- WorkModel::TITLE->value,
- WorkModel::SUMMARY->value,
- WorkModel::DATE_TIMESTAMP_CREATED->value,
- WorkModel::ID->value
- ];
-
- $conditions = [
- WorkModel::IS_LISTABLE->value => true
- ];
-
- $results = $this->search(WorkModel::TABLE, $search, $conditions);
-
- foreach ($results as &$result) {
- $result["actions"] = (new Call(Endpoints::WORK_ACTIONS->value))
- ->params([WorkActionsModel::ANCHOR->value => $result[WorkModel::ID->value]])
- ->get()->output();
- }
-
- return $results;
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ private function search_work(): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::TITLE->value => $_GET[self::GET_QUERY],
+ WorkModel::SUMMARY->value => $_GET[self::GET_QUERY]
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Get search results for each category
- $categories = [
- WorkModel::TABLE => $this->search_work()
+ $results = [
+ Endpoints::WORK->value => $this->search_work()->output()
];
- // Count total number of results from all categories
- $total_num_results = 0;
- foreach (array_values($categories) as $results) {
- $total_num_results += count($results);
- }
+ // Calculate the total number of results from all searched endpoints
+ $num_results = array_sum(array_map(fn(array $result): int => count($result), array_values($results)));
- return new Response([
- "query" => $_GET[self::GET_QUERY],
- "results" => $categories,
- "total_num_results" => $total_num_results
- ]);
+ // Return 404 if no search results
+ return new Response($results, $num_results > 0 ? 200 : 404);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/DELETE.php b/api/endpoints/work/DELETE.php
index d197646..d67c0ce 100755
--- a/api/endpoints/work/DELETE.php
+++ b/api/endpoints/work/DELETE.php
@@ -1,15 +1,16 @@
ruleset = new Ruleset(strict: true);
- $this->ruleset->GET([
- (new Rules("id"))
- ->required()
+ $this->ruleset->POST([
+ (new Rules(WorkModel::ID->value))
->type(Type::STRING)
->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkModel::TITLE->value))
+ ->type(Type::STRING)
+ ->min(3)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkModel::SUMMARY->value))
+ ->type(Type::STRING)
+ ->min(1)
+ ->max(parent::MYSQL_TEXT_MAX_LENGTH),
+
+ (new Rules(WorkModel::IS_LISTABLE->value))
+ ->type(Type::BOOLEAN),
+
+ (new Rules(WorkModel::IS_READABLE->value))
+ ->type(Type::BOOLEAN),
+
+ (new Rules(WorkModel::DATE_MODIFIED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT),
+
+ (new Rules(WorkModel::DATE_CREATED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT)
]);
- }
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to delete work data, please try again later", 503);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Attempt to update the entity
- $update = $this->db->for(WorkModel::TABLE)
- ->where([WorkModel::ID->value => $_GET["id"]])
- ->update([
- WorkModel::IS_LISTABLE->value => false,
- WorkModel::IS_READABLE->value => false
- ]);
-
- return $update ? new Response($_GET["id"]) : $this->resp_database_error();
+ return $this->db->for(FieldsEnumsModel::TABLE)->delete($_POST) === true
+ ? new Response(RESP_DELETE_OK)
+ : new Response("Failed to delete work entity", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/GET.php b/api/endpoints/work/GET.php
index 3a2fe0e..457977e 100755
--- a/api/endpoints/work/GET.php
+++ b/api/endpoints/work/GET.php
@@ -1,136 +1,94 @@
ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
- (new Rules("id"))
+ (new Rules(WorkModel::ID->value))
->type(Type::STRING)
->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ->default(null)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkModel::TITLE->value))
+ ->type(Type::STRING)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkModel::SUMMARY->value))
+ ->type(Type::STRING)
+ ->max(parent::MYSQL_TEXT_MAX_LENGTH),
+
+ (new Rules(WorkModel::IS_LISTABLE->value))
+ ->type(Type::BOOLEAN)
+ ->default(true),
+
+ (new Rules(WorkModel::IS_READABLE->value))
+ ->type(Type::BOOLEAN)
+ ->default(true),
+
+ (new Rules(WorkModel::DATE_MODIFIED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT),
+
+ (new Rules(WorkModel::DATE_CREATED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT)
]);
- }
- // # Helper methods
-
- private function fetch_row_tags(string $id): array {
- $resp = $this->db->for(WorkTagsModel::TABLE)
- ->where([WorkTagsModel::ANCHOR->value => $id])
- ->select(WorkTagsModel::NAME->value);
-
- return parent::is_mysqli_result($resp) ? $resp->fetch_all(MYSQLI_ASSOC) : [];
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
- private function resp_item_details(string $id): Response {
- $resp = $this->db->for(WorkModel::TABLE)
- ->where([
- WorkModel::ID->value => $id,
- WorkModel::IS_READABLE->value => true
- ])
- ->limit(1)
- ->select([
- WorkModel::ID->value,
- WorkModel::TITLE->value,
- WorkModel::SUMMARY->value,
- WorkModel::COVER_SRCSET->value,
- WorkModel::DATE_YEAR->value,
- WorkModel::DATE_MONTH->value,
- WorkModel::DATE_DAY->value,
- WorkModel::DATE_TIMESTAMP_MODIFIED->value,
- WorkModel::DATE_TIMESTAMP_CREATED->value
- ]);
-
- // Bail out if something went wrong retrieving rows from the database
- if (!parent::is_mysqli_result($resp)) {
- return $this->resp_database_error();
- }
-
- return $resp->num_rows === 1
- ? new Response($resp->fetch_assoc())
- : new Response("No entity with id '{$id}' was found", 404);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
+ // Use copy of search paramters as filters
+ $filters = $_GET;
+
+ // Do a wildcard search on the title column if provided
+ if (array_key_exists(WorkModel::TITLE->value, $_GET)) {
+ $filters[WorkModel::TITLE->value] = [
+ "LIKE" => "%{$_GET[WorkModel::TITLE->value]}%"
+ ];
}
- // Return details about a specific item by id
- if (!empty($_GET["id"])) {
- return $this->resp_item_details($_GET["id"]);
+ // Do a wildcard search on the summary column if provided
+ if (array_key_exists(WorkModel::SUMMARY->value, $_GET)) {
+ $filters[WorkModel::SUMMARY->value] = [
+ "LIKE" => "%{$_GET[WorkModel::SUMMARY->value]}%"
+ ];
}
- $resp = $this->db->for(WorkModel::TABLE)
- ->where([WorkModel::IS_LISTABLE->value => true])
- ->order([WorkModel::DATE_TIMESTAMP_CREATED->value => "DESC"])
+ $response = $this->db->for(WorkModel::TABLE)
+ ->where($filters)
->select([
WorkModel::ID->value,
WorkModel::TITLE->value,
WorkModel::SUMMARY->value,
- WorkModel::COVER_SRCSET->value,
+ WorkModel::IS_LISTABLE->value,
+ WorkModel::IS_READABLE->value,
WorkModel::DATE_YEAR->value,
WorkModel::DATE_MONTH->value,
WorkModel::DATE_DAY->value,
- WorkModel::DATE_TIMESTAMP_MODIFIED->value,
- WorkModel::DATE_TIMESTAMP_CREATED->value
+ WorkModel::DATE_MODIFIED->value,
+ WorkModel::DATE_CREATED->value
]);
- // Bail out if something went wrong retrieving rows from the database
- if (!parent::is_mysqli_result($resp)) {
- return $this->resp_database_error();
- }
-
- // Resolve foreign keys
- $rows = [];
- while ($row = $resp->fetch_assoc()) {
- $row["tags"] = $this->fetch_row_tags($row["id"]);
-
- // Fetch actions for work entity by id from endpoint
- $row["actions"] = (new Call(Endpoints::WORK_ACTIONS->value))
- ->params([WorkActionsModel::ANCHOR->value => $row[WorkModel::ID->value]])
- ->get()->output();
-
- $rows[] = $row;
- }
-
- return new Response($rows);
+ return $response->num_rows > 0
+ ? new Response($response->fetch_all(MYSQLI_ASSOC))
+ : new Response([], 404);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/PATCH.php b/api/endpoints/work/PATCH.php
index e8e9be1..ef2c1ca 100755
--- a/api/endpoints/work/PATCH.php
+++ b/api/endpoints/work/PATCH.php
@@ -1,30 +1,28 @@
ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
@@ -52,19 +50,19 @@
(new Rules(WorkModel::IS_READABLE->value))
->type(Type::BOOLEAN),
- (new Rules(WorkModel::DATE_TIMESTAMP_CREATED->value))
+ (new Rules(WorkModel::DATE_MODIFIED->value))
->type(Type::NUMBER)
- ->min(0)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT)
+ ->default(time()),
+
+ (new Rules(WorkModel::DATE_CREATED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
->max(parent::MYSQL_INT_MAX_LENGHT)
]);
- $this->get_existing_entity();
-
- // Copy all provided post data into a new array
- $this->updated_entity = $_POST;
-
- // Set date modified timestamp
- $this->updated_entity[WorkModel::DATE_TIMESTAMP_MODIFIED->value] = time();
+ parent::__construct();
}
// Generate a slug URL from string
@@ -72,130 +70,46 @@
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
}
- // # Helper methods
-
- private function get_existing_entity(): Response {
- // Check if an entity already exists with slugified title from GET endpoint
- $this->current_entity = Call("work?id={$_GET["id"]}", Method::GET);
-
- // Response is not 404 (Not found) so we can't create the entity
- if ($this->current_entity->code !== 200) {
- // Response is not a valid entity, something went wrong
- if ($this->current_entity->code !== 404) {
- return $this->resp_database_error();
- }
-
- // Return 402 Conflict
- return new Response("No entity with id '{$_GET["id"]}' was found", 404);
- }
-
- return $this->current_entity;
+ // Compute and return modeled year, month, and day from Unix timestamp in request body
+ private static function gen_date_created(): array {
+ return [
+ WorkModel::DATE_YEAR->value => date("Y", $_POST[WorkModel::DATE_CREATED->value]),
+ WorkModel::DATE_MONTH ->value => date("n", $_POST[WorkModel::DATE_CREATED->value]),
+ WorkModel::DATE_DAY->value => date("j", $_POST[WorkModel::DATE_CREATED->value])
+ ];
}
- // Create new permalink for entity slug
- private function create_permalink(string $slug): bool {
- $create = Call("work/permalinks", Method::POST, [
- WorkPermalinksModel::SLUG->value => $slug,
- WorkPermalinksModel::ANCHOR->value => $slug
- ]);
-
- return $create->ok;
- }
-
- // ## Updated entity
-
- private function change_slug(): bool {
- if (!array_key_exists(WorkModel::ID->value, $this->updated_entity)) {
- return true;
- }
-
- // Generate new permalink for entity id
- return $this->create_permalink($this->updated_entity[WorkModel::ID->value]);
- }
-
- private function timestamp_to_dates(): void {
- if (!array_key_exists(WorkModel::DATE_TIMESTAMP_CREATED->value, $this->updated_entity)) {
- return;
- }
-
- // Get timestamp from post data
- $timestamp = $this->updated_entity[WorkModel::DATE_TIMESTAMP_CREATED->value];
-
- // Update fractured dates from timestamp
- $this->updated_entity[WorkModel::DATE_YEAR->value] = date("Y", $timestamp);
- $this->updated_entity[WorkModel::DATE_MONTH ->value] = date("n", $timestamp);
- $this->updated_entity[WorkModel::DATE_DAY->value] = date("j", $timestamp);
- }
-
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
- // Return a 422 Unprocessable Entity if there is nothing to change
- private function resp_no_changes(): Response {
- return new Response("No columns to update", 422);
- }
-
- // Rollback changes and return error response
- private function resp_permalink_error_rollback(): Response {
- $update = $this->db->for(WorkModel::TABLE)
- ->where([WorkModel::ID->value => $_GET["id"]])
- ->update($this->current_entity->output());
-
- return $update
- ? new Response("Failed to create new permalink for updated entity. Changes have been rolled back", 500)
- : new Reponse("Failed to create new permalink for updated entity. Changes failed to rollback, this is bad.", 500);
+ private function get_entity_by_id(string $id): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::ID->value => $id
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
+ // Use copy of request body as entity
+ $entity = $_POST;
- // Empty payload, nothing to do
- if (empty($_POST)) {
- return $this->resp_no_changes();
- }
+ // Generate a new slug id from title if changed
+ if ($_POST[WorkModel::TITLE->value]) {
+ $slug = $_POST[WorkModel::TITLE->value];
- // Generate new slug for entity if title is updated
- if (array_key_exists(WorkModel::TITLE->value, $_POST)) {
- // Generate URL slug from title text or UUID if undefined
- $slug = self::gen_slug($_POST["title"]);
-
- // Save generated slug from title if it's different from existing slug
- if ($slug !== $this->current_entity->output()[WorkModel::ID->value]) {
- $this->updated_entity[WorkModel::ID->value] = $slug;
+ // Bail out if the slug generated from the new tite already exist
+ if ($this->get_entity_by_id($slug)) {
+ return new Response("An entity with this title already exist", 409);
}
+
+ // Add the new slug to update entity
+ $entity[WorkModel::ID] = $slug;
}
- // Update fractured dates from timestamp
- $this->timestamp_to_dates();
-
- // Attempt to update the entity
- $update = $this->db->for(WorkModel::TABLE)
- ->where([WorkModel::ID->value => $_GET["id"]])
- ->update($this->updated_entity);
-
- // Bail out if update failed
- if (!$update) {
- return $this->resp_database_error();
- }
-
- // Create new slug for entity if title was changed
- if (!$this->change_slug()) {
- return $this->resp_permalink_error_rollback();
+ // Generate new work date fields from timestamp
+ if ($_POST[WorkModel::DATE_CREATED->value]) {
+ array_merge($entity, self::gen_date_created());
}
- // Return 200 OK and new or existing entity slug as body
- return new Response($this->current_entity->output()[WorkModel::ID->value]);
+ // Update entity by existing id
+ return $this->db->for(WorkModel::TABLE)->where([WorkModel::ID->value => $_GET[WorkModel::ID->value]])->update($entity) === true
+ ? new Response($_GET[WorkModel::ID->value])
+ : new Response("Failed to update entity", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/POST.php b/api/endpoints/work/POST.php
index 90d0f0c..143cc4e 100755
--- a/api/endpoints/work/POST.php
+++ b/api/endpoints/work/POST.php
@@ -1,27 +1,28 @@
ruleset = new Ruleset(strict: true);
$this->ruleset->POST([
@@ -37,12 +38,22 @@
->max(parent::MYSQL_TEXT_MAX_LENGTH)
->default(null),
- (new Rules(WorkModel::DATE_TIMESTAMP_CREATED->value))
+ (new Rules(WorkModel::IS_LISTABLE->value))
+ ->type(Type::BOOLEAN)
+ ->default(false),
+
+ (new Rules(WorkModel::IS_READABLE->value))
+ ->type(Type::BOOLEAN)
+ ->default(false),
+
+ (new Rules(WorkModel::DATE_CREATED->value))
->type(Type::NUMBER)
->min(1)
->max(parent::MYSQL_INT_MAX_LENGHT)
- ->default(null)
+ ->default(time())
]);
+
+ parent::__construct($this->ruleset);
}
// Generate a slug URL from string
@@ -50,84 +61,51 @@
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
}
- // Create permalink for entity slug
- private function create_permalink(string $slug): bool {
- $create = Call("work/permalinks", Method::POST, [
- WorkPermalinksModel::SLUG->value => $slug,
- WorkPermalinksModel::ANCHOR->value => $slug
- ]);
+ // Compute and return modeled year, month, and day from a Unix timestamp
+ private static function gen_date_created(): array {
+ // Use provided timestamp in request
+ $date_created = $_POST[WorkModel::DATE_CREATED->value];
- return $create->ok;
+ return [
+ WorkModel::DATE_YEAR->value => date("Y", $date_created),
+ WorkModel::DATE_MONTH ->value => date("n", $date_created),
+ WorkModel::DATE_DAY->value => date("j", $date_created)
+ ];
}
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ private function get_entity_by_id(string $id): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::ID->value => $id
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
+ // Use copy of request body as entity
+ $entity = $_POST;
// Generate URL slug from title text or UUID if undefined
- $slug = !empty($_POST["title"]) ? self::gen_slug($_POST["title"]) : parent::gen_uuid4();
+ $entity[WorkModel::ID->value] = $_POST[WorkModel::TITLE->value]
+ ? self::gen_slug($_POST[WorkModel::TITLE->value])
+ : parent::gen_uuid4();
- // Check if an entity already exists with slugified title from GET endpoint
- $existing_entity = Call("work?id={$slug}", Method::GET);
- // Response is not 404 (Not found) so we can't create the entity
- if ($existing_entity->code !== 404) {
- // Response is not a valid entity, something went wrong
- if ($existing_entity->code !== 200) {
- return $this->resp_database_error();
- }
-
- // Return 402 Conflict
- return new Response("Entity with id '{$slug}' already exists", 402);
+ // Bail out here if a work entry with id had been created already
+ if ($this->get_entity_by_id($entity[WorkModel::ID->value])->ok) {
+ return new Response("An entity with id '{$slug}' already exist", 409);
}
- // Get created timestamp from payload or use current time if not specified
- $created_timestamp = $_POST[WorkModel::DATE_TIMESTAMP_CREATED->value]
- ? $_POST[WorkModel::DATE_TIMESTAMP_CREATED->value]
- : time();
+ // Generate the necessary date fields
+ array_merge($entity, self::gen_date_created());
- // Attempt to create new entity
- $insert = $this->db->for(WorkModel::TABLE)
- ->insert([
- WorkModel::ID->value => $slug,
- WorkModel::TITLE->value => $_POST["title"],
- WorkModel::SUMMARY->value => $_POST["summary"],
- WorkModel::IS_LISTABLE->value => true,
- WorkModel::IS_READABLE->value => true,
- WorkModel::DATE_YEAR->value => date("Y", $created_timestamp),
- WorkModel::DATE_MONTH ->value => date("n", $created_timestamp),
- WorkModel::DATE_DAY->value => date("j", $created_timestamp),
- WorkModel::DATE_TIMESTAMP_MODIFIED->value => null,
- WorkModel::DATE_TIMESTAMP_CREATED->value => $created_timestamp,
- ]);
-
- // Bail out if insert failed
- if (!$insert) {
- return $this->resp_database_error();
+ // Let's try to insert the new entity
+ if (!$this->db->for(WorkModel::TABLE)->insert($entity)) {
+ return new Response("Failed to insert work entry", 500);
}
- // Create permalink for new entity
- if (!$this->create_permalink($slug)) {
- // Rollback created entity if permalink creation failed
- Call("work", Method::DELETE, [WorkModel::ID->value => $slug]);
-
- return new Response("Failed to create permalink", 500);
- }
-
- // Return 201 Created and entity slug as body
- return new Response($slug, 201);
+ // Generate permalink for new entity
+ return (new Call(Endpoints::WORK_PERMALINKS->value))->post([
+ WorkPermalinksModel::ID => $entity[WorkModel::ID->value],
+ WorkPermalinksModel::REF_WORK_ID => $entity[WorkModel::ID->value],
+ WorkPermalinksModel::DATE_CREATED => time()
+ ]);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/actions/DELETE.php b/api/endpoints/work/actions/DELETE.php
index 2eb1e82..b64194b 100755
--- a/api/endpoints/work/actions/DELETE.php
+++ b/api/endpoints/work/actions/DELETE.php
@@ -6,9 +6,7 @@
use ReflectRules\Rules;
use ReflectRules\Ruleset;
- use Reflect\Method;
- use function Reflect\Call;
-
+ use const VLW\API\RESP_DELETE_OK;
use VLW\API\Databases\VLWdb\VLWdb;
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
@@ -23,51 +21,15 @@
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->POST([
- (new Rules("id"))
- ->required()
- ->type(Type::STRING)
+ (new Rules(WorkActionsModel::REF_WORK_ID->value))
->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
]);
}
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
- }
-
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Ensure the action exists by id
- $existing_action = $this->db->for(WorkActionsModel::TABLE)
- ->where([
- WorkActionsModel::ID->value => $_POST["id"]
- ])
- ->select(null);
-
- // Return idempotent deletion if the action does not exist
- if ($existing_action->num_rows === 0) {
- return new Response($_POST["id"]);
- }
-
- // Attempt to delete action by id
- $delete = $this->db->for(WorkActionsModel::TABLE)
- ->delete([
- WorkActionsModel::ID->value => $_POST["id"]
- ]);
-
- // Return 201 Created and entity id as body if insert was successful
- return $delete === true ? new Response($_POST["id"], 201) : $this->resp_database_error();
+ return $this->db->for(WorkActionsModel::TABLE)->delete($_POST) === true
+ ? new Response(RESP_DELETE_OK)
+ : new Response("Failed to delete action for work entity", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/actions/GET.php b/api/endpoints/work/actions/GET.php
index 616dff9..ccbdb69 100644
--- a/api/endpoints/work/actions/GET.php
+++ b/api/endpoints/work/actions/GET.php
@@ -10,58 +10,37 @@
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
require_once Path::root("src/databases/VLWdb.php");
- require_once Path::root("src/databases/models/WorkActions.php");
+ require_once Path::root("src/databases/models/Work/WorkActions.php");
class GET_WorkActions extends VLWdb {
protected Ruleset $ruleset;
public function __construct() {
- parent::__construct();
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
- (new Rules(WorkActionsModel::ANCHOR->value))
- ->required()
+ (new Rules(WorkActionsModel::REF_WORK_ID->value))
->type(Type::STRING)
->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
]);
- }
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- $resp = $this->db->for(WorkActionsModel::TABLE)
- ->where([WorkActionsModel::ANCHOR->value => $_GET[WorkActionsModel::ANCHOR->value]])
+ $response = $this->db->for(WorkActionsModel::TABLE)
+ ->where($_GET)
->select([
+ WorkActionsModel::REF_WORK_ID->value,
WorkActionsModel::DISPLAY_TEXT->value,
WorkActionsModel::HREF->value,
WorkActionsModel::CLASS_LIST->value,
WorkActionsModel::EXTERNAL->value
]);
- // Bail out if something went wrong retrieving rows from the database
- if (!parent::is_mysqli_result($resp)) {
- return $this->resp_database_error();
- }
-
- return $resp->num_rows > 0
- ? new Response($resp->fetch_all(MYSQLI_ASSOC))
- : new Response([]);
+ return $response->num_rows > 0
+ ? new Response($response->fetch_all(MYSQLI_ASSOC))
+ : new Response([], 404);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/actions/POST.php b/api/endpoints/work/actions/POST.php
index acca855..5587ddc 100755
--- a/api/endpoints/work/actions/POST.php
+++ b/api/endpoints/work/actions/POST.php
@@ -1,36 +1,36 @@
ruleset = new Ruleset(strict: true);
- $this->ruleset->GET([
- (new Rules("id"))
- ->required()
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ]);
-
$this->ruleset->POST([
+ (new Rules(WorkActionsModel::REF_WORK_ID->value))
+ ->required()
+ ->min(1)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
(new Rules(WorkActionsModel::DISPLAY_TEXT->value))
->required()
->type(Type::STRING)
@@ -47,56 +47,31 @@
(new Rules(WorkActionsModel::CLASS_LIST->value))
->type(Type::ARRAY)
->min(1)
- ->max(4)
->default([]),
(new Rules(WorkActionsModel::EXTERNAL->value))
->type(Type::BOOLEAN)
->default(false)
]);
+
+ parent::__construct($this->ruleset);
}
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ private static function get_entity(): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::ID->value => $_POST[WorkActionsModel::REF_WORK_ID->value]
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
+ // Bail out if work entity could not be fetched
+ $entity = self::get_entity();
+ if (!$entity->ok) {
+ return $entity;
}
- // Ensure an entity with the provided id exists
- $entity = Call("work?id={$_GET["id"]}", Method::GET);
- if ($entity->code !== 200) {
- // Response from endpoint is not 404, something went wrong
- if ($entity->code !== 404) {
- return $this->resp_database_error();
- }
-
- return new Response("No entity with id '{$_GET["id"]}' was found", 404);
- }
-
- // Attempt to create action for entity
- $insert = $this->db->for(WorkActionsModel::TABLE)
- ->insert([
- WorkActionsModel::ID->value => parent::gen_uuid4(),
- WorkActionsModel::ANCHOR->value => $_GET["id"],
- WorkActionsModel::DISPLAY_TEXT->value => $_POST[WorkActionsModel::DISPLAY_TEXT->value],
- WorkActionsModel::HREF->value => $_POST[WorkActionsModel::HREF->value],
- WorkActionsModel::CLASS_LIST->value => implode(",", $_POST[WorkActionsModel::CLASS_LIST->value]),
- WorkActionsModel::EXTERNAL->value => $_POST[WorkActionsModel::EXTERNAL->value],
- ]);
-
- // Return 201 Created and entity id as body if insert was successful
- return $insert === true ? new Response($_GET["id"], 201) : $this->resp_database_error();
+ return $this->db->for(WorkActionsModel::TABLE)->insert($_POST) === true
+ ? new Response($_POST[WorkActionsModel::REF_WORK_ID->value], 201)
+ : new Response("Failed to add action to work entity", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/permalinks/GET.php b/api/endpoints/work/permalinks/GET.php
index 9fe437b..4c35b6f 100755
--- a/api/endpoints/work/permalinks/GET.php
+++ b/api/endpoints/work/permalinks/GET.php
@@ -1,6 +1,5 @@
ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
- (new Rules("id"))
- ->required()
+ (new Rules(WorkPermalinksModel::ID->value))
+ ->type(Type::STRING)
+ ->min(1)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkPermalinksModel::REF_WORK_ID->value))
->type(Type::STRING)
->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
]);
- }
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to resolve permalink, please try again later", 503);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
+ $response = $this->db->for(WorkPermalinksModel::TABLE)
+ ->where($_GET)
+ ->select([
+ WorkPermalinksModel::ID->value,
+ WorkPermalinksModel::REF_WORK_ID->value,
+ WorkPermalinksModel::DATE_CREATED->value
+ ]);
- // Get all anchors that match the requested slug
- $resolve = $this->db->for(WorkPermalinksModel::TABLE)
- ->where([WorkPermalinksModel::SLUG->value => $_GET["id"]])
- ->select(WorkPermalinksModel::ANCHOR->value);
-
- // Return array of all matched work table ids. Or empty array if none found
- return parent::is_mysqli_result($resolve)
- ? new Response(array_column($resolve->fetch_all(MYSQLI_ASSOC), WorkPermalinksModel::ANCHOR->value))
- : $this->resp_database_error();
+ return $response->num_rows > 0
+ ? new Response($response->fetch_all(MYSQLI_ASSOC))
+ : new Response([], 404);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/permalinks/POST.php b/api/endpoints/work/permalinks/POST.php
index 6425316..2901217 100755
--- a/api/endpoints/work/permalinks/POST.php
+++ b/api/endpoints/work/permalinks/POST.php
@@ -1,83 +1,62 @@
ruleset = new Ruleset(strict: true);
$this->ruleset->POST([
- (new Rules("slug"))
+ (new Rules(WorkPermalinksModel::ID->value))
->required()
->type(Type::STRING)
->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
- (new Rules("anchor"))
+ (new Rules(WorkPermalinksModel::REF_WORK_ID->value))
->required()
->type(Type::STRING)
->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkPermalinksModel::DATE_CREATED->value))
+ ->type(Type::NUMBER)
+ ->min(1)
+ ->max(parent::MYSQL_INT_MAX_LENGHT)
+ ->default(time())
]);
+
+ parent::__construct($this->ruleset);
}
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to resolve permalink, please try again later", 503);
+ private static function get_entity(): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
+ // Bail out if work entity could not be fetched
+ $entity = self::get_entity();
+ if (!$entity->ok) {
+ return $entity;
}
- // Check if an entity exists with slug
- $existing_entity = Call("work?id={$_POST["slug"]}", Method::GET);
- // Response is not 404 (Not found) so we can't create the entity
- if ($existing_entity->code !== 200) {
- // Response is not a valid entity, something went wrong
- if ($existing_entity->code !== 404) {
- return $this->resp_database_error();
- }
-
- // Return 402 Conflict
- return new Response("No work entity with id '{$_POST["slug"]}' was found to permalink", 404);
- }
-
- // Attempt to create new entity
- $insert = $this->db->for(WorkPermalinksModel::TABLE)
- ->insert([
- WorkPermalinksModel::SLUG->value => $_POST["slug"],
- WorkPermalinksModel::ANCHOR->value => $_POST["anchor"],
- WorkPermalinksModel::DATE_TIMESTAMP_CREATED->value => time(),
- ]);
-
- // Return 201 Created and entity slug as body if insert was successful
- return $insert === true ? new Response($_POST["slug"], 201) : $this->resp_database_error();
+ return $this->db->for(WorkPermalinksModel::TABLE)->insert($_POST) === true
+ ? new Response($_POST[WorkPermalinksModel::ID->value], 201)
+ : new Response("Failed to add permalink to work entity", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/tags/DELETE.php b/api/endpoints/work/tags/DELETE.php
index f674469..2699806 100755
--- a/api/endpoints/work/tags/DELETE.php
+++ b/api/endpoints/work/tags/DELETE.php
@@ -6,75 +6,34 @@
use ReflectRules\Rules;
use ReflectRules\Ruleset;
- use Reflect\Method;
- use function Reflect\Call;
-
+ use const VLW\API\RESP_DELETE_OK;
use VLW\API\Databases\VLWdb\VLWdb;
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
- use VLW\API\Databases\VLWdb\Models\Work\WorkTagsNameEnum;
require_once Path::root("src/databases/VLWdb.php");
- require_once Path::root("src/databases/models/WorkTags.php");
+ require_once Path::root("src/databases/models/Work/WorkTags.php");
class DELETE_WorkTags extends VLWdb {
- protected Ruleset $ruleset;
+ private Ruleset $ruleset;
public function __construct() {
- parent::__construct();
$this->ruleset = new Ruleset(strict: true);
- $this->ruleset->POST([
- (new Rules("id"))
- ->required()
- ->type(Type::STRING)
+ $this->ruleset->GET([
+ (new Rules(WorkTagsModel::REF_WORK_ID->value))
->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
-
+
(new Rules(WorkTagsModel::NAME->value))
- ->required()
->type(Type::ENUM, WorkTagsNameEnum::names())
]);
- }
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ parent::__construct($this->ruleset);
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
- }
-
- // Ensure the tag exists for entity id
- $existing_tag = $this->db->for(WorkTagsModel::TABLE)
- ->where([
- WorkTagsModel::ANCHOR->value => $_POST["id"],
- WorkTagsModel::NAME->value => $_POST["name"]
- ])
- ->select(null);
-
- // Return idempotent deletion if the tag does not exist
- if ($existing_tag->num_rows === 0) {
- return new Response($_POST["id"]);
- }
-
- // Attempt to delete tag for entity
- $delete = $this->db->for(WorkTagsModel::TABLE)
- ->delete([
- WorkTagsModel::ANCHOR->value => $_POST["id"],
- WorkTagsModel::NAME->value => $_POST["name"]
- ]);
-
- // Return 201 Created and entity id as body if insert was successful
- return $delete === true ? new Response($_POST["id"], 201) : $this->resp_database_error();
+ return $this->db->for(WorkTagsModel::TABLE)->delete($_POST) === true
+ ? new Response(RESP_DELETE_OK)
+ : new Response("Failed to delete value from document", 500);
}
}
\ No newline at end of file
diff --git a/api/endpoints/work/tags/GET.php b/api/endpoints/work/tags/GET.php
new file mode 100644
index 0000000..7b6841b
--- /dev/null
+++ b/api/endpoints/work/tags/GET.php
@@ -0,0 +1,48 @@
+ruleset = new Ruleset(strict: true);
+
+ $this->ruleset->GET([
+ (new Rules(WorkTagsModel::REF_WORK_ID->value))
+ ->min(1)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
+ (new Rules(WorkTagsModel::NAME->value))
+ ->type(Type::ENUM, WorkTagsNameEnum::names())
+ ]);
+
+ parent::__construct($this->ruleset);
+ }
+
+ public function main(): Response {
+ $response = $this->db->for(WorkTagsModel::TABLE)
+ ->where($_GET)
+ ->select([
+ WorkTagsModel::REF_WORK_ID->value,
+ WorkTagsModel::NAME->value
+ ]);
+
+ return $response->num_rows > 0
+ ? new Response($response->fetch_all(MYSQLI_ASSOC))
+ : new Response([], 404);
+ }
+ }
\ No newline at end of file
diff --git a/api/endpoints/work/tags/POST.php b/api/endpoints/work/tags/POST.php
index d78c03c..a99205f 100755
--- a/api/endpoints/work/tags/POST.php
+++ b/api/endpoints/work/tags/POST.php
@@ -1,93 +1,60 @@
ruleset = new Ruleset(strict: true);
- $this->ruleset->GET([
- (new Rules("id"))
- ->required()
- ->type(Type::STRING)
- ->min(1)
- ->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
- ]);
-
$this->ruleset->POST([
+ (new Rules(WorkTagsModel::REF_WORK_ID->value))
+ ->required()
+ ->min(1)
+ ->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
+
(new Rules(WorkTagsModel::NAME->value))
->required()
->type(Type::ENUM, WorkTagsNameEnum::names())
]);
+
+ parent::__construct($this->ruleset);
}
- // # Responses
-
- // Return 422 Unprocessable Content error if request validation failed
- private function resp_rules_invalid(): Response {
- return new Response($this->ruleset->get_errors(), 422);
- }
-
- // Return a 503 Service Unavailable error if something went wrong with the database call
- private function resp_database_error(): Response {
- return new Response("Failed to get work data, please try again later", 503);
+ private static function get_entity(): Response {
+ return (new Call(Endpoints::WORK->value))->params([
+ WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
+ ])->get();
}
public function main(): Response {
- // Bail out if request validation failed
- if (!$this->ruleset->is_valid()) {
- return $this->resp_rules_invalid();
+ // Bail out if work entity could not be fetched
+ $entity = self::get_entity();
+ if (!$entity->ok) {
+ return $entity;
}
- // Ensure an entity with the provided id exists
- $entity = Call("work?id={$_GET["id"]}", Method::GET);
- if ($entity->code !== 200) {
- // Response from endpoint is not 404, something went wrong
- if ($entity->code !== 404) {
- return $this->resp_database_error();
- }
-
- return new Response("No entity with id '{$_GET["id"]}' was found", 404);
- }
-
- // Ensure the tag does not already exist for entity
- $existing_tag = $this->db->for(WorkTagsModel::TABLE)
- ->where([
- WorkTagsModel::ANCHOR->value => $_GET["id"],
- WorkTagsModel::NAME->value => $_POST["name"]
- ])
- ->select(null);
-
- // Bail out if this tag already exists
- if ($existing_tag->num_rows !== 0) {
- return new Response("Tag '{$_POST["name"]}' is already set on entity id '{$_GET["id"]}'", 402);
- }
-
- // Attempt to create tag for entity
- $insert = $this->db->for(WorkTagsModel::TABLE)
- ->insert([
- WorkTagsModel::ANCHOR->value => $_GET["id"],
- WorkTagsModel::NAME->value => $_POST["name"]
- ]);
-
- // Return 201 Created and entity id as body if insert was successful
- return $insert === true ? new Response($_GET["id"], 201) : $this->resp_database_error();
+ return $this->db->for(WorkTagsModel::TABLE)->insert($_POST) === true
+ ? new Response($_POST[WorkTagsModel::REF_WORK_ID->value], 201)
+ : new Response("Failed to add tag to work entity", 500);
}
}
\ No newline at end of file
diff --git a/api/src/Endpoints.php b/api/src/Endpoints.php
new file mode 100644
index 0000000..cb6bb87
--- /dev/null
+++ b/api/src/Endpoints.php
@@ -0,0 +1,17 @@
+db = new MySQL(
$_ENV["vlwdb"]["mariadb_host"],
@@ -46,7 +55,8 @@
);
}
- public static function is_mysqli_result(\mysqli_result|bool $resp): bool {
- return $resp instanceof \mysqli_result;
+ // Bail out if provided ReflectRules\Ruleset is invalid
+ private static function eval_ruleset_or_exit(Ruleset $ruleset): ?Response {
+ return !$ruleset->is_valid() ? new Response($ruleset->get_errors(), 422) : null;
}
}
\ No newline at end of file
diff --git a/api/src/databases/models/Coffee.php b/api/src/databases/models/Coffee.php
deleted file mode 100755
index 0d4377c..0000000
--- a/api/src/databases/models/Coffee.php
+++ /dev/null
@@ -1,10 +0,0 @@
-
@@ -50,8 +54,8 @@
// Send message via API
$send = $api->call(Endpoints::MESSAGES->value)->post([
- ContactFieldsEnum::EMAIL->value => $_POST[ContactFieldsEnum::EMAIL->value],
- ContactFieldsEnum::MESSAGE->value => $_POST[ContactFieldsEnum::MESSAGE->value]
+ MessagesModel::EMAIL->value => $_POST[MessagesModel::EMAIL->value],
+ MessagesModel::MESSAGE->value => $_POST[MessagesModel::MESSAGE->value]
]);
?>
@@ -74,11 +78,11 @@
diff --git a/pages/search.php b/pages/search.php
index 068d8a5..9e2ca7b 100755
--- a/pages/search.php
+++ b/pages/search.php
@@ -1,24 +1,36 @@
call(Endpoints::SEARCH->value)
- // Get query string from search parameter if set
- ->params(["q" => $query])
- ->get();
+ $response = $api->call(Endpoints::SEARCH->value)->params([SEARCH_PARAM => $_GET[SEARCH_PARAM]])->get();
?>
-
+ok): ?>
- json(); ?>
+ json(); ?>
-
- code): default: ?>
-
-
-
+
+ value]): ?>
+
+ Work
+ = count($results[Endpoints::WORK->value]) ?> search result(s) from my public work
+
+
-
-
+
+ value] as $result): ?>
+
+
= $result[WorkModel::TITLE->value] ?>
+
= $result[WorkModel::SUMMARY->value] ?>
+
= date(API::DATE_FORMAT, $result[WorkModel::DATE_CREATED->value]) ?>
-
- 0): ?>
-
-
+
+ call(Endpoints::WORK_ACTIONS->value)->params([WorkActionsModel::REF_WORK_ID->value => $result[WorkModel::ID->value]])->get(); ?>
-
-
-
- Work
- = count($categories["work"]) ?> search result(s) from my public work
-
-
-
-
-
-
-
= $result["title"] ?>
-
= $result["summary"] ?>
-
= date(Client::DATE_FORMAT, $result["date_timestamp_created"]) ?>
-
-
-
-
+
+ ok): ?>
+
-
-
-
-
-
-
-
-
-
- No results for search term "= $_GET["q"] ?>"
-
-
-
-
-
-
-
-
- Connection to VLW API was successful but lacking permission to search
-
-
-
-
-
-
-
- $error_msg): ?>
-
-
-
-
- Unknown request validation error
-
-
-
-
-
-
- = VV::media("icons/search.svg") ?>
- type at least = $error_msg ?> characters to search!
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
- = VV::media("icons/search.svg") ?>
+
+
+
+
+ No results for search term "= $_GET[SEARCH_PARAM] ?>"
+
+
+
+ = VV::media("icons/search.svg") ?>
+ Start typing to search
+
+
+
\ No newline at end of file
diff --git a/pages/work.php b/pages/work.php
index c8c6930..5195839 100755
--- a/pages/work.php
+++ b/pages/work.php
@@ -1,13 +1,34 @@
call(Endpoints::WORK->value)->get();
+ require_once Path::root("src/client/API.php");
+ require_once Path::root("api/src/Endpoints.php");
+
+ require_once Path::root("api/src/databases/models/Work/Work.php");
+ require_once Path::root("api/src/databases/models/Work/WorkTags.php");
+ require_once Path::root("api/src/databases/models/Work/WorkActions.php");
+
+ // Connect to VLW API
+ $api = new API();
+
+ // Retreive rows from work endpoints
+ $resp_work = $api->call(Endpoints::WORK->value)->get();
+
+ // Resolve tags and actions if we got work results
+ if ($resp_work->ok) {
+ $work_tags = $api->call(Endpoints::WORK_TAGS->value)->get()->json();
+ $work_actions = $api->call(Endpoints::WORK_ACTIONS->value)->get()->json();
+ }
?>
@@ -21,7 +42,7 @@
-ok): ?>
+ok): ?>
json() as $row) {
+ foreach ($resp_work->json() as $row) {
// Create array for current year if it doesn't exist
- if (!array_key_exists($row["date_year"], $rows)) {
- $rows[$row["date_year"]] = [];
+ if (!array_key_exists($row[WorkModel::DATE_YEAR->value], $rows)) {
+ $rows[$row[WorkModel::DATE_YEAR->value]] = [];
}
// Create array for current month if it doesn't exist
- if (!array_key_exists($row["date_month"], $rows[$row["date_year"]])) {
- $rows[$row["date_year"]][$row["date_month"]] = [];
+ if (!array_key_exists($row[WorkModel::DATE_MONTH->value], $rows[$row[WorkModel::DATE_YEAR->value]])) {
+ $rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]] = [];
}
// Create array for current day if it doesn't exist
- if (!array_key_exists($row["date_day"], $rows[$row["date_year"]][$row["date_month"]])) {
- $rows[$row["date_year"]][$row["date_month"]][$row["date_day"]] = [];
+ if (!array_key_exists($row[WorkModel::DATE_DAY->value], $rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]])) {
+ $rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]] = [];
}
// Append item to ordered array
- $rows[$row["date_year"]][$row["date_month"]][$row["date_day"]][] = $row;
+ $rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]][] = $row;
}
?>
@@ -84,52 +105,50 @@
-
-
+
+ value), $item[WorkModel::ID->value]); ?>
+
+
+
-
-
= $item["title"] ?>
+ value])): ?>
+
= $item[WorkModel::TITLE->value] ?>
-
-
-
+ = $item[WorkModel::SUMMARY->value] ?>
-
-
-
-
-
- .= $srcset["extension"] ?>" type="= $srcset["mime"] ?>">
-
-
-
-
.= $default["extension"] ?>" type="= $default["mime"] ?>" loading="lazy"/>
-
-
-
-
= $item["summary"] ?>
+
+ value), $item[WorkModel::ID->value]); ?>
-
+
-
+
value]
+ // Bind VV Interactions for local links
+ ? "vv='work' vv-call='navigate'"
+ // Open external links in a new tab
+ : "target='_blank'";
+
+ $link_href = $action[WorkActionsModel::HREF->value] === null
+ // Navigate to work details page if no href is defined
+ ? "/work/{$item[WorkModel::ID->value]}"
+ // Href is defined so use it directly
+ : $action[WorkActionsModel::HREF->value];
?>
>
diff --git a/src/packages/API/src/Client.php b/src/client/API.php
similarity index 70%
rename from src/packages/API/src/Client.php
rename to src/client/API.php
index 2f5dd7d..bb314eb 100644
--- a/src/packages/API/src/Client.php
+++ b/src/client/API.php
@@ -1,10 +1,10 @@
id = $id;
+ $this->api = new Api();
+ $this->endpoint = $endpoint;
+
+ $this->resolve_entity_by_id();
+ }
+
+ private function resolve_entity_by_id() {
+ // Bail out wit a dummy Response if no id was provided
+ if (!$this->id) {
+ $this->response = new Response("", 404);
+ return;
+ }
+
+ $this->response = $this->api
+ ->call($this->endpoint->value)
+ ->params([self::ENTITY_ID => $this->id])
+ ->get();
+
+ // Load response into entity object if successful
+ if ($this->response->ok) {
+ $this->entity = (object) $this->response->json()[0];
+ }
+ }
+
+ public function resolve(Endpoints $endpoint, array $params): array {
+ $response = $this->api->call($endpoint->value)->params($params)->get();
+
+ return $response->ok ? $response->json() : [];
+ }
+ }
\ No newline at end of file
diff --git a/src/entities/VLW/Work.php b/src/entities/VLW/Work.php
new file mode 100644
index 0000000..5b4b26a
--- /dev/null
+++ b/src/entities/VLW/Work.php
@@ -0,0 +1,74 @@
+signatures) {
+ return $this->signatures;
+ }
+
+ foreach ($this->resolve(Endpoints::ICELDB_ANALYSES_SIGNATURES, ["ref_analysis_id" => $this->id]) as $rel) {
+ $this->signatures[$rel["id"]] = $rel;
+ $this->signatures[$rel["id"]]["user"] = $this->resolve(Endpoints::ICELDB_USERS, ["ref_user_id" => $rel["ref_user_id"]])[0];
+ }
+
+ return $this->signatures;
+ }
+
+ public function documents(): array {
+ if ($this->documents) {
+ return $this->documents;
+ }
+
+ $this->documents = $this->resolve(Endpoints::ICELDB_ANALYSES_DOCUMENTS, ["ref_analysis_id" => $this->id]);
+ return $this->documents;
+ }
+
+ public function studies(): array {
+ if ($this->studies) {
+ return $this->studies;
+ }
+
+ foreach ($this->resolve(Endpoints::ICELDB_STUDIES_ANALYSES, ["ref_analysis_id" => $this->id]) as $rel) {
+ $this->studies[] = $this->resolve(Endpoints::ICELDB_STUDIES, ["id" => $rel["ref_study_id"]]);
+ }
+
+ return $this->studies;
+ }
+
+ public function notes(): array {
+ if ($this->notes) {
+ return $this->notes;
+ }
+
+ $this->notes = $this->resolve(Endpoints::ICELDB_ANALYSES_NOTES, ["ref_analysis_id" => $this->id]);
+ return $this->notes;
+ }
+
+ public function individuals(): array {
+ if ($this->individuals) {
+ return $this->individuals;
+ }
+
+ foreach ($this->resolve(Endpoints::ICELDB_ANALYSES_INDIVIDUALS, ["ref_analysis_id" => $this->id]) as $rel) {
+ $this->individuals[] = $this->resolve(Endpoints::ICELDB_INDIVIDUALS, ["id" => $rel["ref_individual_id"]])[0];
+ }
+
+ return $this->individuals;
+ }
+ }
\ No newline at end of file
diff --git a/src/packages/API/composer.json b/src/packages/API/composer.json
deleted file mode 100644
index 2edfca6..0000000
--- a/src/packages/API/composer.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "name": "local/api.client",
- "description": "Wrapper for vlw.se API",
- "type": "library",
- "version": "1.0.0-dev",
- "authors": [
- {
- "name": "Victor Westerlund",
- "email": "victor@vlw.se"
- }
- ],
- "autoload": {
- "psr-4": {
- "VLW\\API\\": "src/"
- }
- },
- "require": {
- "reflect/client": "^3.0"
- }
-}
diff --git a/src/packages/API/composer.lock b/src/packages/API/composer.lock
deleted file mode 100644
index 2896728..0000000
--- a/src/packages/API/composer.lock
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
- "This file is @generated automatically"
- ],
- "content-hash": "ea73f1cfa968f06be9c2ac54d2a56c96",
- "packages": [
- {
- "name": "reflect/client",
- "version": "dev-feat/responseobj",
- "source": {
- "type": "git",
- "url": "https://github.com/VictorWesterlund/reflect-client-php.git",
- "reference": "228b3c665d4af5023ac4ed71fe32bb144521b43b"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/VictorWesterlund/reflect-client-php/zipball/228b3c665d4af5023ac4ed71fe32bb144521b43b",
- "reference": "228b3c665d4af5023ac4ed71fe32bb144521b43b",
- "shasum": ""
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Reflect\\": "src/Reflect/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "GPL-2.0-only"
- ],
- "authors": [
- {
- "name": "Victor Westerlund",
- "email": "victor.vesterlund@gmail.com"
- }
- ],
- "description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets",
- "support": {
- "issues": "https://github.com/VictorWesterlund/reflect-client-php/issues",
- "source": "https://github.com/VictorWesterlund/reflect-client-php/tree/feat/responseobj"
- },
- "time": "2024-03-18T11:57:57+00:00"
- }
- ],
- "packages-dev": [],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": {
- "reflect/client": 20
- },
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": [],
- "platform-dev": [],
- "plugin-api-version": "2.0.0"
-}