1,022 karma · joined October 29, 2020
I find it usually better to have short timed cache in front of the renderer instead, like caching the HTML or JSON output of a view, then you don't end up with inconsistent data because one table was cached and another wasn't.
Here is I how I do it with simple SQL and built in PHP constructs, easy to read, debug and implement.
First part of the example is class based and the second one function based if you are more into that.
<?php
declare(strict_types=1);
// SQL with classes
namespace {
$pdo = new PDO('sqlite::memory:');
$pdo->exec(
"CREATE TABLE article (
article_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
");
$pdo->exec("INSERT INTO article (name) VALUES('My Article')");
interface Cache
{
public function get(string $key): mixed;
public function set(string $key, mixed $data): void;
}
final class RuntimeCache implements Cache
{
private array $cache = [];
public function get(string $key): mixed
{
return $this->cache[$key] ?? null;
}
public function set(string $key, mixed $data): void
{
$this->cache[$key] = $data;
}
}
}
namespace Article {
use Cache;
use PDO;
readonly class Article
{
public int $article_id;
public string $name;
}
interface ArticleRepository
{
public function getArticleById(int $article_id): Article|null;
}
final class SqlArticleRepository implements ArticleRepository
{
public function __construct(private readonly PDO $pdo)
{
}
public function getArticleById(int $article_id): Article|null
{
$stmt = $this->pdo->prepare(
"SELECT article_id, name
FROM article
WHERE article_id = ?");
$stmt->execute([$article_id]);
$article = $stmt->fetchObject(Article::class);
return $article === false ? null : $article;
}
}
final class CachedArticleRepository implements ArticleRepository
{
public function __construct(private readonly Cache $cache,
private readonly ArticleRepository $articleRepository)
{
}
public function getArticleById(int $article_id): Article|null
{
$key = "article-{$article_id}";
$cached = $this->cache->get($key);
if (!empty($cached)) {
return $cached;
}
$article = $this->articleRepository->getArticleById($article_id);
if ($article !== null) {
$this->cache->set($key, $article);
}
return $article;
}
}
}
namespace {
$repo = new Article\CachedArticleRepository(
new RuntimeCache(),
new Article\SqlArticleRepository($pdo)
);
var_dump($repo->getArticleById(1));
var_dump($repo->getArticleById(1));
}
// SQL with functions
namespace {
function curry(callable $f, ...$args): callable {
$rf = new ReflectionFunction($f);
$count = $rf->getNumberOfParameters();
return function (...$arguments) use ($f, $rf, $count, $args) {
if (count($args) + count($arguments) >= $count) {
return $rf->invokeArgs(array_merge($args, $arguments));
}
return curry($f, ...array_merge($args, $arguments));
};
}
}
namespace Article\SqlRepository
{
use Article\Article;
function getArticleById(\PDO $pdo, int $article_id): Article|null
{
$stmt = $pdo->prepare(
"SELECT article_id, name
FROM article
WHERE article_id = ?");
$stmt->execute([$article_id]);
$article = $stmt->fetchObject(Article::class);
return $article === false ? null : $article;
}
}
namespace Article\CachedArticleRepository
{
use Article\Article;
function getArticleById(callable $cacheGet, callable $cacheSet, callable $getArticleById, int $article_id): Article|null
{
$key = "article-{$article_id}";
$cached = $cacheGet($key);
if (!empty($cached)) {
return $cached;
}
$article = $getArticleById($article_id);
if ($article !== null) {
$cacheSet($key, $article);
}
return $article;
}
}
namespace {
// for this example, just reuse existing class based implementation
$runtimeCache = new RuntimeCache();
$getArticleById = curry(Article\CachedArticleRepository\getArticleById(...),
$runtimeCache->get(...), $runtimeCache->set(...),
curry(Article\SqlRepository\getArticleById(...), $pdo));
var_dump($getArticleById(1));
var_dump($getArticleById(1));
}E_ERROR is a fatal error thus it will throw (I think since PHP 7) an Error exception that can be catched with a normal try/catch and thus possible to return a proper response.
PHP has many stable frameworks if you are in to that and if you don't want to use a framework you can run vanilla PHP without a problem.
Dependency management is done these days with composer and it is easy add a dependency, like framework or library, from composer.
PHP has has lot of good tooling like IDEs, linters, testing etc.
Here is a short intro to PHP I wrote a few years ago.
However the jQuery 4 announcement page linked to this jQuery migration plugin that I was unaware of. Perhaps it can help in some old projects.
Sweden's welfare state is a left wing dream come true, however the bad news for the left is that it empirically disproves every left wing idea about crime and society.
The educated middle class can afford to hold the most out of touch abstract theories because they don’t need to suffer the consequences.
Few years ago I evaluated FreePascal for writing desktop applications, I ended up instead with the BASIC dialect PureBasic (albeit commercial), better out of the box experience. Pascal is also slightly more verbose than BASIC.
Component rendered before client side JavaScript.
> Web components quite literally don't work without Javascript.
That is why I'm not using them.
There will almost always be a roundtrip, either to fetch HTML segments or to ask for more JSON data, except for the rare cases you already have everything, because that is the nature of client-server paradigm.
By putting all your templates in the backend you can render them as a whole document or in parts in one go and send them wherever, to the browser, over Ajax or in the email. No need for buggy asynchronous SQL JOINs over HTTP and infinite state handling.
If you always start with a JavaScript function, and all that comes with that, you need a programmer to be able to create the web, but with HTML (and CSS) first you can have other categories of people do that, like designers. And combine that with a library like htmx you can then enhance that experience for interactive parts and still keep everything in the same backend template. And if it still a need for more complex interactive parts a programmer can can add that with JavaScript afterwards. That is a much better division of labour.
And it is not outdated, browser actually excel in rendering HTML documents, that is what they are designed to do, thus it will always be faster to render the HTML document as it is rather than building it on the fly.
And also the core idea of reaching for JavaScript before HTML will result in suboptimal solutions, I have seen so much unnecessary JavaScript code that could have been easily solved with a few lines of HTML. This change of perspective changes how we think and reason about a problem and therefore our solutions as well. That is why the bootstrapping of a "modern" web app has become more and more asinine.
Another problem with frameworks like React, Vue, Angular and et al have is that they have their own lifecycle and that has made the web closed and not open how it once was, closed in the sense that you can't enhance the HTML document from the outside after it been rendered, thus this leads to the assumption that the person(s) who wrote the site is also the person(s) that manages or uses it, that is a bad assumption.
Personally I think the main problem here is client side rendering to begin with, with a modern server side templating library you get a component based structure too and can abstract whatever HTML tags you want in your component before client side JavaScript.
Problem with React is that it is JavaScript first, you always begin with a JavaScript function, but in my opinion the web should be HTML first.
Point is that the DOM API version of the initial jQuery snippet are not equivalent. $() statement does a bit more than that.
This becomes obvious if the requirement are a bit more complex than the example, perhaps we want to chain more expression on at the end, then the DOM API version becomes more convoluted.
Today you can get killed even far away from the front in a place that was considered mostly safe in previous wars.
The psychological effect today is much worse than anytime in history.
It is your tech-illiterate friends that hook you into the toxic cloud merely by having your contact information.
Technology changes social interactions.
Edit: typo, 'to' instead of 'the'
With Google you can even make an argument that they can violate more user's rights because of Free software.
Don't get me wrong, I'm not against Free software per see, the topic is just more complex than what is presented.