Skip to content

Commit 0300d85

Browse files
authored
Merge pull request #194 from dotkernel/develop
Update LLMS generation, category names, and fix broken links
2 parents 59b9d21 + fb8a4fa commit 0300d85

14 files changed

Lines changed: 134 additions & 58 deletions

‎public/md-articles/php-troubleshooting/fix-installing-pear-packages-with-php-7-2.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "[FIX] Installing PEAR packages with PHP 7.2"
2+
title: "FIX: Installing PEAR packages with PHP 7.2"
33
description: "How to fix the 'Cannot use result of built-in function in write context' PEAR error on PHP 7.2 by patching Archive_Tar's func_get_args() call, and how to reinstall the affected packages afterward."
44
author: "Gabi DJ"
55
date_published: "2018-05-18"
@@ -8,7 +8,7 @@ category: "PHP Troubleshooting"
88
language: "en"
99
---
1010

11-
# [FIX] Installing PEAR packages with PHP 7.2
11+
# FIX: Installing PEAR packages with PHP 7.2
1212

1313
## TL;DR
1414

1.76 KB
Loading

‎src/App/src/Fixture/articles_cleaned.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"name": "Dotkernel",
3+
"name": "Core Components",
44
"slug": "dotkernel",
55
"isVisible": true,
66
"articles": [
@@ -1169,7 +1169,7 @@
11691169
{
11701170
"post_title": "Zend Framework 1 End-of-Life",
11711171
"post_date": "2016-06-28 11:22:06",
1172-
"post_status": "archived",
1172+
"post_status": "published",
11731173
"author": {
11741174
"display_name": "arhimede",
11751175
"github": "arhimede"
@@ -1238,7 +1238,7 @@
12381238
{
12391239
"post_title": "Sunsetting PEAR Channel for Zend Framework 1",
12401240
"post_date": "2022-11-11 12:55:22",
1241-
"post_status": "archived",
1241+
"post_status": "published",
12421242
"author": {
12431243
"display_name": "arhimede",
12441244
"github": "arhimede"
@@ -1951,7 +1951,7 @@
19511951
]
19521952
},
19531953
{
1954-
"post_title": "[FIX] Installing PEAR packages with PHP 7.2",
1954+
"post_title": "FIX: Installing PEAR packages with PHP 7.2",
19551955
"post_date": "2018-05-18 11:19:49",
19561956
"post_status": "published",
19571957
"author": {

‎src/App/src/Service/LlmsGenerator.php‎

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use RuntimeException;
1010

1111
use function array_key_exists;
12+
use function array_merge;
1213
use function basename;
1314
use function count;
1415
use function explode;
@@ -28,16 +29,16 @@ class LlmsGenerator
2829
{
2930
/** @var list<non-empty-string> */
3031
private const CATEGORY_ORDER = [
31-
'dotkernel',
32-
'php-development',
33-
'dotkernel-api',
34-
'dotkernel3',
3532
'headless-platform',
36-
'middleware',
33+
'dotkernel-api',
3734
'architecture',
38-
'design-pattern',
3935
'how-to',
36+
'middleware',
4037
'best-practice',
38+
'design-pattern',
39+
'dotkernel3',
40+
'dotkernel',
41+
'php-development',
4142
];
4243

4344
/**
@@ -50,11 +51,40 @@ class LlmsGenerator
5051
'php-troubleshooting',
5152
'licensing',
5253
'phpstorm',
54+
'zend-framework',
55+
];
56+
57+
/**
58+
* Individual posts demoted to `## Optional`, keyed by category slug - for a single article
59+
* that doesn't warrant moving its whole category into `OPTIONAL_CATEGORIES`.
60+
*
61+
* @var array<non-empty-string, list<non-empty-string>>
62+
*/
63+
private const array OPTIONAL_POSTS = [
64+
'dotkernel' => [
65+
'commitment-to-php-new-zend-certified-engineers-zce-in-our-team',
66+
'adding-composer-support-in-your-dotkernel-project',
67+
'using-dotkernel-with-composer-dependencies',
68+
'forcing-utf8-connections-and-character-set-in-mysql',
69+
'adding-a-cors-implementation-to-zend-expressive',
70+
],
71+
'best-practice' => [
72+
'insert-update-delete-statements-with-zend-db',
73+
'sql-queries-using-zend-db-select',
74+
'subqueries-with-zend-db',
75+
'using-like-wildcards-with-zend-db',
76+
'what-are-returning-the-fetch-functions-from-zend-db',
77+
],
78+
'php-development' => [
79+
'almalinux-9-in-wsl2-install-php-apache-mariadb-composer-phpmyadmin',
80+
'mezzio-app-development-in-wsl2',
81+
],
5382
];
5483

5584
/** @var array<non-empty-string, non-empty-string> */
5685
private const CATEGORY_BLURBS = [
57-
'dotkernel' => 'the core framework - releases, caching, sessions, auth, WURFL/device detection',
86+
'dotkernel' => 'the core framework - dot-* components (caching, logging, mail, dependency injection),'
87+
. ' Doctrine integration, and getting started with Dotkernel Light',
5888
'php-development' => 'general PHP tooling, environments, IDEs, security',
5989
'how-to' => 'practical guides - migrations, CORS, PSR-7, routing',
6090
'best-practice' => 'coding standards and database access patterns',
@@ -89,6 +119,15 @@ class LlmsGenerator
89119
*/
90120
private const string OSS_LIFECYCLE_SLUG = 'dotkernel-packages-oss-lifecycle';
91121

122+
/**
123+
* The Contact page has no `public/contact.md` counterpart to source a title/description
124+
* from, so it is hardcoded directly into `## Pages` instead.
125+
*/
126+
private const string CONTACT_TITLE = 'Contact';
127+
private const string CONTACT_PATH = 'contact/';
128+
private const string CONTACT_DESCRIPTION = 'Get in touch, report a security issue, or find where to ask a '
129+
. 'question and contribute to the project.';
130+
92131
public function __construct(
93132
private readonly PostRepository $postRepository,
94133
private readonly string $sourceDir,
@@ -108,16 +147,20 @@ public function write(): int
108147
{
109148
$postsByCategory = $this->groupPublishedPostsByCategory();
110149

111-
$sections = [];
150+
$sections = [];
151+
$optionalLines = [];
112152
foreach (self::CATEGORY_ORDER as $slug) {
113153
if (! isset($postsByCategory[$slug])) {
114154
continue;
115155
}
116-
$sections[] = $this->buildCategorySection($slug, $postsByCategory[$slug]);
156+
[$entries, $demoted] = $this->splitOptionalPosts($slug, $postsByCategory[$slug]);
157+
$optionalLines = array_merge($optionalLines, $demoted);
158+
if ($entries !== []) {
159+
$sections[] = $this->buildCategorySection($slug, $entries);
160+
}
117161
unset($postsByCategory[$slug]);
118162
}
119163

120-
$optionalLines = [];
121164
$optionalCategoryCount = 0;
122165
foreach (self::OPTIONAL_CATEGORIES as $slug) {
123166
if (! isset($postsByCategory[$slug])) {
@@ -132,7 +175,11 @@ public function write(): int
132175

133176
uasort($postsByCategory, static fn (array $a, array $b): int => count($b) - count($a));
134177
foreach ($postsByCategory as $slug => $posts) {
135-
$sections[] = $this->buildCategorySection($slug, $posts);
178+
[$entries, $demoted] = $this->splitOptionalPosts($slug, $posts);
179+
$optionalLines = array_merge($optionalLines, $demoted);
180+
if ($entries !== []) {
181+
$sections[] = $this->buildCategorySection($slug, $entries);
182+
}
136183
}
137184

138185
$written = $this->buildHeader()
@@ -174,17 +221,40 @@ private function groupPublishedPostsByCategory(): array
174221
return $byCategory;
175222
}
176223

224+
/**
225+
* @param list<array{post: Post, title: string, description: string}> $entries
226+
* @return array{0: list<array{post: Post, title: string, description: string}>, 1: list<string>}
227+
*/
228+
private function splitOptionalPosts(string $slug, array $entries): array
229+
{
230+
$optionalSlugs = self::OPTIONAL_POSTS[$slug] ?? [];
231+
if ($optionalSlugs === []) {
232+
return [$entries, []];
233+
}
234+
235+
$kept = [];
236+
$optional = [];
237+
foreach ($entries as $entry) {
238+
if (in_array($entry['post']->getSlug(), $optionalSlugs, true)) {
239+
$optional[] = $this->buildOptionalPostLink($slug, $entry);
240+
continue;
241+
}
242+
243+
$kept[] = $entry;
244+
}
245+
246+
return [$kept, $optional];
247+
}
248+
177249
/**
178250
* @param list<array{post: Post, title: string, description: string}> $entries
179251
*/
180252
private function buildCategorySection(string $slug, array $entries): string
181253
{
182254
$category = $entries[0]['post']->getCategory();
183255
$heading = sprintf(
184-
'## %s (%d post%s)',
185-
$category->getName(),
186-
count($entries),
187-
count($entries) === 1 ? '' : 's',
256+
'## %s',
257+
$category->getName()
188258
);
189259

190260
$lines = [$heading, ''];
@@ -256,7 +326,13 @@ private function buildPagesSection(): string
256326
return '';
257327
}
258328

259-
$pages = [];
329+
$pages = [
330+
[
331+
'title' => self::CONTACT_TITLE,
332+
'path' => self::CONTACT_PATH,
333+
'description' => self::CONTACT_DESCRIPTION,
334+
],
335+
];
260336
foreach (glob($this->pagesDir . '/*.md') ?: [] as $path) {
261337
$slug = basename($path, '.md');
262338
if ($slug === self::OSS_LIFECYCLE_SLUG || in_array($slug, self::DOCS_PAGES, true)) {
@@ -276,24 +352,20 @@ private function buildPagesSection(): string
276352

277353
$pages[] = [
278354
'title' => trim(explode('|', $title, 2)[0]),
279-
'slug' => $slug,
355+
'path' => $slug . '.md',
280356
'description' => $description,
281357
];
282358
}
283359

284-
if ($pages === []) {
285-
return '';
286-
}
287-
288360
usort($pages, static fn (array $a, array $b): int => strcasecmp($a['title'], $b['title']));
289361

290362
$lines = ['## Pages', ''];
291363
foreach ($pages as $page) {
292364
$lines[] = sprintf(
293-
'- [%s](%s/%s.md): %s',
365+
'- [%s](%s/%s): %s',
294366
$page['title'],
295367
$this->baseUrl,
296-
$page['slug'],
368+
$page['path'],
297369
$page['description'],
298370
);
299371
}
@@ -358,9 +430,6 @@ private function buildHeader(): string
358430
. 'by category and attributed to an author; URLs follow the pattern `/{category-slug}/{post-slug}/`. '
359431
. 'Each post also has a Markdown version at `/{category-slug}/{post-slug}.md`.';
360432

361-
$about = 'the team behind Dotkernel - how the team works, its commitment to open source and the PHP '
362-
. 'community, and how it uses AI under guardrails';
363-
364433
return "# {$this->title}\n\n"
365434
. $intro . "\n\n"
366435
. $body . "\n\n"

‎src/Blog/templates/page/blog-resource/best-practice/basic-security-in-dotkernel-headless-platform.html.twig‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,6 @@
216216

217217
<li><a href="https://docs.dotkernel.org/api-documentation/v6/tutorials/cors/" target="_blank" rel="noreferrer noopener">CORS in Dotkernel API</a></li>
218218

219-
<li><a href="https://www.dotkernel.com/how-to/mezzio-cors-implementation-in-dotkernel/" target="_blank" rel="noreferrer noopener">CORS Policy Setup in Dotkernel</a></li>
220-
221219
<li><a href="https://docs.dotkernel.org/api-documentation/v6/core-features/error-reporting/" target="_blank" rel="noreferrer noopener">Error Reporting Endpoint</a></li>
222220

223221
<li><a href="https://docs.dotkernel.org/api-documentation/v6/openapi/introduction/" target="_blank" rel="noreferrer noopener">OpenAPI Documentation</a></li>

‎src/Blog/templates/page/blog-resource/best-practice/insert-update-delete-statements-with-zend-db.html.twig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends '@layout/blog-post.html.twig' %}
22

33
{% block body %}
4-
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-select-zend-db/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at DML statements.</br>
4+
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at DML statements.</br>
55
DML (Data Manipulation Language) statements are statements that change data values in database tables. There are 3 primary DML statements:
66
<ul>
77
<li>INSERT - Inserting new rows into database tables.

‎src/Blog/templates/page/blog-resource/best-practice/sql-queries-using-zend-db-select.html.twig‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ echo $select->__toString();exit;
8787
</pre>
8888
</br>
8989
Also see:
90-
- <a href="http://www.dotkernel.com/best-practice/sql-fetch-zend-db/">What are returning the FETCH functions from Zend_Db</a></br>
91-
- <a href="http://www.dotkernel.com/best-practice/subqueris-with-zend-db/">Subqueries with Zend_Db</a></br>
92-
- <a href="http://www.dotkernel.com/best-practice/iud-statements-with-zend-d/">INSERT, UPDATE, DELETE statements with Zend_Db</a>
90+
- <a href="http://www.dotkernel.com/best-practice/what-are-returning-the-fetch-functions-from-zend-db/">What are returning the FETCH functions from Zend_Db</a></br>
91+
- <a href="http://www.dotkernel.com/best-practice/subqueries-with-zend-db/">Subqueries with Zend_Db</a></br>
92+
- <a href="http://www.dotkernel.com/best-practice/insert-update-delete-statements-with-zend-db/">INSERT, UPDATE, DELETE statements with Zend_Db</a>
9393

9494
<h2 id="user-content-faq">Frequently Asked Questions</h2>
9595

‎src/Blog/templates/page/blog-resource/best-practice/subqueries-with-zend-db.html.twig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends '@layout/blog-post.html.twig' %}
22

33
{% block body %}
4-
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-select-zend-db/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at subqueries.
4+
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at subqueries.
55
</br>
66
As you note, the below is a complicate query, with <i>COUNT()</i>, <i>LEFT JOIN()</i>, <i>GROUP BY</i>
77
- select from 3 tables, and make a count from 2 different tables:

‎src/Blog/templates/page/blog-resource/best-practice/using-like-wildcards-with-zend-db.html.twig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends '@layout/blog-post.html.twig' %}
22

33
{% block body %}
4-
Continuing the Zend_Db article <a href="http://www.dotkernel.com/dotkernel/sql-select-zend-db/" target="_blank" rel="noopener noreferrer">series</a>, let's discuss the LIKE condition.
4+
Continuing the Zend_Db article <a href="http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/" target="_blank" rel="noopener noreferrer">series</a>, let's discuss the LIKE condition.
55

66
The <strong>LIKE </strong>condition allows you to use wildcards in the <em>WHERE </em>clause of an SQL statement. This allows pattern matching. It can be used in any valid SQL statement (<em>SELECT</em>, <em>INSERT</em>, <em>UPDATE </em>or <em>DELETE</em>).
77

‎src/Blog/templates/page/blog-resource/best-practice/what-are-returning-the-fetch-functions-from-zend-db.html.twig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends '@layout/blog-post.html.twig' %}
22

33
{% block body %}
4-
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-select-zend-db/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at <i>FETCH</i> methods that are in <a href="https://docs.laminas.dev/laminas-db/adapter/" target="_blank" rel="noopener noreferrer">Zend_Db_Adapter_Abstract</a>:
4+
Continuing the Zend_DB article <a href="http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/" target="_blank" rel="noopener noreferrer">series</a>, we are stopping now at <i>FETCH</i> methods that are in <a href="https://docs.laminas.dev/laminas-db/adapter/" target="_blank" rel="noopener noreferrer">Zend_Db_Adapter_Abstract</a>:
55
<pre>
66
array fetchAll (string|Zend_Db_Select $sql, , )
77
array fetchAssoc (string|Zend_Db_Select $sql, [mixed $bind = array()])

0 commit comments

Comments
 (0)