mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-13 21:13:40 +02:00
wip: 2025-02-02T17:58:20+0100 (1738515500)
This commit is contained in:
parent
0dd247408e
commit
a12c9bc792
7 changed files with 206 additions and 26 deletions
35
endpoints/search/DELETE.php
Normal file
35
endpoints/search/DELETE.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use vlw\MySQL\Operators;
|
||||
use Reflect\{Response, Path};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\Database\Database;
|
||||
use VLW\Database\Tables\Search\SearchTable;
|
||||
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||
|
||||
class DELETE_Search extends Database {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(SearchTable::ID->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(2)
|
||||
->max(parent::SIZE_VARCHAR)
|
||||
]);
|
||||
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->db->for(SearchTable::NAME)->delete($_POST) === true ? new Response() : new Response("", 500);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
<?php
|
||||
|
||||
use vlw\MySQL\Operators;
|
||||
use Reflect\{Response, Path};
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use VLW\Database\Tables\Search\SearchTable;
|
||||
use const VLW\SEARCH_UPDATE_CACHE_PARM;
|
||||
use VLW\Database\Tables\Search\{SearchTable, SearchCategoryEnum};
|
||||
|
||||
require_once Path::root("src/Consts.php");
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||
|
||||
|
@ -18,10 +22,24 @@
|
|||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(SearchTable::QUERY->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(2)
|
||||
->max(parent::SIZE_VARCHAR)
|
||||
->default(null),
|
||||
|
||||
(new Rules(SearchTable::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(10)
|
||||
->max(10)
|
||||
->default(null),
|
||||
|
||||
(new Rules(SearchTable::CATEGORY->value))
|
||||
->type(Type::ENUM, SearchCategoryEnum::names())
|
||||
->default(null),
|
||||
|
||||
(new Rules(SEARCH_UPDATE_CACHE_PARM))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(false)
|
||||
]);
|
||||
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
@ -29,20 +47,47 @@
|
|||
parent::__construct();
|
||||
}
|
||||
|
||||
private static function get_query(): string {
|
||||
preg_match_all("/[a-zA-Z0-9]+/", $_GET[SearchTable::QUERY->value], $matches);
|
||||
|
||||
return strtolower(implode("", $matches[0]));
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$result = $this->db
|
||||
->for(SearchTable::NAME)
|
||||
->where([
|
||||
// Freshen cache if update flag is set
|
||||
if ($_GET[SEARCH_UPDATE_CACHE_PARM]) {
|
||||
(new Call(Endpoints::SEARCH->value))->post();
|
||||
}
|
||||
|
||||
$result = $this->db->for(SearchTable::NAME);
|
||||
|
||||
if ($_GET[SearchTable::ID->value]) {
|
||||
$result = $result->where([SearchTable::ID->value => $_GET[SearchTable::ID->value]]);
|
||||
} else if ($_GET[SearchTable::QUERY->value]) {
|
||||
$query = self::get_query();
|
||||
|
||||
$filter = [
|
||||
SearchTable::QUERY->value => [
|
||||
Operators::LIKE->value => "%{$_GET[SearchTable::QUERY->value]}%"
|
||||
Operators::LIKE->value => "%{$query}%"
|
||||
]
|
||||
])
|
||||
->select([
|
||||
SearchTable::TITLE->value,
|
||||
SearchTable::SUMMARY->value,
|
||||
SearchTable::CATEGORY->value,
|
||||
SearchTable::HREF->value
|
||||
]);
|
||||
];
|
||||
|
||||
if ($_GET[SearchTable::CATEGORY->value]) {
|
||||
$filter[SearchTable::CATEGORY->value] = $_GET[SearchTable::CATEGORY->value];
|
||||
}
|
||||
|
||||
$result = $result->where($filter);
|
||||
} else {
|
||||
new Response([], 400);
|
||||
}
|
||||
|
||||
$result = $result->select([
|
||||
SearchTable::ID->value,
|
||||
SearchTable::TITLE->value,
|
||||
SearchTable::SUMMARY->value,
|
||||
SearchTable::CATEGORY->value,
|
||||
SearchTable::HREF->value
|
||||
]);
|
||||
|
||||
return $result->num_rows > 0
|
||||
? new Response($result->fetch_all(MYSQLI_ASSOC))
|
||||
|
|
73
endpoints/search/POST.php
Normal file
73
endpoints/search/POST.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
use vlw\MySQL\Operators;
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use const VLW\SEARCH_QUERY_MAX_LENGTH;
|
||||
use VLW\Database\Tables\Search\{SearchTable, SearchCategoryEnum};
|
||||
use VLW\Database\Tables\Work\{WorkTable, ActionsTable};
|
||||
|
||||
require_once Path::root("src/Consts.php");
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Work/Work.php");
|
||||
require_once Path::root("src/Database/Tables/Work/Actions.php");
|
||||
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||
|
||||
class POST_Search extends Database {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
private static function truncate_string(string $input): string {
|
||||
return substr($input, 0, SEARCH_QUERY_MAX_LENGTH);
|
||||
}
|
||||
|
||||
private static function create_query(string $input): string {
|
||||
preg_match_all("/[a-zA-Z0-9]+/", $input, $matches);
|
||||
|
||||
return self::truncate_string(strtolower(implode("", $matches[0])));
|
||||
}
|
||||
|
||||
private function index_work(): void {
|
||||
foreach ((new Call(Endpoints::WORK->value))->get()->output() as $result) {
|
||||
$query = self::create_query(implode("", array_values($result)), 0, SEARCH_QUERY_MAX_LENGTH);
|
||||
|
||||
// Get actions related to current result
|
||||
$actions = (new Call(Endpoints::WORK_ACTIONS->value))->params([
|
||||
ActionsTable::REF_WORK_ID->value => $result[WorkTable::ID->value]
|
||||
])->get()->output();
|
||||
|
||||
$this->db->for(SearchTable::NAME)->insert([
|
||||
SearchTable::QUERY->value => $query,
|
||||
SearchTable::ID->value => crc32($query),
|
||||
SearchTable::TITLE->value => self::truncate_string($result[WorkTable::TITLE->value]),
|
||||
SearchTable::SUMMARY->value => self::truncate_string($result[WorkTable::SUMMARY->value]),
|
||||
SearchTable::CATEGORY->value => SearchCategoryEnum::WORK->name,
|
||||
// Use first action as link for search result
|
||||
SearchTable::HREF->value => $actions
|
||||
? self::truncate_string($actions[0][ActionsTable::HREF->value])
|
||||
: null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Truncate existing cache
|
||||
(new Call(Endpoints::SEARCH->value))->delete();
|
||||
|
||||
$this->index_work();
|
||||
|
||||
return $result->num_rows > 0
|
||||
? new Response($result->fetch_all(MYSQLI_ASSOC))
|
||||
: new Response([], 404);
|
||||
}
|
||||
}
|
|
@ -54,4 +54,11 @@ section.start-search {
|
|||
|
||||
section.start-search svg {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
/* # Result */
|
||||
|
||||
section.result {
|
||||
padding: var(--padding);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
use const VLW\ICONS_DIR;
|
||||
use VLW\Database\Models\Search\Search;
|
||||
use const VLW\{ICONS_DIR, DEFAULT_BUTTON_ICON};
|
||||
use VLW\Database\Tables\Search\{SearchTable, SearchCategoryEnum};
|
||||
|
||||
require_once VV::root("src/Consts.php");
|
||||
|
@ -20,7 +20,7 @@
|
|||
}
|
||||
|
||||
public function search(): array {
|
||||
$results = parent::all([SearchTable::QUERY->value => self::get_query()]);
|
||||
return parent::all([SearchTable::QUERY->value => self::get_query()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
|||
<form>
|
||||
<input name="<?= SearchTable::QUERY->value ?>" type="search" placeholder="search vlw.se..." value="<?= $search::get_query() ?>">
|
||||
<select name="<?= SearchTable::CATEGORY->value ?>">
|
||||
|
||||
<option value="null">All</option>
|
||||
<optgroup label="Categories">
|
||||
|
||||
|
@ -40,16 +39,30 @@
|
|||
<?php endforeach; ?>
|
||||
|
||||
</optgroup>
|
||||
|
||||
</select>
|
||||
<button type="submit" class="inline solid"><?= VV::embed(ICONS_DIR . "search.svg") ?></button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<?php if (array_key_exists(SearchTable::QUERY->value, $_GET)): ?>
|
||||
<section class="results">
|
||||
|
||||
</section>
|
||||
|
||||
<?php foreach ($search->search() as $result): ?>
|
||||
<section class="result">
|
||||
<?php if ($result->title()): ?>
|
||||
<h2><?= $result->title() ?></h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<p><?= $result->summary() ?></p>
|
||||
|
||||
<?php if ($result->href()): ?>
|
||||
<a href="<?= $result->href() ?>"><button class="inline">
|
||||
<p>read more</p>
|
||||
<?= VV::embed(DEFAULT_BUTTON_ICON) ?>
|
||||
</button></a>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php else: ?>
|
||||
<section class="start-search">
|
||||
<?= VV::embed(ICONS_DIR . "search.svg") ?>
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
const ICONS_DIR = MEDIA_DIR . "icons/";
|
||||
const DEFAULT_BUTTON_ICON = ICONS_DIR . "chevron.svg";
|
||||
|
||||
/**
|
||||
* # Search
|
||||
* Constants for the search API endpoint
|
||||
*/
|
||||
const SEARCH_QUERY_MAX_LENGTH = 2048;
|
||||
const SEARCH_UPDATE_CACHE_PARM = "update";
|
||||
|
||||
/**
|
||||
* # Timeline
|
||||
* Constants related to the work timeline
|
||||
|
|
|
@ -25,18 +25,18 @@
|
|||
}
|
||||
|
||||
public function title(): ?string {
|
||||
return $this->get(Search::TITLE->value);
|
||||
return $this->get(SearchTable::TITLE->value);
|
||||
}
|
||||
|
||||
public function summary(): string {
|
||||
return $this->get(Search::SUMMARY->value);
|
||||
public function summary(): ?string {
|
||||
return $this->get(SearchTable::SUMMARY->value);
|
||||
}
|
||||
|
||||
public function category(): ?SearchCategoryEnum {
|
||||
return SearchCategoryEnum::tryFromName($this->get(Search::CATEGORY->value));
|
||||
return SearchCategoryEnum::tryFromName($this->get(SearchTable::CATEGORY->value));
|
||||
}
|
||||
|
||||
public function href(): ?string {
|
||||
return $this->get(Search::HREF->value);
|
||||
return $this->get(SearchTable::HREF->value);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue