perf: consolidate article DB queries into single cached lookup (#38)
- Add loadArticle() with static per-request cache for article data - Refactor getArticleDate(), getArticleAuthor() to use cached article - Refactor findImage() for com_content to use cached article - Pass cached article to JsonLdBuilder::buildArticle() to skip its query - Reduces article page DB queries from 5 to 1 for OG tag generation
This commit is contained in:
@@ -220,7 +220,7 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface
|
||||
if ($option === 'com_mokoshop' && $view === 'product' && $id > 0) {
|
||||
$schema = JsonLdBuilder::buildProduct($id, $title, $description, $imageUrl);
|
||||
} elseif ($option === 'com_content' && $view === 'article' && $id > 0) {
|
||||
$schema = JsonLdBuilder::buildArticle($id, $title, $description, $imageUrl);
|
||||
$schema = JsonLdBuilder::buildArticle($id, $title, $description, $imageUrl, $this->loadArticle($id));
|
||||
} else {
|
||||
$schema = JsonLdBuilder::buildWebPage($title, $description);
|
||||
}
|
||||
@@ -448,17 +448,10 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface
|
||||
|
||||
// For Joomla articles, look at the intro/full image fields
|
||||
if ($option === 'com_content' && $id > 0) {
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('images'))
|
||||
->from($db->quoteName('#__content'))
|
||||
->where($db->quoteName('id') . ' = ' . (int) $id);
|
||||
$article = $this->loadArticle($id);
|
||||
|
||||
$db->setQuery($query);
|
||||
$images = $db->loadResult();
|
||||
|
||||
if ($images) {
|
||||
$imagesData = json_decode($images, true);
|
||||
if ($article && !empty($article->images)) {
|
||||
$imagesData = json_decode($article->images, true);
|
||||
|
||||
if (!empty($imagesData['image_fulltext'])) {
|
||||
return $imagesData['image_fulltext'];
|
||||
@@ -514,6 +507,38 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface
|
||||
return rtrim(Uri::root(), '/') . '/' . ltrim($image, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and cache a full article record with author for the current request.
|
||||
*
|
||||
* @param int $id Article ID
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
private function loadArticle(int $id): ?object
|
||||
{
|
||||
static $cache = [];
|
||||
|
||||
if (isset($cache[$id])) {
|
||||
return $cache[$id];
|
||||
}
|
||||
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName([
|
||||
'a.title', 'a.introtext', 'a.fulltext', 'a.images',
|
||||
'a.created', 'a.modified', 'a.publish_up', 'a.metadesc',
|
||||
]))
|
||||
->select($db->quoteName('u.name', 'author_name'))
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'))
|
||||
->where($db->quoteName('a.id') . ' = ' . $id);
|
||||
|
||||
$db->setQuery($query);
|
||||
$cache[$id] = $db->loadObject();
|
||||
|
||||
return $cache[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a date field from an article.
|
||||
*
|
||||
@@ -524,16 +549,9 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface
|
||||
*/
|
||||
private function getArticleDate(int $id, string $field): string
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName($field))
|
||||
->from($db->quoteName('#__content'))
|
||||
->where($db->quoteName('id') . ' = ' . $id);
|
||||
$article = $this->loadArticle($id);
|
||||
|
||||
$db->setQuery($query);
|
||||
$date = $db->loadResult();
|
||||
|
||||
return $date ?: '';
|
||||
return $article->$field ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,16 +563,9 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface
|
||||
*/
|
||||
private function getArticleAuthor(int $id): string
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('u.name'))
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'))
|
||||
->where($db->quoteName('a.id') . ' = ' . $id);
|
||||
$article = $this->loadArticle($id);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadResult() ?: '';
|
||||
return $article->author_name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,31 +20,36 @@ class JsonLdBuilder
|
||||
/**
|
||||
* Build Article schema for a com_content article.
|
||||
*
|
||||
* @param int $articleId Article ID
|
||||
* @param string $title Page title
|
||||
* @param string $description Page description
|
||||
* @param string $image Image URL (absolute)
|
||||
* @param int $articleId Article ID
|
||||
* @param string $title Page title
|
||||
* @param string $description Page description
|
||||
* @param string $image Image URL (absolute)
|
||||
* @param object|null $cachedArticle Pre-loaded article data (avoids duplicate query)
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public static function buildArticle(int $articleId, string $title, string $description, string $image): ?array
|
||||
public static function buildArticle(int $articleId, string $title, string $description, string $image, ?object $cachedArticle = null): ?array
|
||||
{
|
||||
if ($articleId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName([
|
||||
'a.created', 'a.modified', 'a.publish_up',
|
||||
'u.name',
|
||||
]))
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'))
|
||||
->where($db->quoteName('a.id') . ' = ' . $articleId);
|
||||
$article = $cachedArticle;
|
||||
|
||||
$db->setQuery($query);
|
||||
$article = $db->loadObject();
|
||||
if (!$article) {
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName([
|
||||
'a.created', 'a.modified', 'a.publish_up',
|
||||
]))
|
||||
->select($db->quoteName('u.name', 'author_name'))
|
||||
->from($db->quoteName('#__content', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'))
|
||||
->where($db->quoteName('a.id') . ' = ' . $articleId);
|
||||
|
||||
$db->setQuery($query);
|
||||
$article = $db->loadObject();
|
||||
}
|
||||
|
||||
if (!$article) {
|
||||
return null;
|
||||
@@ -60,10 +65,12 @@ class JsonLdBuilder
|
||||
'dateModified' => $article->modified ?: $article->created,
|
||||
];
|
||||
|
||||
if (!empty($article->name)) {
|
||||
$authorName = $article->author_name ?? '';
|
||||
|
||||
if (!empty($authorName)) {
|
||||
$schema['author'] = [
|
||||
'@type' => 'Person',
|
||||
'name' => $article->name,
|
||||
'name' => $authorName,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user