From 091aa46481dca609cf1508338bc6ff8f113c7312 Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Sun, 24 Nov 2024 13:49:20 +0100 Subject: [PATCH] wip: 2024-11-24T10:07:17+0100 (1732439237) --- api/endpoints/work/DELETE.php | 5 +- api/endpoints/work/GET.php | 10 +- api/endpoints/work/PATCH.php | 5 +- api/endpoints/work/POST.php | 6 +- api/endpoints/work/actions/GET.php | 5 +- api/endpoints/work/actions/POST.php | 6 +- api/endpoints/work/tags/GET.php | 2 +- api/src/databases/VLWdb.php | 24 +- api/src/databases/models/Work/Work.php | 22 +- api/src/databases/models/Work/WorkActions.php | 4 +- .../databases/models/Work/WorkNamespaces.php | 15 + api/src/databases/models/Work/WorkTags.php | 1 + public/search.php | 6 +- public/work.php | 49 +++- public/work/timeline.php | 262 +++++++++--------- 15 files changed, 236 insertions(+), 186 deletions(-) create mode 100644 api/src/databases/models/Work/WorkNamespaces.php diff --git a/api/endpoints/work/DELETE.php b/api/endpoints/work/DELETE.php index 47b5d29..2b5905a 100755 --- a/api/endpoints/work/DELETE.php +++ b/api/endpoints/work/DELETE.php @@ -39,10 +39,7 @@ ->min(1) ->max(parent::MYSQL_TEXT_MAX_LENGTH), - (new Rules(WorkModel::IS_LISTABLE->value)) - ->type(Type::BOOLEAN), - - (new Rules(WorkModel::IS_READABLE->value)) + (new Rules(WorkModel::IS_LISTED->value)) ->type(Type::BOOLEAN), (new Rules(WorkModel::DATE_MODIFIED->value)) diff --git a/api/endpoints/work/GET.php b/api/endpoints/work/GET.php index bb280d9..1feee10 100755 --- a/api/endpoints/work/GET.php +++ b/api/endpoints/work/GET.php @@ -37,11 +37,7 @@ ->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)) + (new Rules(WorkModel::IS_LISTED->value)) ->type(Type::BOOLEAN) ->default(true), @@ -93,10 +89,10 @@ ->limit($_GET[PARAM_LIMIT]) ->select([ WorkModel::ID->value, + WorkModel::REF_NAMESPACE_ID->value, WorkModel::TITLE->value, WorkModel::SUMMARY->value, - WorkModel::IS_LISTABLE->value, - WorkModel::IS_READABLE->value, + WorkModel::IS_LISTED->value, WorkModel::DATE_YEAR->value, WorkModel::DATE_MONTH->value, WorkModel::DATE_DAY->value, diff --git a/api/endpoints/work/PATCH.php b/api/endpoints/work/PATCH.php index 43eb6a0..de44730 100755 --- a/api/endpoints/work/PATCH.php +++ b/api/endpoints/work/PATCH.php @@ -47,10 +47,7 @@ ->min(1) ->max(parent::MYSQL_TEXT_MAX_LENGTH), - (new Rules(WorkModel::IS_LISTABLE->value)) - ->type(Type::BOOLEAN), - - (new Rules(WorkModel::IS_READABLE->value)) + (new Rules(WorkModel::IS_LISTED->value)) ->type(Type::BOOLEAN), (new Rules(WorkModel::DATE_MODIFIED->value)) diff --git a/api/endpoints/work/POST.php b/api/endpoints/work/POST.php index 74eb7a2..cf6c3c1 100755 --- a/api/endpoints/work/POST.php +++ b/api/endpoints/work/POST.php @@ -41,11 +41,7 @@ ->max(parent::MYSQL_TEXT_MAX_LENGTH) ->default(null), - (new Rules(WorkModel::IS_LISTABLE->value)) - ->type(Type::BOOLEAN) - ->default(false), - - (new Rules(WorkModel::IS_READABLE->value)) + (new Rules(WorkModel::IS_LISTED->value)) ->type(Type::BOOLEAN) ->default(false), diff --git a/api/endpoints/work/actions/GET.php b/api/endpoints/work/actions/GET.php index e286697..902a97d 100644 --- a/api/endpoints/work/actions/GET.php +++ b/api/endpoints/work/actions/GET.php @@ -38,12 +38,11 @@ WorkActionsModel::REF_WORK_ID->value, WorkActionsModel::DISPLAY_TEXT->value, WorkActionsModel::HREF->value, - WorkActionsModel::CLASS_LIST->value, - WorkActionsModel::EXTERNAL->value + WorkActionsModel::CLASS_LIST->value ]); return $response->num_rows > 0 - ? new Response($response->fetch_all(MYSQLI_ASSOC)) + ? new Response(parent::index_array_by_key($response->fetch_all(MYSQLI_ASSOC), WorkActionsModel::REF_WORK_ID->value)) : 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 2a07540..7c8175a 100755 --- a/api/endpoints/work/actions/POST.php +++ b/api/endpoints/work/actions/POST.php @@ -50,11 +50,7 @@ (new Rules(WorkActionsModel::CLASS_LIST->value)) ->type(Type::ARRAY) ->min(1) - ->default([]), - - (new Rules(WorkActionsModel::EXTERNAL->value)) - ->type(Type::BOOLEAN) - ->default(false) + ->default([]) ]); parent::__construct(Databases::VLW, $this->ruleset); diff --git a/api/endpoints/work/tags/GET.php b/api/endpoints/work/tags/GET.php index bacfb66..9d2bae3 100644 --- a/api/endpoints/work/tags/GET.php +++ b/api/endpoints/work/tags/GET.php @@ -45,7 +45,7 @@ ]); return $response->num_rows > 0 - ? new Response($response->fetch_all(MYSQLI_ASSOC)) + ? new Response(parent::index_array_by_key($response->fetch_all(MYSQLI_ASSOC), WorkTagsModel::REF_WORK_ID->value)) : new Response([], 404); } } \ No newline at end of file diff --git a/api/src/databases/VLWdb.php b/api/src/databases/VLWdb.php index 5286539..7a06a4f 100755 --- a/api/src/databases/VLWdb.php +++ b/api/src/databases/VLWdb.php @@ -37,6 +37,11 @@ ); } + // 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; + } + // Generate and return UUID4 string public static function gen_uuid4(): string { return sprintf("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", @@ -76,8 +81,21 @@ return $filters; } - // 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; + public function index_array_by_key(array $input, string $key): array { + $output = []; + + foreach ($input as $item) { + $idx = $item[$key]; + + // Create entry for key in output array if first item + if (!array_key_exists($idx, $output)) { + $output[$idx] = []; + } + + // Append item to array of array by key + $output[$idx][] = $item; + } + + return $output; } } \ No newline at end of file diff --git a/api/src/databases/models/Work/Work.php b/api/src/databases/models/Work/Work.php index 31dc079..dab578f 100644 --- a/api/src/databases/models/Work/Work.php +++ b/api/src/databases/models/Work/Work.php @@ -9,15 +9,15 @@ const TABLE = "work"; - case ID = "id"; - case TITLE = "title"; - case SUMMARY = "summary"; - case COVER_SRCSET = "cover_srcset"; - case IS_LISTABLE = "is_listable"; - case IS_READABLE = "is_readable"; - case DATE_YEAR = "date_year"; - case DATE_MONTH = "date_month"; - case DATE_DAY = "date_day"; - case DATE_MODIFIED = "date_modified"; - case DATE_CREATED = "date_created"; + case ID = "id"; + case REF_NAMESPACE_ID = "ref_namespace_id"; + case TITLE = "title"; + case SUMMARY = "summary"; + case COVER_SRCSET = "cover_srcset"; + case IS_LISTED = "is_listed"; + case DATE_YEAR = "date_year"; + case DATE_MONTH = "date_month"; + case DATE_DAY = "date_day"; + case DATE_MODIFIED = "date_modified"; + case DATE_CREATED = "date_created"; } \ No newline at end of file diff --git a/api/src/databases/models/Work/WorkActions.php b/api/src/databases/models/Work/WorkActions.php index dc5021d..6b411a1 100644 --- a/api/src/databases/models/Work/WorkActions.php +++ b/api/src/databases/models/Work/WorkActions.php @@ -6,8 +6,10 @@ const TABLE = "work_actions"; case REF_WORK_ID = "ref_work_id"; + case ICON_PREFIX = "icon_prefix"; + case ICON_SUFFIX = "icon_suffix"; + case ORDER_IDX = "order_idx"; case DISPLAY_TEXT = "display_text"; case HREF = "href"; case CLASS_LIST = "class_list"; - case EXTERNAL = "external"; } \ No newline at end of file diff --git a/api/src/databases/models/Work/WorkNamespaces.php b/api/src/databases/models/Work/WorkNamespaces.php new file mode 100644 index 0000000..0834725 --- /dev/null +++ b/api/src/databases/models/Work/WorkNamespaces.php @@ -0,0 +1,15 @@ +json() as $action): ?> - value]): ?> - - - - + diff --git a/public/work.php b/public/work.php index b0aa70f..793fc85 100644 --- a/public/work.php +++ b/public/work.php @@ -1,8 +1,44 @@ resp = $this->call(Endpoints::WORK->value)->params([ + WorkModel::IS_LISTED->value => true + ])->get(); + } + + private function get_item(string $key): array { + $idx = array_search($key, array_column($this->resp->json(), WorkModel::ID->value)); + return $this->resp->json()[$idx]; + } + + public function get_summary(string $key): string { + return $this->resp->ok ? $this->get_item($key)[WorkModel::SUMMARY->value] : self::ERROR_MSG; + } + } + ?>
@@ -20,7 +56,7 @@

vegvisir

-

Web navigation framework for PHP websites that does on the fly MPA-to-SPA routing between pages on the [open] web seas.

+

get_summary("vlw/vegvisir") ?>

@@ -32,7 +68,7 @@

reflect

-

A weird framework for building REST APIs in PHP with focus on native internal request routing and proxying.

+

get_summary("vlw/reflect") ?>

@@ -43,7 +79,6 @@
-

vlw.se

Can I put my own website here, is that cheating? Maybe, but I think this site counts as the most important thing I've personally created. I've only used my own libraries and frameworks to create this website, so it kind of works as a live demonstration of many of my web projects bundled together.

@@ -69,7 +104,7 @@

vlw/php-mysql

-

Yet another abstraction library for the php-mysql extension. For this library, I was willing to sacrifice most of MySQL's flexibility that comes with string interpolation in favor of method chaining that adheres to an SQL-like syntax. For simple DML operations I think it's pretty intuitive.

+

get_summary("vlw/php-mysql") ?>

@@ -77,10 +112,9 @@
-

Website for iCellate Medical

-

Together with the iCellate team, I created a new front-end for the biopharma startup using my Vegvisir framework as the foundation.

+

get_summary("icellate/website") ?>

@@ -88,10 +122,9 @@
-

Modernizing GeneMate by iCellate

-

Together with copy written by the amazing team at iCellate, and a new brand new appearance for the company, I helped design a new website and underlying systems for their GeneMate product.

+

get_summary("icellate/genemate") ?>

diff --git a/public/work/timeline.php b/public/work/timeline.php index cd96585..cbb8d87 100644 --- a/public/work/timeline.php +++ b/public/work/timeline.php @@ -19,154 +19,158 @@ require_once VV::root("api/src/databases/models/Work/WorkTags.php"); require_once VV::root("api/src/databases/models/Work/WorkActions.php"); - // Connect to VLW API - $api = new API(); + $work = new class extends API { + private readonly Response $resp; + private readonly Response $tags; + private readonly Response $actions; - // Retreive rows from work endpoints - $resp_work = $api->call(Endpoints::WORK->value)->params($_GET)->get(); + public function __construct() { + parent::__construct(); - // 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(); - } + $this->resp = $this->call(Endpoints::WORK->value)->params([ + WorkModel::IS_LISTED->value => true + ])->get(); -?> - - -ok): ?> - resp->ok) { + $this->tags = $this->call(Endpoints::WORK_TAGS->value)->get(); + $this->actions = $this->call(Endpoints::WORK_ACTIONS->value)->get(); + } + } /* Order response from endpoint into a multi-dimensional array. For example, a single item created at 14th of February 2024 would be ordered like this [2024 => [[02 => [14 => []]]]] */ - - $rows = []; - // Create array of arrays ordered by decending year, month, day, items - foreach ($resp_work->json() as $row) { - // Create array for current year if it doesn't exist - if (!array_key_exists($row[WorkModel::DATE_YEAR->value], $rows)) { - $rows[$row[WorkModel::DATE_YEAR->value]] = []; + public function get_timeline(): array { + if (!$this->resp->ok) { + return []; } - // Create array for current month if it doesn't exist - 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]] = []; + $timeline = []; + + // Create array of arrays ordered by decending year, month, day, items + foreach ($this->resp->json() as $row) { + // Create array for current year if it doesn't exist + if (!array_key_exists($row[WorkModel::DATE_YEAR->value], $timeline)) { + $timeline[$row[WorkModel::DATE_YEAR->value]] = []; + } + + // Create array for current month if it doesn't exist + if (!array_key_exists($row[WorkModel::DATE_MONTH->value], $timeline[$row[WorkModel::DATE_YEAR->value]])) { + $timeline[$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[WorkModel::DATE_DAY->value], $timeline[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]])) { + $timeline[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]] = []; + } + + // Append item to ordered array + $timeline[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]][] = $row; } - // Create array for current day if it doesn't exist - 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[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]][] = $row; + return $timeline; } - ?> + public function get_tags(string $key): array { + if (!$this->resp->ok) { + return []; + } -
- - $months): ?> -
-
-

-
+ return in_array($key, $this->tags->json()) ? $this->tags->json()[$key] : []; + } -
- - $days): ?> -
-
- -

-
+ public function get_actions(string $key): array { + if (!$this->resp->ok) { + return []; + } -
- - $items): ?> -
-
- -

-
+ return in_array($key, $this->actions->json()) ? $this->actions->json()[$key] : []; + } + } -
- -
- - - value), $item[WorkModel::ID->value]); ?> - - - -
- - - - -

value] ?>

- -
- - - - value])): ?> -

value] ?>

- - -

value] ?>

- - - 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]; - ?> - - > - -
- - -
- -
- -
- -
- -
- -
+?> + +
+ + get_timeline() as $year => $months): ?> +
+
+

- -
-
-

This is not really the end of the list. I will add some of my notable older work at some point.

-
- -

Something went wrong!

- + +
+ + $days): ?> +
+
+ +

+
+ +
+ + $items): ?> +
+
+ +

+
+ +
+ +
+ + + get_tags($item[WorkModel::ID->value])): ?> +
+ get_tags($item[WorkModel::ID->value]) as $tag): ?> +

value] ?>

+ +
+ + + + value])): ?> +

value] ?>

+ + +

value] ?>

+ +
+ get_actions($item[WorkModel::ID->value])): ?> + + + get_actions($item[WorkModel::ID->value]) as $action): ?> + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+
+

This is not really the end of the list. I will add some of my notable older work at some point.

+
\ No newline at end of file