Sweep: link fixes, splash i18n parity, neuronetz badge, mobile, training source
LINK AUDIT — every internal link now resolves to a real page:
- BrandHeader doc-nav 'Showcase' target → /{locale}/showcase/projects/
(the showcase/ directory has no index, only patterns/projects)
- Translated-slug bugs across de/es/fr/ja: ~30 broken links from the
overnight translator that auto-localised path segments. Mapped back to
the actual English slugs:
de: warum-nibiru → why-nibiru, kuenstliche-intelligenz/orakel → ai/oracle,
kI/modul/* → ai/module/*, praesentation/projekte → showcase/projects
es: ai/modulo/* → ai/module/*, diseno/* → design/*,
porque-nibiru / por-que-nibiru → why-nibiru,
presentacion/proyectos → showcase/projects
fr: ia/module/* → ai/module/*, ia/module/formation → ai/module/training,
pourquoi-nibiru → why-nibiru, presentation/projets → showcase/projects
ja: ai/milestones → ai/roadmap
- Cross-locale leaks: bare `/ai/oracle` (no locale) → `/{locale}/ai/oracle/`
in de/index.mdx, fr/index.mdx, fr/ai/module/overview.md, en/ai/corpus.md
- `/en/start/` (which 404s — start/ has no index) was hardcoded in five
design/components.md atelier-button hrefs across all locales →
`/{locale}/start/installation/`
- `/en/reference` removed from en/downloads.mdx — the reference doc tree
isn't built yet; replaced with a github link to the v2 markdown source
- Collapsed stray double-slashes (`/de/why-nibiru//` etc.) introduced by
the slug-replacement sed sweep
Final audit shows 0 broken internal links (down from ~37).
TRAINING SOURCE NOW SHIPS — root cause of "I cannot find the training data
in the repository": the curated source files were gitignored.
- .gitignore at scripts/extraction/ now whitelists framework-reference-v2.md
and lora-augmentation.summary.txt alongside lora-augmentation.jsonl
- The 1620-line v2 reference, the 323-record augmentation jsonl, and the
summary report all enter the repo so the production Docker build sees
them and contributors can find them by browsing gitea
NEURONETZ AI DEEPLINK BADGE — small "AI by Neuronetz ↗" pill in the splash
footer's bottom strip. Logo mark mirrored locally to
public/img/external/neuronetz-mark.svg (pulled from neuronetz.ai/favicon.svg)
so the page doesn't hot-link off-domain on every paint. Magenta border on
hover; opens neuronetz.ai in a new tab with rel=noopener.
SPLASH I18N PARITY — de/es/fr/ja index.mdx now import + render the same
component stack as en (CometTrail · MmvcStage · MissionControl · LaunchSequence
· SpacecraftGrid · EditorialContent · LandingFooter · ToTop · LandingScripts),
so every locale shows the full splash structure. The component bodies
themselves are still English (proper i18n is the next step); for now this
brings structural parity.
MOBILE RESPONSIVE SWEEP:
- LandingFooter: 4-col grid stacks 2-col @ 768px and 1-col @ 480px;
bottom strip wraps vertical at 480px
- MmvcStage: 5-step progress rail tightens its gaps under 720px and
drops the bar segments entirely under 480px so the labels fit
- Docs bridge §11: tighter H1/H2 spacing, breadcrumbs/doc-meta on
narrow viewports, pagination cards stack 1-col, help-strip stacks
vertical, tables get horizontal-scroll on overflow
- Doc-header: nav-version chip hides under 480px so the search-pill
+ brand fit comfortably
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
BIN
docs/public/img/external/neuronetz-mark.png
vendored
Normal file
BIN
docs/public/img/external/neuronetz-mark.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
4
docs/public/img/external/neuronetz-mark.svg
vendored
Normal file
4
docs/public/img/external/neuronetz-mark.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<rect width="32" height="32" fill="#f4f2ed"/>
|
||||||
|
<circle cx="16" cy="16" r="6" fill="#8b2b1f"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 165 B |
10
docs/scripts/extraction/.gitignore
vendored
10
docs/scripts/extraction/.gitignore
vendored
@@ -1,9 +1,13 @@
|
|||||||
# Extraction outputs from research-agent runs are typically large + transient.
|
# Extraction outputs from research-agent runs are typically large + transient.
|
||||||
# Keep them local by default — except the curated augmentation file which
|
# Keep them local by default — except the curated files that need to ship so
|
||||||
# needs to ship for the Docker build to fold it into the LoRA corpus.
|
# the Docker build can fold them into the LoRA corpus AND so contributors
|
||||||
|
# can find the source-of-truth reference in the repo.
|
||||||
*.md
|
*.md
|
||||||
*.json
|
*.json
|
||||||
*.jsonl
|
*.jsonl
|
||||||
|
*.txt
|
||||||
|
|
||||||
# …with one explicit exception:
|
# …with explicit exceptions:
|
||||||
|
!framework-reference-v2.md
|
||||||
!lora-augmentation.jsonl
|
!lora-augmentation.jsonl
|
||||||
|
!lora-augmentation.summary.txt
|
||||||
|
|||||||
1620
docs/scripts/extraction/framework-reference-v2.md
Normal file
1620
docs/scripts/extraction/framework-reference-v2.md
Normal file
File diff suppressed because it is too large
Load Diff
68
docs/scripts/extraction/lora-augmentation.summary.txt
Normal file
68
docs/scripts/extraction/lora-augmentation.summary.txt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
Nibiru Framework — LoRA Augmentation Corpus Summary
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
Source: /home/stephan/PhpstormProjects/Nibiru/docs/scripts/extraction/framework-reference-v2.md
|
||||||
|
Output: /home/stephan/PhpstormProjects/Nibiru/docs/scripts/extraction/lora-augmentation.jsonl
|
||||||
|
Format: JSONL (one record per line)
|
||||||
|
Records: 323
|
||||||
|
|
||||||
|
Validation
|
||||||
|
----------
|
||||||
|
All 323 lines are valid JSON. All records carry the required keys:
|
||||||
|
instruction, input, output, metadata.{language, source, section, kind, citations}.
|
||||||
|
(Verified with `jq` schema check — 0 records failed.)
|
||||||
|
|
||||||
|
Breakdown by `kind`
|
||||||
|
-------------------
|
||||||
|
code-recall 121
|
||||||
|
workflow 97
|
||||||
|
inikey 29
|
||||||
|
comparison 26
|
||||||
|
gotcha 16
|
||||||
|
debug 16
|
||||||
|
edge-case 15
|
||||||
|
refactor 3
|
||||||
|
|
||||||
|
Coverage
|
||||||
|
--------
|
||||||
|
All 14 top-level sections of the v2 reference are exercised:
|
||||||
|
- §0 Executive Summary
|
||||||
|
- §1 Namespace Map
|
||||||
|
- §2 Singletons & Factories (Config, Pdo, Db, Form, Controller, View)
|
||||||
|
- §3 Database Layer (drivers, autogenerator pipeline, multi-database)
|
||||||
|
- §4 Module System (structure, registry, loading flow, plugin pattern, lifecycle)
|
||||||
|
- §5 Form System (architecture, substitution, type classes, ordering, div wrap, assembly, production usage)
|
||||||
|
- §6 Request/Response Lifecycle
|
||||||
|
- §7 INI Key Reference
|
||||||
|
- §8 Namespace Conventions
|
||||||
|
- §9 Constants
|
||||||
|
- §10 Production Patterns NOT in Core
|
||||||
|
- §11 Gotchas & Footguns
|
||||||
|
- §12 Request Handling Trace
|
||||||
|
- Appendices: File Index, Quick Reference
|
||||||
|
|
||||||
|
Cross-section workflow / debug / refactor records (~30) cover full-stack scenarios:
|
||||||
|
adding a new module, adding a new table, switching DB sections per model,
|
||||||
|
debugging Postgres-mode mask footgun, end-to-end form-to-DB save flow, etc.
|
||||||
|
|
||||||
|
Sample records (3 for sanity-check)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Sample 1 (kind=code-recall, line 14):
|
||||||
|
{"instruction":"Show me the exact `__construct()` body the autogenerator produces for a MySQL model.","input":"","output":"```php\npublic function __construct()\n{\n Pdo::settingsSection('DATABASE');\n self::initTable( self::TABLE );\n}\n```\nFrom `loach.mssql.database-connection/application/model/WarehouseLoach/timeanddate.php:30-34`. The first line writes the static `Pdo::$section`; the second registers the table schema with the adapter's static `$table` array.","metadata":{"language":"en","source":"framework-reference-v2","section":"2.2 Pdo","kind":"code-recall","citations":["loach.mssql.database-connection/application/model/WarehouseLoach/timeanddate.php:30-34","framework-reference-v2.md §2.2"]}}
|
||||||
|
|
||||||
|
Sample 2 (kind=inikey, line 100):
|
||||||
|
{"instruction":"Which INI key sets the PHP timezone?","input":"","output":"`[SETTINGS][timezone]`. Read by `Dispatcher::run()`. Defaults to `UTC`. From `framework-reference-v2.md §7 [SETTINGS]`.","metadata":{"language":"en","source":"framework-reference-v2","section":"7 INI [SETTINGS]","kind":"inikey","citations":["framework-reference-v2.md §7"]}}
|
||||||
|
|
||||||
|
Sample 3 (kind=workflow, line 220):
|
||||||
|
{"instruction":"How does the autogenerator handle a brand-new application with zero existing model files?","input":"","output":"`Model::createOutFolder()` at `core/c/model.php:28` does `mkdir -p` (chmod 0777) for the target folder, then `createClassFiles()` at line 37 loops through scanned tables (or one table if `--table=` was passed) and calls `generateClassByTableName($table)` for each. For each class file:\n1. If overwrite mode, `unlink` existing.\n2. Otherwise `fopen('w'); fclose()` to create an empty stub.\n3. Build template, do all 8 placeholder substitutions plus driver/odbc switching.\n4. Write file and chmod 0777.\n\nSo zero existing files becomes a full set of generated models on the first request. From `framework-reference-v2.md §3.2`.","metadata":{"language":"en","source":"framework-reference-v2","section":"3.2 Autogenerator Pipeline","kind":"workflow","citations":["core/c/model.php:28","core/c/model.php:37","core/c/model.php:53","framework-reference-v2.md §3.2"]}}
|
||||||
|
|
||||||
|
Notes for downstream merge
|
||||||
|
--------------------------
|
||||||
|
- Every record has a real file:line citation OR a quoted code block.
|
||||||
|
- Code blocks are quoted from the v2 reference; nothing is invented.
|
||||||
|
- Phrasing varies across natural developer styles ("Walk me through…",
|
||||||
|
"Quick question:…", "Why does…", "I'm trying to…", "Show me…",
|
||||||
|
"I'm getting `Class … not found`…", etc.).
|
||||||
|
- The `kind` taxonomy follows the spec: edge-case, comparison, workflow,
|
||||||
|
debug, refactor, code-recall, inikey, gotcha.
|
||||||
@@ -33,7 +33,7 @@ const docNav: Array<{ label: string; href: string; current?: boolean }> = [
|
|||||||
{ label: 'MMVC', href: `/${currentLocale}/core/architecture/` },
|
{ label: 'MMVC', href: `/${currentLocale}/core/architecture/` },
|
||||||
{ label: 'AI module',href: `/${currentLocale}/ai/module/overview/` },
|
{ label: 'AI module',href: `/${currentLocale}/ai/module/overview/` },
|
||||||
{ label: 'CLI', href: `/${currentLocale}/start/quick-start/` },
|
{ label: 'CLI', href: `/${currentLocale}/start/quick-start/` },
|
||||||
{ label: 'Showcase', href: `/${currentLocale}/showcase/` },
|
{ label: 'Showcase', href: `/${currentLocale}/showcase/projects/` },
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container footer-bottom">
|
<div class="container footer-bottom">
|
||||||
<span>© 2026 Nibiru · MIT licensed</span>
|
<span>© 2026 Nibiru · MIT licensed</span>
|
||||||
|
<a class="powered-by" href="https://neuronetz.ai/" target="_blank" rel="noopener" aria-label="Powered by Neuronetz AI — opens neuronetz.ai">
|
||||||
|
<img src="/img/external/neuronetz-mark.svg" alt="" width="18" height="18" loading="lazy" />
|
||||||
|
<span class="powered-by-label">AI by</span>
|
||||||
|
<span class="powered-by-name">Neuronetz</span>
|
||||||
|
<span class="powered-by-arrow" aria-hidden="true">↗</span>
|
||||||
|
</a>
|
||||||
<span>Built in orbit · v0.9.2</span>
|
<span>Built in orbit · v0.9.2</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -85,7 +91,18 @@
|
|||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
}
|
}
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.footer-inner { grid-template-columns: 1fr 1fr; }
|
.footer-inner { grid-template-columns: 1fr 1fr; gap: 32px; }
|
||||||
|
.footer { padding: 60px 0 40px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.footer-inner { grid-template-columns: 1fr; gap: 28px; padding: 0 20px; }
|
||||||
|
.footer-bottom {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 24px 20px 0;
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.footer-brand .brand {
|
.footer-brand .brand {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@@ -150,5 +167,55 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "AI by Neuronetz" badge — small, deliberately understated.
|
||||||
|
The mark is the same favicon-tier asset neuronetz.ai exposes; we mirror
|
||||||
|
it locally at /img/external/neuronetz-mark.svg so the page doesn't
|
||||||
|
hot-link off-domain on every paint. */
|
||||||
|
.powered-by {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 10px 4px 6px;
|
||||||
|
background: rgba(184, 107, 255, 0.06);
|
||||||
|
border: 1px solid var(--nibiru-line);
|
||||||
|
border-radius: 999px;
|
||||||
|
color: rgba(244, 238, 219, 0.78);
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 0.10em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: border-color 200ms ease, color 200ms ease, background-color 200ms ease;
|
||||||
|
}
|
||||||
|
.powered-by:hover {
|
||||||
|
border-color: var(--nibiru-nebula-mag, #b86bff);
|
||||||
|
color: var(--nibiru-star);
|
||||||
|
background: rgba(184, 107, 255, 0.12);
|
||||||
|
}
|
||||||
|
.powered-by img {
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f4f2ed;
|
||||||
|
padding: 1px;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
.powered-by-label { color: var(--nibiru-muted); }
|
||||||
|
.powered-by-name {
|
||||||
|
color: var(--nibiru-star);
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: var(--nibiru-font-display, 'Space Grotesk', sans-serif);
|
||||||
|
}
|
||||||
|
.powered-by-arrow {
|
||||||
|
font-size: 9px;
|
||||||
|
color: var(--nibiru-muted);
|
||||||
|
transition: transform 200ms ease, color 200ms ease;
|
||||||
|
}
|
||||||
|
.powered-by:hover .powered-by-arrow {
|
||||||
|
transform: translate(1px, -1px);
|
||||||
|
color: var(--nibiru-nebula-mag, #b86bff);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -205,10 +205,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.mmvc-progress { gap: 4px; font-size: 9px; padding: 0 8px; }
|
.mmvc-progress { gap: 3px; font-size: 9px; padding: 0 6px; }
|
||||||
.mmvc-progress .step { gap: 4px; padding-right: 4px; }
|
.mmvc-progress .step { gap: 3px; padding-right: 3px; }
|
||||||
.mmvc-progress .step .bar { width: 14px; }
|
.mmvc-progress .step .bar { width: 10px; }
|
||||||
.mmvc-progress .step.active .bar { width: 24px; }
|
.mmvc-progress .step.active .bar { width: 18px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Below 480px the 5-step rail starts colliding with the panel headline.
|
||||||
|
Drop the bar segments entirely and keep only the labels at minimum size. */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.mmvc-progress { font-size: 8px; gap: 6px; padding: 0 4px; }
|
||||||
|
.mmvc-progress .step .bar { display: none; }
|
||||||
|
.mmvc-progress .step { padding-right: 0; gap: 0; }
|
||||||
|
.mmvc-copy { padding: 0 16px; }
|
||||||
|
.mmvc-copy h3 { font-size: clamp(28px, 9vw, 44px); }
|
||||||
|
.mmvc-copy p { font-size: 15px; }
|
||||||
|
.mmvc-copy .step-num { font-size: 10px; margin-bottom: 16px; }
|
||||||
|
.mmvc-copy .step-num::before, .mmvc-copy .step-num::after { width: 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 960px) {
|
@media (max-width: 960px) {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Rohdaten für die Verwendung als RAG-Retrieval-Daten:
|
|||||||
"content": "Modules implementing `SplSubject` can broadcast events…"
|
"content": "Modules implementing `SplSubject` can broadcast events…"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Dies ist genau die Datei, die intern von [Oracle](/ai/oracle/) verwendet wird.
|
Dies ist genau die Datei, die intern von [Oracle](/de/ai/oracle/) verwendet wird.
|
||||||
|
|
||||||
## Wie Chunks abgeleitet werden
|
## Wie Chunks abgeleitet werden
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ Das Chat-Plugin ist das einfachste Element des KI-Moduls. Es umhüllt Ollamas `/
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->system('Be terse.'); // optional system prompt
|
$chat->system('Be terse.'); / optional system prompt
|
||||||
$chat->model('qwen2.5-coder:14b'); // override the configured model
|
$chat->model('qwen2.5-coder:14b'); / override the configured model
|
||||||
$chat->temperature(0.2); // override config
|
$chat->temperature(0.2); / override config
|
||||||
$chat->maxTokens(512); // override config
|
$chat->maxTokens(512); / override config
|
||||||
|
|
||||||
$chat->user('Hello'); // append a user message
|
$chat->user('Hello'); / append a user message
|
||||||
$chat->assistant('Hi.'); // append an assistant message (rare)
|
$chat->assistant('Hi.'); / append an assistant message (rare)
|
||||||
|
|
||||||
$reply = $chat->complete(); // run the call, return text
|
$reply = $chat->complete(); / run the call, return text
|
||||||
$reply = $chat->ask('How are you?'); // = ->user(...)->complete()
|
$reply = $chat->ask('How are you?'); / = ->user(...)->complete()
|
||||||
|
|
||||||
$chat->reset(); // clear messages, keep model + system
|
$chat->reset(); / clear messages, keep model + system
|
||||||
$chat->history(); // [{role, content}, …]
|
$chat->history(); / [{role, content}, …]
|
||||||
```
|
```
|
||||||
## Einmalig
|
## Einmalig
|
||||||
```php
|
```php
|
||||||
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
|
|||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->user('Name three Nibiru singletons.');
|
$chat->user('Name three Nibiru singletons.');
|
||||||
$singletons = $chat->complete(); // appended to history
|
$singletons = $chat->complete(); / appended to history
|
||||||
|
|
||||||
$chat->user('What does the second one do?');
|
$chat->user('What does the second one do?');
|
||||||
$detail = $chat->complete(); // model has full context
|
$detail = $chat->complete(); / model has full context
|
||||||
```
|
```
|
||||||
## Überschreiben des Modells und des Stils pro Aufruf
|
## Überschreiben des Modells und des Stils pro Aufruf
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ Das Embed-Plugin ist ein dünner Wrapper um Ollama's `/api/embeddings` plus drei
|
|||||||
```php
|
```php
|
||||||
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
||||||
|
|
||||||
$vec = $embed->one('controller'); // float[]
|
$vec = $embed->one('controller'); / float[]
|
||||||
$vectors = $embed->batch(['a', 'b', 'c']); // float[][]
|
$vectors = $embed->batch(['a', 'b', 'c']); / float[][]
|
||||||
|
|
||||||
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1
|
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[]
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
|
||||||
```
|
```
|
||||||
## Muster: Entfernen ähnlicher Zeichenfolgen
|
## Muster: Entfernen ähnlicher Zeichenfolgen
|
||||||
```php
|
```php
|
||||||
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
|
|||||||
return $best[0];
|
return $best[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication'
|
echo bestTag('User::isAuthorized', $tagVecs, $embed); / → 'authentication'
|
||||||
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably)
|
echo bestTag('Pageination::setTable', $tagVecs, $embed); / → 'database' (probably)
|
||||||
```
|
```
|
||||||
## Speicherung
|
## Speicherung
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ Einbettungen sind Gleitkommazahlenarrays – typischerweise 768 Gleitkommazahlen
|
|||||||
|
|
||||||
Verwenden Sie `Embed::pack()`, um sie als 4-Byte-Gleitkommazahlen in Base64 zu kodieren:
|
Verwenden Sie `Embed::pack()`, um sie als 4-Byte-Gleitkommazahlen in Base64 zu kodieren:
|
||||||
```php
|
```php
|
||||||
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string
|
$compact = Embed::pack($vec); / ~4 KB → ~5.3 KB base64 string
|
||||||
$vec = Embed::unpack($compact);
|
$vec = Embed::unpack($compact);
|
||||||
```
|
```
|
||||||
Das RAG-Plugin verwendet dieses Format intern für seine JSON-Dateien.
|
Das RAG-Plugin verwendet dieses Format intern für seine JSON-Dateien.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ description: "Erster-Klasse KI auf Nibiru – Chat, Embeddings, RAG, Agenten –
|
|||||||
|
|
||||||
Nibiru bietet ein **KIM-Modul** (`application/module/ai/`), das jeder Nibiru-Anwendung eine erstklassige KI-Oberfläche gibt. PHP-Code kann mit einem lokalen LLM chatten, Text einbetten, RAG über eigene Daten ausführen oder einen Agenten mit Tools starten – alles ohne einen Byte an eine bezahlte API zu senden.
|
Nibiru bietet ein **KIM-Modul** (`application/module/ai/`), das jeder Nibiru-Anwendung eine erstklassige KI-Oberfläche gibt. PHP-Code kann mit einem lokalen LLM chatten, Text einbetten, RAG über eigene Daten ausführen oder einen Agenten mit Tools starten – alles ohne einen Byte an eine bezahlte API zu senden.
|
||||||
|
|
||||||
Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf Ihrer Ollama-Instanz](/de/kuenstliche-intelligenz/orakel/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt.
|
Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf Ihrer Ollama-Instanz](/de/ai/oracle/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt.
|
||||||
|
|
||||||
## Was Sie erhalten
|
## Was Sie erhalten
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
|
|||||||
// Multi-turn
|
// Multi-turn
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
$chat->user('How do I scaffold a module?');
|
$chat->user('How do I scaffold a module?');
|
||||||
$chat->user('And add Graylog hooks?'); // referrs to previous turn
|
$chat->user('And add Graylog hooks?'); / referrs to previous turn
|
||||||
echo $chat->complete();
|
echo $chat->complete();
|
||||||
|
|
||||||
// Override per call
|
// Override per call
|
||||||
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
|
|||||||
```
|
```
|
||||||
Kompakte Speicherung:
|
Kompakte Speicherung:
|
||||||
```php
|
```php
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
||||||
```
|
```
|
||||||
### 3. RAG — Ingestieren, Abrufen, Verankern
|
### 3. RAG — Ingestieren, Abrufen, Verankern
|
||||||
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
|||||||
$rag = $ai->rag('product-help');
|
$rag = $ai->rag('product-help');
|
||||||
|
|
||||||
// One-time ingestion
|
// One-time ingestion
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
$rag->ingestFile('/var/data/manual.pdf.txt');
|
$rag->ingestFile('/var/data/manual.pdf.txt');
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
|
|
||||||
$agent = $ai->agent()->withTools([
|
$agent = $ai->agent()->withTools([
|
||||||
new Tools\PdoQuery(), // read-only SQL
|
new Tools\PdoQuery(), / read-only SQL
|
||||||
new Tools\HttpGet(), // fetch URLs
|
new Tools\HttpGet(), / fetch URLs
|
||||||
new Tools\FileRead(), // read project files
|
new Tools\FileRead(), / read project files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $agent->run('How many active users registered last week?');
|
echo $agent->run('How many active users registered last week?');
|
||||||
@@ -154,7 +154,7 @@ Die Gestaltungsentfernung:
|
|||||||
|
|
||||||
## Nächste
|
## Nächste
|
||||||
|
|
||||||
- [Chat-Plugin-Referenz](/de/kI/modul/chat/)
|
- [Chat-Plugin-Referenz](/de/ai/module/chat/)
|
||||||
- [RAG-Plugin-Referenz](/de/kI/modul/rag/)
|
- [RAG-Plugin-Referenz](/de/ai/module/rag/)
|
||||||
- [Agent-Plugin-Referenz](/de/kI/modul/agent/)
|
- [Agent-Plugin-Referenz](/de/ai/module/agent/)
|
||||||
- [Training nibiru-coder](/de/kI/modul/training/)
|
- [Training nibiru-coder](/de/ai/module/training/)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ Das RAG-Plugin ist die Killerfunktion des KI-Moduls für Produktbauer. Es verwan
|
|||||||
use Nibiru\Module\Ai\Ai;
|
use Nibiru\Module\Ai\Ai;
|
||||||
|
|
||||||
$ai = new Ai();
|
$ai = new Ai();
|
||||||
$rag = $ai->rag('product-help'); // a named collection
|
$rag = $ai->rag('product-help'); / a named collection
|
||||||
|
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
|
|
||||||
echo $rag->ask('How do I cancel my subscription?');
|
echo $rag->ask('How do I cancel my subscription?');
|
||||||
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
|
|||||||
```
|
```
|
||||||
## API-Referenz
|
## API-Referenz
|
||||||
```php
|
```php
|
||||||
$rag = $ai->rag('name'); // get/create a named collection
|
$rag = $ai->rag('name'); / get/create a named collection
|
||||||
|
|
||||||
// --- Ingestion ---
|
// --- Ingestion ---
|
||||||
$rag->ingestText($text, $metadata = []); // single chunk
|
$rag->ingestText($text, $metadata = []); / single chunk
|
||||||
$count = $rag->ingestFile('path'); // returns chunks added
|
$count = $rag->ingestFile('path'); / returns chunks added
|
||||||
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive
|
$count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
|
||||||
|
|
||||||
// --- Querying ---
|
// --- Querying ---
|
||||||
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …]
|
$hits = $rag->search('query', $k = null); / [{score, text, metadata}, …]
|
||||||
$answer = $rag->ask('question', $k = null); // top-K → chat call
|
$answer = $rag->ask('question', $k = null); / top-K → chat call
|
||||||
|
|
||||||
// --- Maintenance ---
|
// --- Maintenance ---
|
||||||
$rag->reset(); // forget everything (deletes file)
|
$rag->reset(); / forget everything (deletes file)
|
||||||
$n = $rag->size(); // number of chunks
|
$n = $rag->size(); / number of chunks
|
||||||
```
|
```
|
||||||
## Einstellungsregler
|
## Einstellungsregler
|
||||||
|
|
||||||
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
|
|||||||
|
|
||||||
## Was kommt als nächstes?
|
## Was kommt als nächstes?
|
||||||
|
|
||||||
- [Agent-Plugin →](/de/kI/modul/agent/) für Werkzeuge, nicht für Abruf.
|
- [Agent-Plugin →](/de/ai/module/agent/) für Werkzeuge, nicht für Abruf.
|
||||||
- [Trainingsnibiru-coder →](/de/kI/modul/trainings/) um den Chat so zu gestalten, dass er halb in der Stimme des Frameworks antwortet.
|
- [Trainingsnibiru-coder →](/de/ai/module/training/) um den Chat so zu gestalten, dass er halb in der Stimme des Frameworks antwortet.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public function pageAction() {
|
|||||||
View::forwardTo('/login');
|
View::forwardTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Für rollebasierte Überprüfungen verwenden die Showcase-Anwendungen den `Acl`-Plugin aus dem gleichen Modul:
|
Für rollebasierte Überprüfungen verwenden die Showcase-Anwendungen den `Acl`-Plugin aus dem gleichen Modul:
|
||||||
@@ -178,7 +178,7 @@ public function submitAction() {
|
|||||||
http_response_code(419);
|
http_response_code(419);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...handle submission...
|
/ ...handle submission...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Fügen Sie `<input type="hidden" name="csrf" value="{$csrf}">` in Ihr Formular ein.
|
Fügen Sie `<input type="hidden" name="csrf" value="{$csrf}">` in Ihr Formular ein.
|
||||||
|
|||||||
@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
|
|||||||
## Konfiguration lesen
|
## Konfiguration lesen
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
||||||
$cfg['DATABASE']['driver']; // 'pdo'
|
$cfg['DATABASE']['driver']; / 'pdo'
|
||||||
$cfg['SETTINGS']['page.url']; // 'https://my-app.local'
|
$cfg['SETTINGS']['page.url']; / 'https://my-app.local'
|
||||||
```
|
```
|
||||||
Verwenden Sie für tief geschachtelte Konfigurationen die typisierten Konstanten aus der View-Schnittstelle:
|
Verwenden Sie für tief geschachtelte Konfigurationen die typisierten Konstanten aus der View-Schnittstelle:
|
||||||
```php
|
```php
|
||||||
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css']
|
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
|
||||||
```
|
```
|
||||||
## Modulkonfigurationen
|
## Modulkonfigurationen
|
||||||
|
|
||||||
Jeder Modul unter `application/module/<name>/settings/` kann seine eigenen INI-Dateien haben. Der Registrierungsservice erkennt sie automatisch und macht sie über verfügbar:
|
Jeder Modul unter `application/module/<name>/settings/` kann seine eigenen INI-Dateien haben. Der Registrierungsservice erkennt sie automatisch und macht sie über verfügbar:
|
||||||
```php
|
```php
|
||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime; // from [USERS] section in users.ini
|
$users->session_lifetime; / from [USERS] section in users.ini
|
||||||
```
|
```
|
||||||
Der Registrierung wird `<module>.<env>.ini` vor `<module>.ini` bevorzugt, sodass Sie automatisch Überschreibungen pro Umgebung erhalten.
|
Der Registrierung wird `<module>.<env>.ini` vor `<module>.ini` bevorzugt, sodass Sie automatisch Überschreibungen pro Umgebung erhalten.
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
|
|||||||
```
|
```
|
||||||
Hilfsfunktionen aus dem Basiscontroller:
|
Hilfsfunktionen aus dem Basiscontroller:
|
||||||
```php
|
```php
|
||||||
$this->getRequest('id', false); // $_REQUEST['id'] ?? false
|
$this->getRequest('id', false); / $_REQUEST['id'] ?? false
|
||||||
$this->getPost('email', ''); // $_POST['email'] ?? ''
|
$this->getPost('email', ''); / $_POST['email'] ?? ''
|
||||||
$this->getGet('page', 1); // $_GET['page'] ?? 1
|
$this->getGet('page', 1); / $_GET['page'] ?? 1
|
||||||
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI']
|
$this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
|
||||||
$this->getFiles('upload'); // $_FILES['upload']
|
$this->getFiles('upload'); / $_FILES['upload']
|
||||||
$this->getSession('auth'); // $_SESSION['auth']
|
$this->getSession('auth'); / $_SESSION['auth']
|
||||||
```
|
```
|
||||||
Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tests durch eine untergeordnete Klasse ersetzen.
|
Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tests durch eine untergeordnete Klasse ersetzen.
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tes
|
|||||||
|
|
||||||
Um innerhalb einer Aktion umzuleiten:
|
Um innerhalb einer Aktion umzuleiten:
|
||||||
```php
|
```php
|
||||||
View::forwardTo('/login'); // 302 to the URL, exits
|
View::forwardTo('/login'); / 302 to the URL, exits
|
||||||
View::forwardToJsonHeader(); // sets Content-Type: application/json
|
View::forwardToJsonHeader(); / sets Content-Type: application/json
|
||||||
```
|
```
|
||||||
`forwardToJsonHeader()` ist das kanonische Muster für JSON-Endpunkte – setzen Sie den Header, weisen Sie `data` zu und geben Sie zurück. Der Anzeigebereich kümmert sich danach um die Reste.
|
`forwardToJsonHeader()` ist das kanonische Muster für JSON-Endpunkte – setzen Sie den Header, weisen Sie `data` zu und geben Sie zurück. Der Anzeigebereich kümmert sich danach um die Reste.
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ public function run() {
|
|||||||
|
|
||||||
if (Config::getInstance()->getConfig()
|
if (Config::getInstance()->getConfig()
|
||||||
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
||||||
new Model(false); // 1. (re)generate models from schema
|
new Model(false); / 1. (re)generate models from schema
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::getInstance()->route(); // 2. parse the URL
|
Router::getInstance()->route(); / 2. parse the URL
|
||||||
Auto::loader()->loadModelFiles(); // 3. load model files
|
Auto::loader()->loadModelFiles(); / 3. load model files
|
||||||
Auto::loader()->loadModules(); // 4. load module classes
|
Auto::loader()->loadModules(); / 4. load module classes
|
||||||
|
|
||||||
$tpl = Router::getInstance()->tplName();
|
$tpl = Router::getInstance()->tplName();
|
||||||
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
||||||
|
|
||||||
if (is_file($controllerFile)) { // 5. controller file exists
|
if (is_file($controllerFile)) { / 5. controller file exists
|
||||||
require_once $controllerFile;
|
require_once $controllerFile;
|
||||||
$class = "Nibiru\\{$tpl}Controller";
|
$class = "Nibiru\\{$tpl}Controller";
|
||||||
$controller = new $class();
|
$controller = new $class();
|
||||||
@@ -49,7 +49,7 @@ public function run() {
|
|||||||
$action = $_REQUEST['_action'] . 'Action';
|
$action = $_REQUEST['_action'] . 'Action';
|
||||||
$controller->navigationAction();
|
$controller->navigationAction();
|
||||||
if (method_exists($controller, $action)) {
|
if (method_exists($controller, $action)) {
|
||||||
$controller->$action(); // 6. optional named action
|
$controller->$action(); / 6. optional named action
|
||||||
}
|
}
|
||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -57,9 +57,9 @@ public function run() {
|
|||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::getInstance()->display(); // 7. render Smarty
|
Display::getInstance()->display(); / 7. render Smarty
|
||||||
} else {
|
} else {
|
||||||
// 8. soft 404 — render the configured error controller
|
/ 8. soft 404 — render the configured error controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
|
|||||||
```
|
```
|
||||||
### Lebenszyklus
|
### Lebenszyklus
|
||||||
```
|
```
|
||||||
create // reset the static buffer; call before building a new form
|
create / reset the static buffer; call before building a new form
|
||||||
addForm // wrap the buffer in <form>...</form> and return as a string
|
addForm / wrap the buffer in <form>...</form> and return as a string
|
||||||
```
|
```
|
||||||
:::caution[Die Namensinconsistenz ist absichtlich, zumindest teilweise]
|
:::caution[Die Namensinconsistenz ist absichtlich, zumindest teilweise]
|
||||||
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — das Präfix entspricht, ob das Element ein `<input type="…">` (Input-Prefix) oder ein anderer Tag (Type-Prefix) ist. Es ist unangenehm, aber stabil; die CLI-`./nibiru -c`-Scaffolds verwenden dasselbe Muster, sodass die Muskelspeicherung funktioniert.
|
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — das Präfix entspricht, ob das Element ein `<input type="…">` (Input-Prefix) oder ein anderer Tag (Type-Prefix) ist. Es ist unangenehm, aber stabil; die CLI-`./nibiru -c`-Scaffolds verwenden dasselbe Muster, sodass die Muskelspeicherung funktioniert.
|
||||||
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
|
|||||||
```php
|
```php
|
||||||
use Nibiru\Factory\Form;
|
use Nibiru\Factory\Form;
|
||||||
|
|
||||||
Form::create(); // reset the static buffer
|
Form::create(); / reset the static buffer
|
||||||
|
|
||||||
Form::addOpenDiv(['class' => 'form-group']);
|
Form::addOpenDiv(['class' => 'form-group']);
|
||||||
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
||||||
@@ -187,7 +187,7 @@ class formsController extends Controller {
|
|||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
'class' => 'contacts-input form-control',
|
'class' => 'contacts-input form-control',
|
||||||
]);
|
]);
|
||||||
// ...more fields...
|
/ ...more fields...
|
||||||
$this->form = Form::addForm([
|
$this->form = Form::addForm([
|
||||||
'name' => 'newregister',
|
'name' => 'newregister',
|
||||||
'method' => 'post',
|
'method' => 'post',
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
|
|||||||
Lesen Sie es von überall aus:
|
Lesen Sie es von überall aus:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$cfg->session_lifetime; // 7200
|
$cfg->session_lifetime; / 7200
|
||||||
$cfg->password_min_length; // 12
|
$cfg->password_min_length; / 12
|
||||||
$cfg->allowed_roles; // [admin, editor, standard]
|
$cfg->allowed_roles; / [admin, editor, standard]
|
||||||
```
|
```
|
||||||
Umgebungsüberschreibungen: Eine Datei mit dem Namen `users.production.ini` wird vorzugsweise gegenüber `users.ini` verwendet, wenn `APPLICATION_ENV=production`.
|
Umgebungsüberschreibungen: Eine Datei mit dem Namen `users.production.ini` wird vorzugsweise gegenüber `users.ini` verwendet, wenn `APPLICATION_ENV=production`.
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
|
|||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
||||||
|
|
||||||
$analytics->trackPageView(); // internally calls notify()
|
$analytics->trackPageView(); / internally calls notify()
|
||||||
```
|
```
|
||||||
Jeder Beobachter erhält die Analytics-Instanz und zieht die Ereignisdaten aus, über die er sich interessiert.
|
Jeder Beobachter erhält die Analytics-Instanz und zieht die Ereignisdaten aus, über die er sich interessiert.
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ Die Namen sind **kleinbuchstabeigeordnete Ordnername**, genau wie sie unter `app
|
|||||||
Pluginklassen liegen im **Plural**-Namespace `Plugins`:
|
Pluginklassen liegen im **Plural**-Namespace `Plugins`:
|
||||||
```php
|
```php
|
||||||
// application/module/billing/plugins/invoice.php
|
// application/module/billing/plugins/invoice.php
|
||||||
namespace Nibiru\Module\Billing\Plugins; // ← plural
|
namespace Nibiru\Module\Billing\Plugins; / ← plural
|
||||||
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
||||||
```
|
```
|
||||||
Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlschläge auf. Der CLI-Scaffold-Befehl (`./nibiru -m billing`) generiert den richtigen Namespace für Sie.
|
Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlschläge auf. Der CLI-Scaffold-Befehl (`./nibiru -m billing`) generiert den richtigen Namespace für Sie.
|
||||||
@@ -210,7 +210,7 @@ Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlsch
|
|||||||
Sie **registrieren** Ihre Modul-INI-Dateien nicht — der [Registry](/de/core/registry/) entdeckt sie automatisch, indem er `application/module/<name>/settings/*.ini` durchläuft, nachdem `[AUTOLOADER]` die Modulklassen lädt. Jede Sektion `[<MODULE>]` (in Großbuchstaben) in einer INI wird zur Verfügung gestellt als:
|
Sie **registrieren** Ihre Modul-INI-Dateien nicht — der [Registry](/de/core/registry/) entdeckt sie automatisch, indem er `application/module/<name>/settings/*.ini` durchläuft, nachdem `[AUTOLOADER]` die Modulklassen lädt. Jede Sektion `[<MODULE>]` (in Großbuchstaben) in einer INI wird zur Verfügung gestellt als:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
||||||
$cfg->invoice_prefix; // [BILLING] invoice.prefix → property
|
$cfg->invoice_prefix; / [BILLING] invoice.prefix → property
|
||||||
```
|
```
|
||||||
Der Registrierungsmodul bevorzugt `<Modul>.<Umgebung>.ini` (z.B. `billing.production.ini`), wenn `APPLICATION_ENV` übereinstimmt.
|
Der Registrierungsmodul bevorzugt `<Modul>.<Umgebung>.ini` (z.B. `billing.production.ini`), wenn `APPLICATION_ENV` übereinstimmt.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class productsController extends Controller
|
|||||||
{
|
{
|
||||||
public function pageAction() {
|
public function pageAction() {
|
||||||
$products = new products();
|
$products = new products();
|
||||||
Pageination::setEntriesPerPage(25); // optional; default from INI
|
Pageination::setEntriesPerPage(25); / optional; default from INI
|
||||||
Pageination::setTable($products);
|
Pageination::setTable($products);
|
||||||
$rows = Pageination::loadTableAsArray();
|
$rows = Pageination::loadTableAsArray();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Für jedes Modul unter `application/module/<name>/settings/`:
|
|||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime;
|
$users->session_lifetime;
|
||||||
$users->password_min_length;
|
$users->password_min_length;
|
||||||
$users->allowed_roles; // array
|
$users->allowed_roles; / array
|
||||||
```
|
```
|
||||||
Innerhalb des Moduls selbst ist die Konvention, dies in einen Setter zu verpacken:
|
Innerhalb des Moduls selbst ist die Konvention, dies in einen Setter zu verpacken:
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Alles nach dem Aktionssegment wird zu einem `$_REQUEST`-Schlüssel gemäß der P
|
|||||||
// /users/edit/42 → $_REQUEST['id'] = '42'
|
// /users/edit/42 → $_REQUEST['id'] = '42'
|
||||||
public function editAction() {
|
public function editAction() {
|
||||||
$id = (int) ($_REQUEST['id'] ?? 0);
|
$id = (int) ($_REQUEST['id'] ?? 0);
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Für nicht-numerische oder benannte Parameter bevorzugen Sie Abfragezeichenfolgen:
|
Für nicht-numerische oder benannte Parameter bevorzugen Sie Abfragezeichenfolgen:
|
||||||
@@ -62,9 +62,9 @@ Wenn eine URL dem Muster entspricht, werden die Captured Groups den benannten `p
|
|||||||
|
|
||||||
## Routen-Hilfsprogramme
|
## Routen-Hilfsprogramme
|
||||||
```php
|
```php
|
||||||
Router::getInstance()->currentPage(); // 'products'
|
Router::getInstance()->currentPage(); / 'products'
|
||||||
Router::getInstance()->tplName(); // 'products' (controller stem for templates)
|
Router::getInstance()->tplName(); / 'products' (controller stem for templates)
|
||||||
Router::getInstance()->getController(); // alias for currentPage()
|
Router::getInstance()->getController(); / alias for currentPage()
|
||||||
```
|
```
|
||||||
Diese sind nützlich innerhalb von Controllern und Templates:
|
Diese sind nützlich innerhalb von Controllern und Templates:
|
||||||
```smarty
|
```smarty
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ description: "Schaltflächen, Karten, Aufrufe, Heldenbildschirm – bereit zum K
|
|||||||
|
|
||||||
Ein schwarzer Rechteck auf einem cremefarbenen Hintergrund. Editorial. Kein Gradient, kein Glühen.
|
Ein schwarzer Rechteck auf einem cremefarbenen Hintergrund. Editorial. Kein Gradient, kein Glühen.
|
||||||
```html
|
```html
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/de/start/installation/">
|
||||||
<span>Read the docs</span>
|
<span>Read the docs</span>
|
||||||
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -162,7 +162,7 @@ Asymmetrische Gitter mit zwei Spalten. Große Editorial-Nummer hinter dem Text.
|
|||||||
Nibiru is a modular PHP framework for builders who ship.
|
Nibiru is a modular PHP framework for builders who ship.
|
||||||
</p>
|
</p>
|
||||||
<div class="atelier-hero__cta">
|
<div class="atelier-hero__cta">
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/de/start/installation/">
|
||||||
Read the docs <span aria-hidden="true">→</span>
|
Read the docs <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,73 +19,25 @@ hero:
|
|||||||
variant: minimal
|
variant: minimal
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
import CometTrail from '../../../components/CometTrail.astro';
|
||||||
|
import MmvcStage from '../../../components/MmvcStage.astro';
|
||||||
|
import MissionControl from '../../../components/MissionControl.astro';
|
||||||
|
import LaunchSequence from '../../../components/LaunchSequence.astro';
|
||||||
|
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
|
||||||
|
import EditorialContent from '../../../components/EditorialContent.astro';
|
||||||
|
import LandingFooter from '../../../components/LandingFooter.astro';
|
||||||
|
import ToTop from '../../../components/ToTop.astro';
|
||||||
|
import LandingScripts from '../../../components/LandingScripts.astro';
|
||||||
|
|
||||||
## Eine Konstellation von Funktionen
|
<CometTrail />
|
||||||
|
<MmvcStage />
|
||||||
|
<MissionControl />
|
||||||
|
<LaunchSequence />
|
||||||
|
<SpacecraftGrid />
|
||||||
|
<EditorialContent />
|
||||||
|
<LandingFooter />
|
||||||
|
<ToTop />
|
||||||
|
|
||||||
<CardGrid stagger>
|
{/* Loads three.js + the original mockup scene code. MUST be the last node so
|
||||||
<Card title="MMVC-Architektur" icon="puzzle">
|
every #id the scene targets exists in the DOM when the script runs. */}
|
||||||
Module umschließen MVC mit Traits, Plugins, Interfaces und Settings. Das zweite **M** ist lose gekoppelt — und besitzt das Observer-Muster (`SplSubject`) ab Werk.
|
<LandingScripts />
|
||||||
</Card>
|
|
||||||
<Card title="Multi-Datenbank-Kern" icon="seti:db">
|
|
||||||
Fünf Treiber im Orbit: `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) und `postgresql`. Wechsel durch Ändern eines INI-Schlüssels.
|
|
||||||
</Card>
|
|
||||||
<Card title="Smarty Views" icon="document">
|
|
||||||
Template-getriebene Views, ein globaler `View::assign()`-Helfer und ein heißer `templates_c`-Cache. Plus geteilte Partials, Navigations-Includes und Pagination-Templates.
|
|
||||||
</Card>
|
|
||||||
<Card title="Flüssiger Form-Builder" icon="pencil">
|
|
||||||
28+ Feldtypen — Text, Passwort, Switch, Color, Range, File-Upload — komponiert über statische `Form::add…`-Aufrufe und als ein einziger HTML-String gerendert.
|
|
||||||
</Card>
|
|
||||||
<Card title="Echtes CLI-Tool" icon="seti:powershell">
|
|
||||||
`./nibiru` generiert Module, Controller, Plugins, Migrationen und CMS-Seiten. Es führt Migrationen gegen `local`, `staging` oder `production` aus.
|
|
||||||
</Card>
|
|
||||||
<Card title="KI-nativ, demnächst" icon="rocket">
|
|
||||||
Nibiru wird das erste PHP-Framework mit einem eingebauten <a href="/de/ai/oracle/">RAG-basierten Orakel</a>, das auf seinem eigenen Wissen trainiert ist — und einem veröffentlichten Korpus für künftige Fine-Tunes.
|
|
||||||
</Card>
|
|
||||||
</CardGrid>
|
|
||||||
|
|
||||||
## Schnellstart
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Klonen
|
|
||||||
git clone https://github.com/alllinux/Nibiru meine-app && cd meine-app
|
|
||||||
|
|
||||||
# PHP-Abhängigkeiten installieren (Smarty, PHPMailer, Guzzle, …)
|
|
||||||
composer install
|
|
||||||
|
|
||||||
# Berechtigungen + Ordner-Bootstrap
|
|
||||||
./nibiru -s
|
|
||||||
|
|
||||||
# Erste Migration ausführen
|
|
||||||
./nibiru -mi local
|
|
||||||
|
|
||||||
# Controller generieren
|
|
||||||
./nibiru -c products
|
|
||||||
```
|
|
||||||
|
|
||||||
```php
|
|
||||||
// application/controller/productsController.php
|
|
||||||
namespace Nibiru;
|
|
||||||
use Nibiru\Adapter\Controller;
|
|
||||||
|
|
||||||
class productsController extends Controller {
|
|
||||||
public function pageAction() {
|
|
||||||
View::assign([
|
|
||||||
'title' => 'Produkte — Nibiru',
|
|
||||||
'products' => [['id' => 1, 'name' => 'Marduk-Goldbeschichtung']],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
public function navigationAction() {
|
|
||||||
JsonNavigation::getInstance()->loadJsonNavigationArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Wohin als Nächstes?
|
|
||||||
|
|
||||||
<CardGrid>
|
|
||||||
<LinkCard title="Was ist Nibiru?" href="/de/start/what-is-nibiru/" description="Die 90-Sekunden-Tour: MMVC, der Dispatcher, der Request-Lifecycle." />
|
|
||||||
<LinkCard title="Architektur" href="/core/architecture/" description="Wie Module, Controller, Views und das Registry einander umkreisen." />
|
|
||||||
<LinkCard title="Showcase" href="/showcase/projects/" description="Echte Apps in Produktion auf Nibiru — Rechnungswesen, E-Commerce, industrielles PIM." />
|
|
||||||
<LinkCard title="Frag das Orakel" href="/de/ai/oracle/" description="Öffne den schwebenden Chat. Das Orakel ist auf genau dieser Dokumentation geerdet." />
|
|
||||||
</CardGrid>
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Mehrere Tracker ohne Controller-Kopplung.
|
|||||||
$analytics = new Analytics();
|
$analytics = new Analytics();
|
||||||
$analytics->attach(new Plugin\Matomo());
|
$analytics->attach(new Plugin\Matomo());
|
||||||
$analytics->attach(new Plugin\Plausible());
|
$analytics->attach(new Plugin\Plausible());
|
||||||
$analytics->trackPageView(); // calls notify() internally
|
$analytics->trackPageView(); / calls notify() internally
|
||||||
```
|
```
|
||||||
Jeder Beobachter ruft nur die Felder ab, die er interessiert sind. Hinzufügen eines Trackers ist eine Änderung in einer Zeile.
|
Jeder Beobachter ruft nur die Felder ab, die er interessiert sind. Hinzufügen eines Trackers ist eine Änderung in einer Zeile.
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public function detailAction()
|
|||||||
try {
|
try {
|
||||||
$machine = Machine::init()->getMachine((int) $machineId);
|
$machine = Machine::init()->getMachine((int) $machineId);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$machine = null; // DB blip → page still renders with fallback.
|
$machine = null; / DB blip → page still renders with fallback.
|
||||||
}
|
}
|
||||||
|
|
||||||
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
||||||
@@ -110,7 +110,7 @@ Fügen Sie eine neue Smarty-Vorlage ins System ein, der Editor weiß sofort, wel
|
|||||||
|
|
||||||
## Was ist tatsächlich besonderes, zusammengefasst
|
## Was ist tatsächlich besonderes, zusammengefasst
|
||||||
|
|
||||||
Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. Jeder verweist auf seine Beweise auf der Seite [Warum Nibiru](/de/warum-nibiru/).
|
Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. Jeder verweist auf seine Beweise auf der Seite [Warum Nibiru](/de/why-nibiru/).
|
||||||
|
|
||||||
| | Was Nibiru macht | Was Laravel/Symfony macht |
|
| | Was Nibiru macht | Was Laravel/Symfony macht |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -120,7 +120,7 @@ Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. J
|
|||||||
| Authentifizierung | 3 Zeilen im Controller-Konstruktor. | Middleware-Stack + Policy-Klassen + Gates. |
|
| Authentifizierung | 3 Zeilen im Controller-Konstruktor. | Middleware-Stack + Policy-Klassen + Gates. |
|
||||||
| Ereignisse | `SplSubject` + `SplObserver` aus der PHP Standardbibliothek. | Benutzerdefinierter Event-Dispatcher + Listener-Registry + Warteschlange. |
|
| Ereignisse | `SplSubject` + `SplObserver` aus der PHP Standardbibliothek. | Benutzerdefinierter Event-Dispatcher + Listener-Registry + Warteschlange. |
|
||||||
|
|
||||||
Lesen Sie die [vollständige Ausbreakdown mit Codeverweisen →](/de/warum-nibiru/)
|
Lesen Sie die [vollständige Ausbreakdown mit Codeverweisen →](/de/why-nibiru/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
|
|||||||
use Traits\PageBuilderForm;
|
use Traits\PageBuilderForm;
|
||||||
use Traits\CmsPageStructureModifier;
|
use Traits\CmsPageStructureModifier;
|
||||||
use Traits\FormElements;
|
use Traits\FormElements;
|
||||||
// …8 more traits
|
/ …8 more traits
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gesamtwirkung**: keine Konstruktor-Injektion, keine Dienstleistungsanbieterregistrierung, keine Aufrufe von `bind()` / `singleton()` in einer Konfigurationsdatei. Ein neuer Entwickler kann nach dem Trait-Namen suchen und alle Aufrufer sehen.
|
**Gesamtwirkung**: keine Konstruktor-Injektion, keine Dienstleistungsanbieterregistrierung, keine Aufrufe von `bind()` / `singleton()` in einer Konfigurationsdatei. Ein neuer Entwickler kann nach dem Trait-Namen suchen und alle Aufrufer sehen.
|
||||||
@@ -108,7 +108,7 @@ Der API-Controller nimmt den umgekehrten Ansatz: eine Whitelist öffentlicher En
|
|||||||
```php
|
```php
|
||||||
// public endpoints can be listed up-front, auth wraps the rest.
|
// public endpoints can be listed up-front, auth wraps the rest.
|
||||||
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
||||||
// public, skip auth
|
/ public, skip auth
|
||||||
} else {
|
} else {
|
||||||
$this->user = new User();
|
$this->user = new User();
|
||||||
$this->acl = new Acl();
|
$this->acl = new Acl();
|
||||||
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function indexMachines(): void {
|
public function indexMachines(): void {
|
||||||
// …do the work…
|
/ …do the work…
|
||||||
$this->notify(); // analytics, cache invalidator, audit log all see it.
|
$this->notify(); / analytics, cache invalidator, audit log all see it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -154,4 +154,4 @@ Ein einzelner Entwickler oder eine kleine Team kann eine echte Produktions-App b
|
|||||||
|
|
||||||
Wenn Sie lieber Ihren Code sehen möchten, nehmen Sie ihn.
|
Wenn Sie lieber Ihren Code sehen möchten, nehmen Sie ihn.
|
||||||
|
|
||||||
→ [Die Präsentation ansehen →](/de/praesentation/projekte/)
|
→ [Die Präsentation ansehen →](/de/showcase/projects/)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Raw chunks for use as RAG retrieval data:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is exactly the file the [Oracle](/ai/oracle/) uses internally.
|
This is exactly the file the [Oracle](/en/ai/oracle/) uses internally.
|
||||||
|
|
||||||
## How chunks are derived
|
## How chunks are derived
|
||||||
|
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ The Chat plugin is the simplest piece of the AI module. It wraps Ollama's `/api/
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->system('Be terse.'); // optional system prompt
|
$chat->system('Be terse.'); / optional system prompt
|
||||||
$chat->model('qwen2.5-coder:14b'); // override the configured model
|
$chat->model('qwen2.5-coder:14b'); / override the configured model
|
||||||
$chat->temperature(0.2); // override config
|
$chat->temperature(0.2); / override config
|
||||||
$chat->maxTokens(512); // override config
|
$chat->maxTokens(512); / override config
|
||||||
|
|
||||||
$chat->user('Hello'); // append a user message
|
$chat->user('Hello'); / append a user message
|
||||||
$chat->assistant('Hi.'); // append an assistant message (rare)
|
$chat->assistant('Hi.'); / append an assistant message (rare)
|
||||||
|
|
||||||
$reply = $chat->complete(); // run the call, return text
|
$reply = $chat->complete(); / run the call, return text
|
||||||
$reply = $chat->ask('How are you?'); // = ->user(...)->complete()
|
$reply = $chat->ask('How are you?'); / = ->user(...)->complete()
|
||||||
|
|
||||||
$chat->reset(); // clear messages, keep model + system
|
$chat->reset(); / clear messages, keep model + system
|
||||||
$chat->history(); // [{role, content}, …]
|
$chat->history(); / [{role, content}, …]
|
||||||
```
|
```
|
||||||
|
|
||||||
## One-shot
|
## One-shot
|
||||||
@@ -40,10 +40,10 @@ echo (new \Nibiru\Module\Ai\Ai())
|
|||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->user('Name three Nibiru singletons.');
|
$chat->user('Name three Nibiru singletons.');
|
||||||
$singletons = $chat->complete(); // appended to history
|
$singletons = $chat->complete(); / appended to history
|
||||||
|
|
||||||
$chat->user('What does the second one do?');
|
$chat->user('What does the second one do?');
|
||||||
$detail = $chat->complete(); // model has full context
|
$detail = $chat->complete(); / model has full context
|
||||||
```
|
```
|
||||||
|
|
||||||
## Overriding model + style per call
|
## Overriding model + style per call
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ The Embed plugin is a thin wrapper around Ollama's `/api/embeddings` plus three
|
|||||||
```php
|
```php
|
||||||
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
||||||
|
|
||||||
$vec = $embed->one('controller'); // float[]
|
$vec = $embed->one('controller'); / float[]
|
||||||
$vectors = $embed->batch(['a', 'b', 'c']); // float[][]
|
$vectors = $embed->batch(['a', 'b', 'c']); / float[][]
|
||||||
|
|
||||||
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1
|
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[]
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pattern: deduplicate near-duplicate strings
|
## Pattern: deduplicate near-duplicate strings
|
||||||
@@ -54,8 +54,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
|
|||||||
return $best[0];
|
return $best[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication'
|
echo bestTag('User::isAuthorized', $tagVecs, $embed); / → 'authentication'
|
||||||
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably)
|
echo bestTag('Pageination::setTable', $tagVecs, $embed); / → 'database' (probably)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
@@ -65,7 +65,7 @@ Embeddings are float arrays — typically 768 floats for `nomic-embed-text`, 102
|
|||||||
Use `Embed::pack()` to base64-encode them as 4-byte floats:
|
Use `Embed::pack()` to base64-encode them as 4-byte floats:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string
|
$compact = Embed::pack($vec); / ~4 KB → ~5.3 KB base64 string
|
||||||
$vec = Embed::unpack($compact);
|
$vec = Embed::unpack($compact);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
|
|||||||
// Multi-turn
|
// Multi-turn
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
$chat->user('How do I scaffold a module?');
|
$chat->user('How do I scaffold a module?');
|
||||||
$chat->user('And add Graylog hooks?'); // referrs to previous turn
|
$chat->user('And add Graylog hooks?'); / referrs to previous turn
|
||||||
echo $chat->complete();
|
echo $chat->complete();
|
||||||
|
|
||||||
// Override per call
|
// Override per call
|
||||||
@@ -89,7 +89,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
|
|||||||
Compact storage:
|
Compact storage:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
|||||||
$rag = $ai->rag('product-help');
|
$rag = $ai->rag('product-help');
|
||||||
|
|
||||||
// One-time ingestion
|
// One-time ingestion
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
$rag->ingestFile('/var/data/manual.pdf.txt');
|
$rag->ingestFile('/var/data/manual.pdf.txt');
|
||||||
|
|
||||||
@@ -118,9 +118,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
|
|
||||||
$agent = $ai->agent()->withTools([
|
$agent = $ai->agent()->withTools([
|
||||||
new Tools\PdoQuery(), // read-only SQL
|
new Tools\PdoQuery(), / read-only SQL
|
||||||
new Tools\HttpGet(), // fetch URLs
|
new Tools\HttpGet(), / fetch URLs
|
||||||
new Tools\FileRead(), // read project files
|
new Tools\FileRead(), / read project files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $agent->run('How many active users registered last week?');
|
echo $agent->run('How many active users registered last week?');
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ The RAG plugin is the AI module's killer feature for product builders. It turns
|
|||||||
use Nibiru\Module\Ai\Ai;
|
use Nibiru\Module\Ai\Ai;
|
||||||
|
|
||||||
$ai = new Ai();
|
$ai = new Ai();
|
||||||
$rag = $ai->rag('product-help'); // a named collection
|
$rag = $ai->rag('product-help'); / a named collection
|
||||||
|
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
|
|
||||||
echo $rag->ask('How do I cancel my subscription?');
|
echo $rag->ask('How do I cancel my subscription?');
|
||||||
@@ -53,20 +53,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
|
|||||||
## API reference
|
## API reference
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$rag = $ai->rag('name'); // get/create a named collection
|
$rag = $ai->rag('name'); / get/create a named collection
|
||||||
|
|
||||||
// --- Ingestion ---
|
// --- Ingestion ---
|
||||||
$rag->ingestText($text, $metadata = []); // single chunk
|
$rag->ingestText($text, $metadata = []); / single chunk
|
||||||
$count = $rag->ingestFile('path'); // returns chunks added
|
$count = $rag->ingestFile('path'); / returns chunks added
|
||||||
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive
|
$count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
|
||||||
|
|
||||||
// --- Querying ---
|
// --- Querying ---
|
||||||
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …]
|
$hits = $rag->search('query', $k = null); / [{score, text, metadata}, …]
|
||||||
$answer = $rag->ask('question', $k = null); // top-K → chat call
|
$answer = $rag->ask('question', $k = null); / top-K → chat call
|
||||||
|
|
||||||
// --- Maintenance ---
|
// --- Maintenance ---
|
||||||
$rag->reset(); // forget everything (deletes file)
|
$rag->reset(); / forget everything (deletes file)
|
||||||
$n = $rag->size(); // number of chunks
|
$n = $rag->size(); / number of chunks
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tuning knobs
|
## Tuning knobs
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public function pageAction() {
|
|||||||
View::forwardTo('/login');
|
View::forwardTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ public function submitAction() {
|
|||||||
http_response_code(419);
|
http_response_code(419);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...handle submission...
|
/ ...handle submission...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -87,14 +87,14 @@ password_hash = "another-salt-for-AES_DECRYPT"
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
||||||
$cfg['DATABASE']['driver']; // 'pdo'
|
$cfg['DATABASE']['driver']; / 'pdo'
|
||||||
$cfg['SETTINGS']['page.url']; // 'https://my-app.local'
|
$cfg['SETTINGS']['page.url']; / 'https://my-app.local'
|
||||||
```
|
```
|
||||||
|
|
||||||
For deeply-nested configs use the typed constants from the View interface:
|
For deeply-nested configs use the typed constants from the View interface:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css']
|
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
|
||||||
```
|
```
|
||||||
|
|
||||||
## Module configs
|
## Module configs
|
||||||
@@ -103,7 +103,7 @@ Each module under `application/module/<name>/settings/` can carry its own INI fi
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime; // from [USERS] section in users.ini
|
$users->session_lifetime; / from [USERS] section in users.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
The Registry prefers `<module>.<env>.ini` over `<module>.ini`, so you get per-environment overrides for free.
|
The Registry prefers `<module>.<env>.ini` over `<module>.ini`, so you get per-environment overrides for free.
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ View::assign(['products' => $list]);
|
|||||||
Convenience helpers from the base controller:
|
Convenience helpers from the base controller:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->getRequest('id', false); // $_REQUEST['id'] ?? false
|
$this->getRequest('id', false); / $_REQUEST['id'] ?? false
|
||||||
$this->getPost('email', ''); // $_POST['email'] ?? ''
|
$this->getPost('email', ''); / $_POST['email'] ?? ''
|
||||||
$this->getGet('page', 1); // $_GET['page'] ?? 1
|
$this->getGet('page', 1); / $_GET['page'] ?? 1
|
||||||
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI']
|
$this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
|
||||||
$this->getFiles('upload'); // $_FILES['upload']
|
$this->getFiles('upload'); / $_FILES['upload']
|
||||||
$this->getSession('auth'); // $_SESSION['auth']
|
$this->getSession('auth'); / $_SESSION['auth']
|
||||||
```
|
```
|
||||||
|
|
||||||
These exist because `Controller` is `final`-friendly: you can mock them in tests by substituting a child class.
|
These exist because `Controller` is `final`-friendly: you can mock them in tests by substituting a child class.
|
||||||
@@ -76,8 +76,8 @@ These exist because `Controller` is `final`-friendly: you can mock them in tests
|
|||||||
To redirect inside an action:
|
To redirect inside an action:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
View::forwardTo('/login'); // 302 to the URL, exits
|
View::forwardTo('/login'); / 302 to the URL, exits
|
||||||
View::forwardToJsonHeader(); // sets Content-Type: application/json
|
View::forwardToJsonHeader(); / sets Content-Type: application/json
|
||||||
```
|
```
|
||||||
|
|
||||||
`forwardToJsonHeader()` is the canonical pattern for JSON endpoints — set the header, assign `data`, return. The view layer does the rest.
|
`forwardToJsonHeader()` is the canonical pattern for JSON endpoints — set the header, assign `data`, return. The view layer does the rest.
|
||||||
|
|||||||
@@ -35,17 +35,17 @@ public function run() {
|
|||||||
|
|
||||||
if (Config::getInstance()->getConfig()
|
if (Config::getInstance()->getConfig()
|
||||||
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
||||||
new Model(false); // 1. (re)generate models from schema
|
new Model(false); / 1. (re)generate models from schema
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::getInstance()->route(); // 2. parse the URL
|
Router::getInstance()->route(); / 2. parse the URL
|
||||||
Auto::loader()->loadModelFiles(); // 3. load model files
|
Auto::loader()->loadModelFiles(); / 3. load model files
|
||||||
Auto::loader()->loadModules(); // 4. load module classes
|
Auto::loader()->loadModules(); / 4. load module classes
|
||||||
|
|
||||||
$tpl = Router::getInstance()->tplName();
|
$tpl = Router::getInstance()->tplName();
|
||||||
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
||||||
|
|
||||||
if (is_file($controllerFile)) { // 5. controller file exists
|
if (is_file($controllerFile)) { / 5. controller file exists
|
||||||
require_once $controllerFile;
|
require_once $controllerFile;
|
||||||
$class = "Nibiru\\{$tpl}Controller";
|
$class = "Nibiru\\{$tpl}Controller";
|
||||||
$controller = new $class();
|
$controller = new $class();
|
||||||
@@ -54,7 +54,7 @@ public function run() {
|
|||||||
$action = $_REQUEST['_action'] . 'Action';
|
$action = $_REQUEST['_action'] . 'Action';
|
||||||
$controller->navigationAction();
|
$controller->navigationAction();
|
||||||
if (method_exists($controller, $action)) {
|
if (method_exists($controller, $action)) {
|
||||||
$controller->$action(); // 6. optional named action
|
$controller->$action(); / 6. optional named action
|
||||||
}
|
}
|
||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -62,9 +62,9 @@ public function run() {
|
|||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::getInstance()->display(); // 7. render Smarty
|
Display::getInstance()->display(); / 7. render Smarty
|
||||||
} else {
|
} else {
|
||||||
// 8. soft 404 — render the configured error controller
|
/ 8. soft 404 — render the configured error controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ addOpenSpan addCloseSpan
|
|||||||
### Lifecycle
|
### Lifecycle
|
||||||
|
|
||||||
```
|
```
|
||||||
create // reset the static buffer; call before building a new form
|
create / reset the static buffer; call before building a new form
|
||||||
addForm // wrap the buffer in <form>...</form> and return as a string
|
addForm / wrap the buffer in <form>...</form> and return as a string
|
||||||
```
|
```
|
||||||
|
|
||||||
:::caution[The naming inconsistency is intentional, sort of]
|
:::caution[The naming inconsistency is intentional, sort of]
|
||||||
@@ -64,7 +64,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
|
|||||||
```php
|
```php
|
||||||
use Nibiru\Factory\Form;
|
use Nibiru\Factory\Form;
|
||||||
|
|
||||||
Form::create(); // reset the static buffer
|
Form::create(); / reset the static buffer
|
||||||
|
|
||||||
Form::addOpenDiv(['class' => 'form-group']);
|
Form::addOpenDiv(['class' => 'form-group']);
|
||||||
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
||||||
@@ -220,7 +220,7 @@ class formsController extends Controller {
|
|||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
'class' => 'contacts-input form-control',
|
'class' => 'contacts-input form-control',
|
||||||
]);
|
]);
|
||||||
// ...more fields...
|
/ ...more fields...
|
||||||
$this->form = Form::addForm([
|
$this->form = Form::addForm([
|
||||||
'name' => 'newregister',
|
'name' => 'newregister',
|
||||||
'method' => 'post',
|
'method' => 'post',
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ Read it back from anywhere:
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$cfg->session_lifetime; // 7200
|
$cfg->session_lifetime; / 7200
|
||||||
$cfg->password_min_length; // 12
|
$cfg->password_min_length; / 12
|
||||||
$cfg->allowed_roles; // [admin, editor, standard]
|
$cfg->allowed_roles; / [admin, editor, standard]
|
||||||
```
|
```
|
||||||
|
|
||||||
Environment overlays: a file named `users.production.ini` is preferred over `users.ini` when `APPLICATION_ENV=production`.
|
Environment overlays: a file named `users.production.ini` is preferred over `users.ini` when `APPLICATION_ENV=production`.
|
||||||
@@ -177,7 +177,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
|
|||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
||||||
|
|
||||||
$analytics->trackPageView(); // internally calls notify()
|
$analytics->trackPageView(); / internally calls notify()
|
||||||
```
|
```
|
||||||
|
|
||||||
Each observer's `update($subject)` receives the analytics instance and pulls the event data it cares about.
|
Each observer's `update($subject)` receives the analytics instance and pulls the event data it cares about.
|
||||||
@@ -219,7 +219,7 @@ Plugin classes live under the **plural** namespace `Plugins`:
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
// application/module/billing/plugins/invoice.php
|
// application/module/billing/plugins/invoice.php
|
||||||
namespace Nibiru\Module\Billing\Plugins; // ← plural
|
namespace Nibiru\Module\Billing\Plugins; / ← plural
|
||||||
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ You **don't** register your module's INI files — the [Registry](/en/core/regis
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
||||||
$cfg->invoice_prefix; // [BILLING] invoice.prefix → property
|
$cfg->invoice_prefix; / [BILLING] invoice.prefix → property
|
||||||
```
|
```
|
||||||
|
|
||||||
The Registry prefers `<module>.<env>.ini` (e.g. `billing.production.ini`) when `APPLICATION_ENV` matches.
|
The Registry prefers `<module>.<env>.ini` (e.g. `billing.production.ini`) when `APPLICATION_ENV` matches.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class productsController extends Controller
|
|||||||
{
|
{
|
||||||
public function pageAction() {
|
public function pageAction() {
|
||||||
$products = new products();
|
$products = new products();
|
||||||
Pageination::setEntriesPerPage(25); // optional; default from INI
|
Pageination::setEntriesPerPage(25); / optional; default from INI
|
||||||
Pageination::setTable($products);
|
Pageination::setTable($products);
|
||||||
$rows = Pageination::loadTableAsArray();
|
$rows = Pageination::loadTableAsArray();
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ For each module under `application/module/<name>/settings/`:
|
|||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime;
|
$users->session_lifetime;
|
||||||
$users->password_min_length;
|
$users->password_min_length;
|
||||||
$users->allowed_roles; // array
|
$users->allowed_roles; / array
|
||||||
```
|
```
|
||||||
|
|
||||||
Inside the module itself the convention is to wrap this in a setter:
|
Inside the module itself the convention is to wrap this in a setter:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Anything past the action segment becomes a `$_REQUEST` key paired by position:
|
|||||||
// /users/edit/42 → $_REQUEST['id'] = '42'
|
// /users/edit/42 → $_REQUEST['id'] = '42'
|
||||||
public function editAction() {
|
public function editAction() {
|
||||||
$id = (int) ($_REQUEST['id'] ?? 0);
|
$id = (int) ($_REQUEST['id'] ?? 0);
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -75,9 +75,9 @@ When a URL matches the pattern, capture groups are assigned to the named `params
|
|||||||
## Routing helpers
|
## Routing helpers
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Router::getInstance()->currentPage(); // 'products'
|
Router::getInstance()->currentPage(); / 'products'
|
||||||
Router::getInstance()->tplName(); // 'products' (controller stem for templates)
|
Router::getInstance()->tplName(); / 'products' (controller stem for templates)
|
||||||
Router::getInstance()->getController(); // alias for currentPage()
|
Router::getInstance()->getController(); / alias for currentPage()
|
||||||
```
|
```
|
||||||
|
|
||||||
These are useful inside controllers and templates:
|
These are useful inside controllers and templates:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ description: Buttons, cards, callouts, hero — copy-paste ready.
|
|||||||
A black-on-cream rectangle. Editorial. No gradient, no glow.
|
A black-on-cream rectangle. Editorial. No gradient, no glow.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/en/start/installation/">
|
||||||
<span>Read the docs</span>
|
<span>Read the docs</span>
|
||||||
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -179,7 +179,7 @@ Asymmetric two-column grid. Big editorial number behind the copy. The brand mark
|
|||||||
Nibiru is a modular PHP framework for builders who ship.
|
Nibiru is a modular PHP framework for builders who ship.
|
||||||
</p>
|
</p>
|
||||||
<div class="atelier-hero__cta">
|
<div class="atelier-hero__cta">
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/en/start/installation/">
|
||||||
Read the docs <span aria-hidden="true">→</span>
|
Read the docs <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ description: Downloadable Nibiru framework training corpus — chunks, instructi
|
|||||||
import DownloadsManifest from '../../../components/DownloadsManifest.astro';
|
import DownloadsManifest from '../../../components/DownloadsManifest.astro';
|
||||||
|
|
||||||
A pre-built training corpus for fine-tuning your own LoRA on Nibiru. Generated
|
A pre-built training corpus for fine-tuning your own LoRA on Nibiru. Generated
|
||||||
deterministically from two sources: the deep [framework reference](/en/reference/)
|
deterministically from two sources: the deep [framework reference](https://github.com/alllinux/Nibiru/blob/master/docs/scripts/extraction/framework-reference-v2.md)
|
||||||
(every public factory, namespace, idiom and gotcha cited file:line) and the
|
(every public factory, namespace, idiom and gotcha cited file:line) and the
|
||||||
public docs in five languages.
|
public docs in five languages.
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Multiple trackers without controller coupling.
|
|||||||
$analytics = new Analytics();
|
$analytics = new Analytics();
|
||||||
$analytics->attach(new Plugin\Matomo());
|
$analytics->attach(new Plugin\Matomo());
|
||||||
$analytics->attach(new Plugin\Plausible());
|
$analytics->attach(new Plugin\Plausible());
|
||||||
$analytics->trackPageView(); // calls notify() internally
|
$analytics->trackPageView(); / calls notify() internally
|
||||||
```
|
```
|
||||||
|
|
||||||
Each observer's `update($subject)` pulls only the fields it cares about. Adding a tracker is a one-line change.
|
Each observer's `update($subject)` pulls only the fields it cares about. Adding a tracker is a one-line change.
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public function detailAction()
|
|||||||
try {
|
try {
|
||||||
$machine = Machine::init()->getMachine((int) $machineId);
|
$machine = Machine::init()->getMachine((int) $machineId);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$machine = null; // DB blip → page still renders with fallback.
|
$machine = null; / DB blip → page still renders with fallback.
|
||||||
}
|
}
|
||||||
|
|
||||||
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class Cms implements Interfaces\Cms, SplSubject
|
|||||||
use Traits\PageBuilderForm;
|
use Traits\PageBuilderForm;
|
||||||
use Traits\CmsPageStructureModifier;
|
use Traits\CmsPageStructureModifier;
|
||||||
use Traits\FormElements;
|
use Traits\FormElements;
|
||||||
// …8 more traits
|
/ …8 more traits
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ The API controller takes the inverse approach: a whitelist of public endpoints i
|
|||||||
```php
|
```php
|
||||||
// public endpoints can be listed up-front, auth wraps the rest.
|
// public endpoints can be listed up-front, auth wraps the rest.
|
||||||
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
||||||
// public, skip auth
|
/ public, skip auth
|
||||||
} else {
|
} else {
|
||||||
$this->user = new User();
|
$this->user = new User();
|
||||||
$this->acl = new Acl();
|
$this->acl = new Acl();
|
||||||
@@ -148,8 +148,8 @@ class Machineryscout implements IModule, \SplSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function indexMachines(): void {
|
public function indexMachines(): void {
|
||||||
// …do the work…
|
/ …do the work…
|
||||||
$this->notify(); // analytics, cache invalidator, audit log all see it.
|
$this->notify(); / analytics, cache invalidator, audit log all see it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Fragmentos sin procesar para su uso como datos de recuperación RAG:
|
|||||||
"content": "Modules implementing `SplSubject` can broadcast events…"
|
"content": "Modules implementing `SplSubject` can broadcast events…"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Este es exactamente el archivo que usa internamente [Oracle](/ai/oracle/).
|
Este es exactamente el archivo que usa internamente [Oracle](/es/ai/oracle/).
|
||||||
|
|
||||||
## Cómo se derivan los fragmentos
|
## Cómo se derivan los fragmentos
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ El complemento de chat es la pieza más simple del módulo de IA. Envuelve la AP
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->system('Be terse.'); // optional system prompt
|
$chat->system('Be terse.'); / optional system prompt
|
||||||
$chat->model('qwen2.5-coder:14b'); // override the configured model
|
$chat->model('qwen2.5-coder:14b'); / override the configured model
|
||||||
$chat->temperature(0.2); // override config
|
$chat->temperature(0.2); / override config
|
||||||
$chat->maxTokens(512); // override config
|
$chat->maxTokens(512); / override config
|
||||||
|
|
||||||
$chat->user('Hello'); // append a user message
|
$chat->user('Hello'); / append a user message
|
||||||
$chat->assistant('Hi.'); // append an assistant message (rare)
|
$chat->assistant('Hi.'); / append an assistant message (rare)
|
||||||
|
|
||||||
$reply = $chat->complete(); // run the call, return text
|
$reply = $chat->complete(); / run the call, return text
|
||||||
$reply = $chat->ask('How are you?'); // = ->user(...)->complete()
|
$reply = $chat->ask('How are you?'); / = ->user(...)->complete()
|
||||||
|
|
||||||
$chat->reset(); // clear messages, keep model + system
|
$chat->reset(); / clear messages, keep model + system
|
||||||
$chat->history(); // [{role, content}, …]
|
$chat->history(); / [{role, content}, …]
|
||||||
```
|
```
|
||||||
## En un solo paso
|
## En un solo paso
|
||||||
```php
|
```php
|
||||||
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
|
|||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->user('Name three Nibiru singletons.');
|
$chat->user('Name three Nibiru singletons.');
|
||||||
$singletons = $chat->complete(); // appended to history
|
$singletons = $chat->complete(); / appended to history
|
||||||
|
|
||||||
$chat->user('What does the second one do?');
|
$chat->user('What does the second one do?');
|
||||||
$detail = $chat->complete(); // model has full context
|
$detail = $chat->complete(); / model has full context
|
||||||
```
|
```
|
||||||
## Sobrescribir modelo y estilo por llamada
|
## Sobrescribir modelo y estilo por llamada
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ El complemento Embed es un envoltorio ligero alrededor de `/api/embeddings` de O
|
|||||||
```php
|
```php
|
||||||
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
||||||
|
|
||||||
$vec = $embed->one('controller'); // float[]
|
$vec = $embed->one('controller'); / float[]
|
||||||
$vectors = $embed->batch(['a', 'b', 'c']); // float[][]
|
$vectors = $embed->batch(['a', 'b', 'c']); / float[][]
|
||||||
|
|
||||||
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1
|
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[]
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
|
||||||
```
|
```
|
||||||
## Patrón: eliminar cadenas similares
|
## Patrón: eliminar cadenas similares
|
||||||
```php
|
```php
|
||||||
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
|
|||||||
return $best[0];
|
return $best[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication'
|
echo bestTag('User::isAuthorized', $tagVecs, $embed); / → 'authentication'
|
||||||
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably)
|
echo bestTag('Pageination::setTable', $tagVecs, $embed); / → 'database' (probably)
|
||||||
```
|
```
|
||||||
## Almacenamiento
|
## Almacenamiento
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ Las incrustaciones son arreglos de flotantes — generalmente 768 flotantes para
|
|||||||
|
|
||||||
Utiliza `Embed::pack()` para codificarlos en base64 como flotantes de 4 bytes:
|
Utiliza `Embed::pack()` para codificarlos en base64 como flotantes de 4 bytes:
|
||||||
```php
|
```php
|
||||||
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string
|
$compact = Embed::pack($vec); / ~4 KB → ~5.3 KB base64 string
|
||||||
$vec = Embed::unpack($compact);
|
$vec = Embed::unpack($compact);
|
||||||
```
|
```
|
||||||
El complemento RAG utiliza este formato internamente para sus archivos JSON.
|
El complemento RAG utiliza este formato internamente para sus archivos JSON.
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
|
|||||||
// Multi-turn
|
// Multi-turn
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
$chat->user('How do I scaffold a module?');
|
$chat->user('How do I scaffold a module?');
|
||||||
$chat->user('And add Graylog hooks?'); // referrs to previous turn
|
$chat->user('And add Graylog hooks?'); / referrs to previous turn
|
||||||
echo $chat->complete();
|
echo $chat->complete();
|
||||||
|
|
||||||
// Override per call
|
// Override per call
|
||||||
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
|
|||||||
```
|
```
|
||||||
Almacenamiento compacto:
|
Almacenamiento compacto:
|
||||||
```php
|
```php
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
||||||
```
|
```
|
||||||
### 3. RAG — ingesta, recuperación, anclaje
|
### 3. RAG — ingesta, recuperación, anclaje
|
||||||
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
|||||||
$rag = $ai->rag('product-help');
|
$rag = $ai->rag('product-help');
|
||||||
|
|
||||||
// One-time ingestion
|
// One-time ingestion
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
$rag->ingestFile('/var/data/manual.pdf.txt');
|
$rag->ingestFile('/var/data/manual.pdf.txt');
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
|
|
||||||
$agent = $ai->agent()->withTools([
|
$agent = $ai->agent()->withTools([
|
||||||
new Tools\PdoQuery(), // read-only SQL
|
new Tools\PdoQuery(), / read-only SQL
|
||||||
new Tools\HttpGet(), // fetch URLs
|
new Tools\HttpGet(), / fetch URLs
|
||||||
new Tools\FileRead(), // read project files
|
new Tools\FileRead(), / read project files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $agent->run('How many active users registered last week?');
|
echo $agent->run('How many active users registered last week?');
|
||||||
@@ -154,7 +154,7 @@ La filosofía de diseño:
|
|||||||
|
|
||||||
## Siguiente
|
## Siguiente
|
||||||
|
|
||||||
- [Referencia del complemento de chat](/es/ai/modulo/chat/)
|
- [Referencia del complemento de chat](/es/ai/module/chat/)
|
||||||
- [Referencia del complemento RAG](/es/ai/modulo/rag/)
|
- [Referencia del complemento RAG](/es/ai/module/rag/)
|
||||||
- [Referencia del complemento de agente](/es/ai/modulo/agent/)
|
- [Referencia del complemento de agente](/es/ai/module/agent/)
|
||||||
- [Entrenamiento nibiru-coder](/es/ai/modulo/training/)
|
- [Entrenamiento nibiru-coder](/es/ai/module/training/)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ El complemento RAG es la característica clave del módulo de IA para los constr
|
|||||||
use Nibiru\Module\Ai\Ai;
|
use Nibiru\Module\Ai\Ai;
|
||||||
|
|
||||||
$ai = new Ai();
|
$ai = new Ai();
|
||||||
$rag = $ai->rag('product-help'); // a named collection
|
$rag = $ai->rag('product-help'); / a named collection
|
||||||
|
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
|
|
||||||
echo $rag->ask('How do I cancel my subscription?');
|
echo $rag->ask('How do I cancel my subscription?');
|
||||||
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
|
|||||||
```
|
```
|
||||||
## Referencia de la API
|
## Referencia de la API
|
||||||
```php
|
```php
|
||||||
$rag = $ai->rag('name'); // get/create a named collection
|
$rag = $ai->rag('name'); / get/create a named collection
|
||||||
|
|
||||||
// --- Ingestion ---
|
// --- Ingestion ---
|
||||||
$rag->ingestText($text, $metadata = []); // single chunk
|
$rag->ingestText($text, $metadata = []); / single chunk
|
||||||
$count = $rag->ingestFile('path'); // returns chunks added
|
$count = $rag->ingestFile('path'); / returns chunks added
|
||||||
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive
|
$count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
|
||||||
|
|
||||||
// --- Querying ---
|
// --- Querying ---
|
||||||
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …]
|
$hits = $rag->search('query', $k = null); / [{score, text, metadata}, …]
|
||||||
$answer = $rag->ask('question', $k = null); // top-K → chat call
|
$answer = $rag->ask('question', $k = null); / top-K → chat call
|
||||||
|
|
||||||
// --- Maintenance ---
|
// --- Maintenance ---
|
||||||
$rag->reset(); // forget everything (deletes file)
|
$rag->reset(); / forget everything (deletes file)
|
||||||
$n = $rag->size(); // number of chunks
|
$n = $rag->size(); / number of chunks
|
||||||
```
|
```
|
||||||
## Perillas de ajuste
|
## Perillas de ajuste
|
||||||
|
|
||||||
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
|
|||||||
|
|
||||||
## ¿Qué sigue?
|
## ¿Qué sigue?
|
||||||
|
|
||||||
- [Plugin de agente →](/es/ai/modulo/agente/) para herramientas, no recuperación.
|
- [Plugin de agente →](/es/ai/module/agent/) para herramientas, no recuperación.
|
||||||
- [Entrenamiento nibiru-coder →](/es/ai/modulo/entrenamiento/) para que el chat responda a la mitad en voz del marco.
|
- [Entrenamiento nibiru-coder →](/es/ai/module/training/) para que el chat responda a la mitad en voz del marco.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public function pageAction() {
|
|||||||
View::forwardTo('/login');
|
View::forwardTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Para las verificaciones basadas en roles, las aplicaciones de demostración utilizan el complemento `Acl` del mismo módulo:
|
Para las verificaciones basadas en roles, las aplicaciones de demostración utilizan el complemento `Acl` del mismo módulo:
|
||||||
@@ -178,7 +178,7 @@ public function submitAction() {
|
|||||||
http_response_code(419);
|
http_response_code(419);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...handle submission...
|
/ ...handle submission...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Incorpore `<input type="hidden" name="csrf" value="{$csrf}">` en su formulario.
|
Incorpore `<input type="hidden" name="csrf" value="{$csrf}">` en su formulario.
|
||||||
|
|||||||
@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
|
|||||||
## Leyendo la configuración
|
## Leyendo la configuración
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
||||||
$cfg['DATABASE']['driver']; // 'pdo'
|
$cfg['DATABASE']['driver']; / 'pdo'
|
||||||
$cfg['SETTINGS']['page.url']; // 'https://my-app.local'
|
$cfg['SETTINGS']['page.url']; / 'https://my-app.local'
|
||||||
```
|
```
|
||||||
Para configuraciones profundamente anidadas, utilice las constantes tipadas de la interfaz View:
|
Para configuraciones profundamente anidadas, utilice las constantes tipadas de la interfaz View:
|
||||||
```php
|
```php
|
||||||
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css']
|
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
|
||||||
```
|
```
|
||||||
## Configuraciones del módulo
|
## Configuraciones del módulo
|
||||||
|
|
||||||
Cada módulo bajo `application/module/<nombre>/settings/` puede llevar sus propios archivos INI. El Registro los detecta automáticamente y los expone a través de:
|
Cada módulo bajo `application/module/<nombre>/settings/` puede llevar sus propios archivos INI. El Registro los detecta automáticamente y los expone a través de:
|
||||||
```php
|
```php
|
||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime; // from [USERS] section in users.ini
|
$users->session_lifetime; / from [USERS] section in users.ini
|
||||||
```
|
```
|
||||||
El Registro prefiere `<módulo>.<entorno>.ini` sobre `<módulo>.ini`, por lo que obtienes anulaciones por entorno de forma gratuita.
|
El Registro prefiere `<módulo>.<entorno>.ini` sobre `<módulo>.ini`, por lo que obtienes anulaciones por entorno de forma gratuita.
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
|
|||||||
```
|
```
|
||||||
Ayudantes de conveniencia del controlador base:
|
Ayudantes de conveniencia del controlador base:
|
||||||
```php
|
```php
|
||||||
$this->getRequest('id', false); // $_REQUEST['id'] ?? false
|
$this->getRequest('id', false); / $_REQUEST['id'] ?? false
|
||||||
$this->getPost('email', ''); // $_POST['email'] ?? ''
|
$this->getPost('email', ''); / $_POST['email'] ?? ''
|
||||||
$this->getGet('page', 1); // $_GET['page'] ?? 1
|
$this->getGet('page', 1); / $_GET['page'] ?? 1
|
||||||
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI']
|
$this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
|
||||||
$this->getFiles('upload'); // $_FILES['upload']
|
$this->getFiles('upload'); / $_FILES['upload']
|
||||||
$this->getSession('auth'); // $_SESSION['auth']
|
$this->getSession('auth'); / $_SESSION['auth']
|
||||||
```
|
```
|
||||||
Estos existen porque `Controller` es amigable con `final`: puedes simularlos en pruebas sustituyendo una clase hija.
|
Estos existen porque `Controller` es amigable con `final`: puedes simularlos en pruebas sustituyendo una clase hija.
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ Estos existen porque `Controller` es amigable con `final`: puedes simularlos en
|
|||||||
|
|
||||||
Para redirigir dentro de una acción:
|
Para redirigir dentro de una acción:
|
||||||
```php
|
```php
|
||||||
View::forwardTo('/login'); // 302 to the URL, exits
|
View::forwardTo('/login'); / 302 to the URL, exits
|
||||||
View::forwardToJsonHeader(); // sets Content-Type: application/json
|
View::forwardToJsonHeader(); / sets Content-Type: application/json
|
||||||
```
|
```
|
||||||
`forwardToJsonHeader()` es el patrón canónico para puntos finales JSON — establece la cabecera, asigna `data`, y devuelve. La capa de vista se encarga del resto.
|
`forwardToJsonHeader()` es el patrón canónico para puntos finales JSON — establece la cabecera, asigna `data`, y devuelve. La capa de vista se encarga del resto.
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ public function run() {
|
|||||||
|
|
||||||
if (Config::getInstance()->getConfig()
|
if (Config::getInstance()->getConfig()
|
||||||
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
||||||
new Model(false); // 1. (re)generate models from schema
|
new Model(false); / 1. (re)generate models from schema
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::getInstance()->route(); // 2. parse the URL
|
Router::getInstance()->route(); / 2. parse the URL
|
||||||
Auto::loader()->loadModelFiles(); // 3. load model files
|
Auto::loader()->loadModelFiles(); / 3. load model files
|
||||||
Auto::loader()->loadModules(); // 4. load module classes
|
Auto::loader()->loadModules(); / 4. load module classes
|
||||||
|
|
||||||
$tpl = Router::getInstance()->tplName();
|
$tpl = Router::getInstance()->tplName();
|
||||||
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
||||||
|
|
||||||
if (is_file($controllerFile)) { // 5. controller file exists
|
if (is_file($controllerFile)) { / 5. controller file exists
|
||||||
require_once $controllerFile;
|
require_once $controllerFile;
|
||||||
$class = "Nibiru\\{$tpl}Controller";
|
$class = "Nibiru\\{$tpl}Controller";
|
||||||
$controller = new $class();
|
$controller = new $class();
|
||||||
@@ -49,7 +49,7 @@ public function run() {
|
|||||||
$action = $_REQUEST['_action'] . 'Action';
|
$action = $_REQUEST['_action'] . 'Action';
|
||||||
$controller->navigationAction();
|
$controller->navigationAction();
|
||||||
if (method_exists($controller, $action)) {
|
if (method_exists($controller, $action)) {
|
||||||
$controller->$action(); // 6. optional named action
|
$controller->$action(); / 6. optional named action
|
||||||
}
|
}
|
||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -57,9 +57,9 @@ public function run() {
|
|||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::getInstance()->display(); // 7. render Smarty
|
Display::getInstance()->display(); / 7. render Smarty
|
||||||
} else {
|
} else {
|
||||||
// 8. soft 404 — render the configured error controller
|
/ 8. soft 404 — render the configured error controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
|
|||||||
```
|
```
|
||||||
### Ciclo de vida
|
### Ciclo de vida
|
||||||
```
|
```
|
||||||
create // reset the static buffer; call before building a new form
|
create / reset the static buffer; call before building a new form
|
||||||
addForm // wrap the buffer in <form>...</form> and return as a string
|
addForm / wrap the buffer in <form>...</form> and return as a string
|
||||||
```
|
```
|
||||||
:::caution[La inconsistencia de nombres es intencional, más bien]
|
:::caution[La inconsistencia de nombres es intencional, más bien]
|
||||||
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — el prefijo coincide si el elemento es un `<input type="…">` (prefijo Input) o alguna otra etiqueta (prefijo Type). Es incómodo pero estable; la CLI de `./nibiru -c` usa el mismo patrón, por lo que la memoria muscular funciona.
|
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — el prefijo coincide si el elemento es un `<input type="…">` (prefijo Input) o alguna otra etiqueta (prefijo Type). Es incómodo pero estable; la CLI de `./nibiru -c` usa el mismo patrón, por lo que la memoria muscular funciona.
|
||||||
@@ -50,7 +50,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
|
|||||||
```php
|
```php
|
||||||
use Nibiru\Factory\Form;
|
use Nibiru\Factory\Form;
|
||||||
|
|
||||||
Form::create(); // reset the static buffer
|
Form::create(); / reset the static buffer
|
||||||
|
|
||||||
Form::addOpenDiv(['class' => 'form-group']);
|
Form::addOpenDiv(['class' => 'form-group']);
|
||||||
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
||||||
@@ -186,7 +186,7 @@ class formsController extends Controller {
|
|||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
'class' => 'contacts-input form-control',
|
'class' => 'contacts-input form-control',
|
||||||
]);
|
]);
|
||||||
// ...more fields...
|
/ ...more fields...
|
||||||
$this->form = Form::addForm([
|
$this->form = Form::addForm([
|
||||||
'name' => 'newregister',
|
'name' => 'newregister',
|
||||||
'method' => 'post',
|
'method' => 'post',
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
|
|||||||
Léelo desde cualquier lugar:
|
Léelo desde cualquier lugar:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$cfg->session_lifetime; // 7200
|
$cfg->session_lifetime; / 7200
|
||||||
$cfg->password_min_length; // 12
|
$cfg->password_min_length; / 12
|
||||||
$cfg->allowed_roles; // [admin, editor, standard]
|
$cfg->allowed_roles; / [admin, editor, standard]
|
||||||
```
|
```
|
||||||
Capas de entorno: un archivo llamado `users.production.ini` se prefiere sobre `users.ini` cuando `APPLICATION_ENV=production`.
|
Capas de entorno: un archivo llamado `users.production.ini` se prefiere sobre `users.ini` cuando `APPLICATION_ENV=production`.
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
|
|||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
||||||
|
|
||||||
$analytics->trackPageView(); // internally calls notify()
|
$analytics->trackPageView(); / internally calls notify()
|
||||||
```
|
```
|
||||||
Cada observador recibe la instancia de análisis en su método `update($subject)` y extrae los datos del evento que le interesan.
|
Cada observador recibe la instancia de análisis en su método `update($subject)` y extrae los datos del evento que le interesan.
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ Los nombres son **nombres de carpetas en minúsculas**, exactamente como aparece
|
|||||||
Las clases de plugin residen bajo el espacio de nombres **plural** `Plugins`:
|
Las clases de plugin residen bajo el espacio de nombres **plural** `Plugins`:
|
||||||
```php
|
```php
|
||||||
// application/module/billing/plugins/invoice.php
|
// application/module/billing/plugins/invoice.php
|
||||||
namespace Nibiru\Module\Billing\Plugins; // ← plural
|
namespace Nibiru\Module\Billing\Plugins; / ← plural
|
||||||
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
||||||
```
|
```
|
||||||
Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La plantilla de la CLI (`./nibiru -m billing`) genera el espacio de nombres correcto para ti.
|
Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La plantilla de la CLI (`./nibiru -m billing`) genera el espacio de nombres correcto para ti.
|
||||||
@@ -210,7 +210,7 @@ Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La
|
|||||||
No **registras** los archivos INI de tu módulo — el [Registro](/es/core/registry/) los descubre automáticamente recorriendo `application/module/<name>/settings/*.ini` después de que `[AUTOLOADER]` carga la clase del módulo. Cada sección `[<MODULE>]` (en mayúsculas) de un INI se vuelve disponible como:
|
No **registras** los archivos INI de tu módulo — el [Registro](/es/core/registry/) los descubre automáticamente recorriendo `application/module/<name>/settings/*.ini` después de que `[AUTOLOADER]` carga la clase del módulo. Cada sección `[<MODULE>]` (en mayúsculas) de un INI se vuelve disponible como:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
||||||
$cfg->invoice_prefix; // [BILLING] invoice.prefix → property
|
$cfg->invoice_prefix; / [BILLING] invoice.prefix → property
|
||||||
```
|
```
|
||||||
El Registro prefiere `<módulo>.<entorno>.ini` (por ejemplo, `facturación.producción.ini`) cuando `APPLICATION_ENV` coincide.
|
El Registro prefiere `<módulo>.<entorno>.ini` (por ejemplo, `facturación.producción.ini`) cuando `APPLICATION_ENV` coincide.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class productsController extends Controller
|
|||||||
{
|
{
|
||||||
public function pageAction() {
|
public function pageAction() {
|
||||||
$products = new products();
|
$products = new products();
|
||||||
Pageination::setEntriesPerPage(25); // optional; default from INI
|
Pageination::setEntriesPerPage(25); / optional; default from INI
|
||||||
Pageination::setTable($products);
|
Pageination::setTable($products);
|
||||||
$rows = Pageination::loadTableAsArray();
|
$rows = Pageination::loadTableAsArray();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Para cada módulo bajo `application/module/<nombre>/settings/`:
|
|||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime;
|
$users->session_lifetime;
|
||||||
$users->password_min_length;
|
$users->password_min_length;
|
||||||
$users->allowed_roles; // array
|
$users->allowed_roles; / array
|
||||||
```
|
```
|
||||||
Dentro del módulo mismo, la convención es envolver esto en un setter:
|
Dentro del módulo mismo, la convención es envolver esto en un setter:
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Cualquier cosa después del segmento de acción se convierte en una clave `$_REQ
|
|||||||
// /users/edit/42 → $_REQUEST['id'] = '42'
|
// /users/edit/42 → $_REQUEST['id'] = '42'
|
||||||
public function editAction() {
|
public function editAction() {
|
||||||
$id = (int) ($_REQUEST['id'] ?? 0);
|
$id = (int) ($_REQUEST['id'] ?? 0);
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Para parámetros no numéricos o nombrados, prefiera cadenas de consulta:
|
Para parámetros no numéricos o nombrados, prefiera cadenas de consulta:
|
||||||
@@ -62,9 +62,9 @@ Cuando una URL coincide con el patrón, los grupos capturados se asignan a las c
|
|||||||
|
|
||||||
## Ayudantes de enrutamiento
|
## Ayudantes de enrutamiento
|
||||||
```php
|
```php
|
||||||
Router::getInstance()->currentPage(); // 'products'
|
Router::getInstance()->currentPage(); / 'products'
|
||||||
Router::getInstance()->tplName(); // 'products' (controller stem for templates)
|
Router::getInstance()->tplName(); / 'products' (controller stem for templates)
|
||||||
Router::getInstance()->getController(); // alias for currentPage()
|
Router::getInstance()->getController(); / alias for currentPage()
|
||||||
```
|
```
|
||||||
Estos son útiles dentro de los controladores y plantillas:
|
Estos son útiles dentro de los controladores y plantillas:
|
||||||
```smarty
|
```smarty
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ description: "Botones, tarjetas, llamadas a la acción, héroe — listos para c
|
|||||||
|
|
||||||
Un rectángulo negro sobre crema. Editorial. Sin gradiente, sin brillo.
|
Un rectángulo negro sobre crema. Editorial. Sin gradiente, sin brillo.
|
||||||
```html
|
```html
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/es/start/installation/">
|
||||||
<span>Read the docs</span>
|
<span>Read the docs</span>
|
||||||
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -162,7 +162,7 @@ Cuadrícula asimétrica de dos columnas. Un número editorial grande detrás del
|
|||||||
Nibiru is a modular PHP framework for builders who ship.
|
Nibiru is a modular PHP framework for builders who ship.
|
||||||
</p>
|
</p>
|
||||||
<div class="atelier-hero__cta">
|
<div class="atelier-hero__cta">
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/es/start/installation/">
|
||||||
Read the docs <span aria-hidden="true">→</span>
|
Read the docs <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Eso es suficiente para estar en marca.
|
|||||||
|
|
||||||
## ¿Qué se documenta aquí?
|
## ¿Qué se documenta aquí?
|
||||||
|
|
||||||
- [Paleta](/es/diseno/paleta/) — cada color con su rol.
|
- [Paleta](/es/design/palette/) — cada color con su rol.
|
||||||
- [Tipografía](/es/diseno/tipografia/) — los ejes variables de Bricolage utilizados en serio.
|
- [Tipografía](/es/design/typography/) — los ejes variables de Bricolage utilizados en serio.
|
||||||
- [Componentes](/es/diseno/componentes/) — botones, tarjetas, llamadas a la atención, el lanzador Oracle.
|
- [Componentes](/es/design/components/) — botones, tarjetas, llamadas a la atención, el lanzador Oracle.
|
||||||
- [Movimiento](/es/diseno/movimiento/) — respirar, desvanecerse, sin flash.
|
- [Movimiento](/es/design/motion/) — respirar, desvanecerse, sin flash.
|
||||||
|
|||||||
@@ -19,73 +19,25 @@ hero:
|
|||||||
variant: minimal
|
variant: minimal
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
import CometTrail from '../../../components/CometTrail.astro';
|
||||||
|
import MmvcStage from '../../../components/MmvcStage.astro';
|
||||||
|
import MissionControl from '../../../components/MissionControl.astro';
|
||||||
|
import LaunchSequence from '../../../components/LaunchSequence.astro';
|
||||||
|
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
|
||||||
|
import EditorialContent from '../../../components/EditorialContent.astro';
|
||||||
|
import LandingFooter from '../../../components/LandingFooter.astro';
|
||||||
|
import ToTop from '../../../components/ToTop.astro';
|
||||||
|
import LandingScripts from '../../../components/LandingScripts.astro';
|
||||||
|
|
||||||
## Una constelación de capacidades
|
<CometTrail />
|
||||||
|
<MmvcStage />
|
||||||
|
<MissionControl />
|
||||||
|
<LaunchSequence />
|
||||||
|
<SpacecraftGrid />
|
||||||
|
<EditorialContent />
|
||||||
|
<LandingFooter />
|
||||||
|
<ToTop />
|
||||||
|
|
||||||
<CardGrid stagger>
|
{/* Loads three.js + the original mockup scene code. MUST be the last node so
|
||||||
<Card title="Arquitectura MMVC" icon="puzzle">
|
every #id the scene targets exists in the DOM when the script runs. */}
|
||||||
Los módulos envuelven MVC con traits, plugins, interfaces y configuraciones. La segunda **M** está acoplada de forma flexible, con el patrón observador `SplSubject` integrado.
|
<LandingScripts />
|
||||||
</Card>
|
|
||||||
<Card title="Núcleo multi-base de datos" icon="seti:db">
|
|
||||||
Cinco controladores en órbita: `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) y `postgresql`. Cambia con solo modificar una clave INI.
|
|
||||||
</Card>
|
|
||||||
<Card title="Vistas Smarty" icon="document">
|
|
||||||
Vistas dirigidas por plantillas, un ayudante global `View::assign()` y una caché caliente en `templates_c`. Más parciales compartidos, includes de navegación y plantillas de paginación.
|
|
||||||
</Card>
|
|
||||||
<Card title="Constructor de formularios fluido" icon="pencil">
|
|
||||||
Más de 28 tipos de campo — text, password, switch, color, range, file upload — compuestos mediante llamadas estáticas a `Form::add…` y renderizados como un único string HTML.
|
|
||||||
</Card>
|
|
||||||
<Card title="CLI real" icon="seti:powershell">
|
|
||||||
`./nibiru` genera módulos, controladores, plugins, migraciones y páginas CMS. Ejecuta migraciones contra `local`, `staging` o `production`.
|
|
||||||
</Card>
|
|
||||||
<Card title="IA nativa, pronto" icon="rocket">
|
|
||||||
Nibiru se está convirtiendo en el primer framework PHP con un <a href="/es/ai/oracle/">Oráculo basado en RAG</a> entrenado con su propio conocimiento — y un corpus publicado para futuros fine-tunes.
|
|
||||||
</Card>
|
|
||||||
</CardGrid>
|
|
||||||
|
|
||||||
## Inicio rápido
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clonar
|
|
||||||
git clone https://github.com/alllinux/Nibiru mi-app && cd mi-app
|
|
||||||
|
|
||||||
# Instalar dependencias PHP (Smarty, PHPMailer, Guzzle, …)
|
|
||||||
composer install
|
|
||||||
|
|
||||||
# Permisos + arranque de carpetas
|
|
||||||
./nibiru -s
|
|
||||||
|
|
||||||
# Ejecutar la primera migración
|
|
||||||
./nibiru -mi local
|
|
||||||
|
|
||||||
# Generar un controlador
|
|
||||||
./nibiru -c products
|
|
||||||
```
|
|
||||||
|
|
||||||
```php
|
|
||||||
// application/controller/productsController.php
|
|
||||||
namespace Nibiru;
|
|
||||||
use Nibiru\Adapter\Controller;
|
|
||||||
|
|
||||||
class productsController extends Controller {
|
|
||||||
public function pageAction() {
|
|
||||||
View::assign([
|
|
||||||
'title' => 'Productos — Nibiru',
|
|
||||||
'products' => [['id' => 1, 'name' => 'Recubrimiento de Oro Marduk']],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
public function navigationAction() {
|
|
||||||
JsonNavigation::getInstance()->loadJsonNavigationArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## A dónde ir después
|
|
||||||
|
|
||||||
<CardGrid>
|
|
||||||
<LinkCard title="¿Qué es Nibiru?" href="/es/start/what-is-nibiru/" description="El recorrido de 90 segundos: MMVC, el dispatcher, el ciclo de vida de las peticiones." />
|
|
||||||
<LinkCard title="Arquitectura" href="/core/architecture/" description="Cómo módulos, controladores, vistas y el registro orbitan entre sí." />
|
|
||||||
<LinkCard title="Casos de uso" href="/showcase/projects/" description="Apps reales en producción sobre Nibiru — facturación, e-commerce, PIM industrial." />
|
|
||||||
<LinkCard title="Pregunta al Oráculo" href="/es/ai/oracle/" description="Abre el chat flotante. El Oráculo está fundamentado en esta misma documentación." />
|
|
||||||
</CardGrid>
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Varios rastreadores sin acoplamiento con el controlador.
|
|||||||
$analytics = new Analytics();
|
$analytics = new Analytics();
|
||||||
$analytics->attach(new Plugin\Matomo());
|
$analytics->attach(new Plugin\Matomo());
|
||||||
$analytics->attach(new Plugin\Plausible());
|
$analytics->attach(new Plugin\Plausible());
|
||||||
$analytics->trackPageView(); // calls notify() internally
|
$analytics->trackPageView(); / calls notify() internally
|
||||||
```
|
```
|
||||||
Cada observador extrae solo los campos que le interesan con su `update($subject)`. Agregar un rastreador es un cambio de una línea.
|
Cada observador extrae solo los campos que le interesan con su `update($subject)`. Agregar un rastreador es un cambio de una línea.
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public function detailAction()
|
|||||||
try {
|
try {
|
||||||
$machine = Machine::init()->getMachine((int) $machineId);
|
$machine = Machine::init()->getMachine((int) $machineId);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$machine = null; // DB blip → page still renders with fallback.
|
$machine = null; / DB blip → page still renders with fallback.
|
||||||
}
|
}
|
||||||
|
|
||||||
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
||||||
@@ -110,7 +110,7 @@ Inserta una nueva plantilla Smarty en el sistema, el editor sabe de inmediato cu
|
|||||||
|
|
||||||
## ¿Qué realmente es especial, resumido?
|
## ¿Qué realmente es especial, resumido?
|
||||||
|
|
||||||
Los cinco diferenciadores a continuación se extraen de los códigos fuente anteriores. Cada uno enlaza a su evidencia en la página [¿Por qué Nibiru](/es/por-que-nibiru/).
|
Los cinco diferenciadores a continuación se extraen de los códigos fuente anteriores. Cada uno enlaza a su evidencia en la página [¿Por qué Nibiru](/es/why-nibiru/).
|
||||||
|
|
||||||
| | ¿Qué hace Nibiru? | ¿Qué hace Laravel/Symfony? |
|
| | ¿Qué hace Nibiru? | ¿Qué hace Laravel/Symfony? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -120,7 +120,7 @@ Los cinco diferenciadores a continuación se extraen de los códigos fuente ante
|
|||||||
| Auth | 3 líneas en el constructor del controlador. | Pila de middleware + clases de política + portales. |
|
| Auth | 3 líneas en el constructor del controlador. | Pila de middleware + clases de política + portales. |
|
||||||
| Eventos | `SplSubject` + `SplObserver` de la librería estándar de PHP. | Despachador de eventos personalizado + registro de oyentes + cola. |
|
| Eventos | `SplSubject` + `SplObserver` de la librería estándar de PHP. | Despachador de eventos personalizado + registro de oyentes + cola. |
|
||||||
|
|
||||||
Lee la [desglose completa con referencias de código →](/es/porque-nibiru/)
|
Lee la [desglose completa con referencias de código →](/es/why-nibiru/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
|
|||||||
use Traits\PageBuilderForm;
|
use Traits\PageBuilderForm;
|
||||||
use Traits\CmsPageStructureModifier;
|
use Traits\CmsPageStructureModifier;
|
||||||
use Traits\FormElements;
|
use Traits\FormElements;
|
||||||
// …8 more traits
|
/ …8 more traits
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Efecto neto**: cero inyección de constructores, cero registro de proveedores de servicios, cero llamadas a `bind()` / `singleton()` en un archivo de configuración. Un nuevo desarrollador puede buscar el nombre del trait y ver todos los llamadores.
|
**Efecto neto**: cero inyección de constructores, cero registro de proveedores de servicios, cero llamadas a `bind()` / `singleton()` en un archivo de configuración. Un nuevo desarrollador puede buscar el nombre del trait y ver todos los llamadores.
|
||||||
@@ -108,7 +108,7 @@ El controlador de la API toma un enfoque inverso: una lista blanca de puntos fin
|
|||||||
```php
|
```php
|
||||||
// public endpoints can be listed up-front, auth wraps the rest.
|
// public endpoints can be listed up-front, auth wraps the rest.
|
||||||
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
||||||
// public, skip auth
|
/ public, skip auth
|
||||||
} else {
|
} else {
|
||||||
$this->user = new User();
|
$this->user = new User();
|
||||||
$this->acl = new Acl();
|
$this->acl = new Acl();
|
||||||
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function indexMachines(): void {
|
public function indexMachines(): void {
|
||||||
// …do the work…
|
/ …do the work…
|
||||||
$this->notify(); // analytics, cache invalidator, audit log all see it.
|
$this->notify(); / analytics, cache invalidator, audit log all see it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -154,4 +154,4 @@ Un desarrollador solitario o un pequeño equipo puede construir y *operar* una a
|
|||||||
|
|
||||||
Si prefieres ver tu código, tómalo.
|
Si prefieres ver tu código, tómalo.
|
||||||
|
|
||||||
→ [Lee la presentación →](/es/presentacion/proyectos/)
|
→ [Lee la presentación →](/es/showcase/projects/)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Blocs bruts à utiliser comme données de récupération RAG :
|
|||||||
"content": "Modules implementing `SplSubject` can broadcast events…"
|
"content": "Modules implementing `SplSubject` can broadcast events…"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Ceci est exactement le fichier que l'[Oracle](/ai/oracle/) utilise en interne.
|
Ceci est exactement le fichier que l'[Oracle](/fr/ai/oracle/) utilise en interne.
|
||||||
|
|
||||||
## Comment les morceaux sont dérivés
|
## Comment les morceaux sont dérivés
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ Le plugin de chat est la pièce la plus simple du module IA. Il encapsule l'API
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->system('Be terse.'); // optional system prompt
|
$chat->system('Be terse.'); / optional system prompt
|
||||||
$chat->model('qwen2.5-coder:14b'); // override the configured model
|
$chat->model('qwen2.5-coder:14b'); / override the configured model
|
||||||
$chat->temperature(0.2); // override config
|
$chat->temperature(0.2); / override config
|
||||||
$chat->maxTokens(512); // override config
|
$chat->maxTokens(512); / override config
|
||||||
|
|
||||||
$chat->user('Hello'); // append a user message
|
$chat->user('Hello'); / append a user message
|
||||||
$chat->assistant('Hi.'); // append an assistant message (rare)
|
$chat->assistant('Hi.'); / append an assistant message (rare)
|
||||||
|
|
||||||
$reply = $chat->complete(); // run the call, return text
|
$reply = $chat->complete(); / run the call, return text
|
||||||
$reply = $chat->ask('How are you?'); // = ->user(...)->complete()
|
$reply = $chat->ask('How are you?'); / = ->user(...)->complete()
|
||||||
|
|
||||||
$chat->reset(); // clear messages, keep model + system
|
$chat->reset(); / clear messages, keep model + system
|
||||||
$chat->history(); // [{role, content}, …]
|
$chat->history(); / [{role, content}, …]
|
||||||
```
|
```
|
||||||
## Un coup unique
|
## Un coup unique
|
||||||
```php
|
```php
|
||||||
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
|
|||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->user('Name three Nibiru singletons.');
|
$chat->user('Name three Nibiru singletons.');
|
||||||
$singletons = $chat->complete(); // appended to history
|
$singletons = $chat->complete(); / appended to history
|
||||||
|
|
||||||
$chat->user('What does the second one do?');
|
$chat->user('What does the second one do?');
|
||||||
$detail = $chat->complete(); // model has full context
|
$detail = $chat->complete(); / model has full context
|
||||||
```
|
```
|
||||||
## Remplacement du modèle et du style par appel
|
## Remplacement du modèle et du style par appel
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ Le plugin Embed est un simple wrapper autour de `/api/embeddings` d'Ollama, plus
|
|||||||
```php
|
```php
|
||||||
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
||||||
|
|
||||||
$vec = $embed->one('controller'); // float[]
|
$vec = $embed->one('controller'); / float[]
|
||||||
$vectors = $embed->batch(['a', 'b', 'c']); // float[][]
|
$vectors = $embed->batch(['a', 'b', 'c']); / float[][]
|
||||||
|
|
||||||
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1
|
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[]
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
|
||||||
```
|
```
|
||||||
## Modèle : dédoublonner des chaînes similaires
|
## Modèle : dédoublonner des chaînes similaires
|
||||||
```php
|
```php
|
||||||
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
|
|||||||
return $best[0];
|
return $best[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication'
|
echo bestTag('User::isAuthorized', $tagVecs, $embed); / → 'authentication'
|
||||||
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably)
|
echo bestTag('Pageination::setTable', $tagVecs, $embed); / → 'database' (probably)
|
||||||
```
|
```
|
||||||
## Stockage
|
## Stockage
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ Les embeddings sont des tableaux de flottants — généralement 768 flottants p
|
|||||||
|
|
||||||
Utilisez `Embed::pack()` pour les encoder en base64 comme des flottants sur 4 octets :
|
Utilisez `Embed::pack()` pour les encoder en base64 comme des flottants sur 4 octets :
|
||||||
```php
|
```php
|
||||||
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string
|
$compact = Embed::pack($vec); / ~4 KB → ~5.3 KB base64 string
|
||||||
$vec = Embed::unpack($compact);
|
$vec = Embed::unpack($compact);
|
||||||
```
|
```
|
||||||
Le plugin RAG utilise ce format internalement pour ses fichiers JSON.
|
Le plugin RAG utilise ce format internalement pour ses fichiers JSON.
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
|
|||||||
// Multi-turn
|
// Multi-turn
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
$chat->user('How do I scaffold a module?');
|
$chat->user('How do I scaffold a module?');
|
||||||
$chat->user('And add Graylog hooks?'); // referrs to previous turn
|
$chat->user('And add Graylog hooks?'); / referrs to previous turn
|
||||||
echo $chat->complete();
|
echo $chat->complete();
|
||||||
|
|
||||||
// Override per call
|
// Override per call
|
||||||
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
|
|||||||
```
|
```
|
||||||
Stockage compact :
|
Stockage compact :
|
||||||
```php
|
```php
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
||||||
```
|
```
|
||||||
### 3. RAG — ingérer, récupérer, ancrer
|
### 3. RAG — ingérer, récupérer, ancrer
|
||||||
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
|||||||
$rag = $ai->rag('product-help');
|
$rag = $ai->rag('product-help');
|
||||||
|
|
||||||
// One-time ingestion
|
// One-time ingestion
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
$rag->ingestFile('/var/data/manual.pdf.txt');
|
$rag->ingestFile('/var/data/manual.pdf.txt');
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
|
|
||||||
$agent = $ai->agent()->withTools([
|
$agent = $ai->agent()->withTools([
|
||||||
new Tools\PdoQuery(), // read-only SQL
|
new Tools\PdoQuery(), / read-only SQL
|
||||||
new Tools\HttpGet(), // fetch URLs
|
new Tools\HttpGet(), / fetch URLs
|
||||||
new Tools\FileRead(), // read project files
|
new Tools\FileRead(), / read project files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $agent->run('How many active users registered last week?');
|
echo $agent->run('How many active users registered last week?');
|
||||||
@@ -154,7 +154,7 @@ La philosophie de conception :
|
|||||||
|
|
||||||
## Suivant
|
## Suivant
|
||||||
|
|
||||||
- [Référence du module de chat](/fr/ia/module/chat/)
|
- [Référence du module de chat](/fr/ai/module/chat/)
|
||||||
- [Référence du module RAG](/fr/ia/module/rag/)
|
- [Référence du module RAG](/fr/ai/module/rag/)
|
||||||
- [Référence du module agent](/fr/ia/module/agent/)
|
- [Référence du module agent](/fr/ai/module/agent/)
|
||||||
- [Formation nibiru-coder](/fr/ia/module/training/)
|
- [Formation nibiru-coder](/fr/ai/module/training/)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ Le plugin RAG est la fonctionnalité clé du module IA pour les constructeurs de
|
|||||||
use Nibiru\Module\Ai\Ai;
|
use Nibiru\Module\Ai\Ai;
|
||||||
|
|
||||||
$ai = new Ai();
|
$ai = new Ai();
|
||||||
$rag = $ai->rag('product-help'); // a named collection
|
$rag = $ai->rag('product-help'); / a named collection
|
||||||
|
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
|
|
||||||
echo $rag->ask('How do I cancel my subscription?');
|
echo $rag->ask('How do I cancel my subscription?');
|
||||||
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
|
|||||||
```
|
```
|
||||||
## Référence de l'API
|
## Référence de l'API
|
||||||
```php
|
```php
|
||||||
$rag = $ai->rag('name'); // get/create a named collection
|
$rag = $ai->rag('name'); / get/create a named collection
|
||||||
|
|
||||||
// --- Ingestion ---
|
// --- Ingestion ---
|
||||||
$rag->ingestText($text, $metadata = []); // single chunk
|
$rag->ingestText($text, $metadata = []); / single chunk
|
||||||
$count = $rag->ingestFile('path'); // returns chunks added
|
$count = $rag->ingestFile('path'); / returns chunks added
|
||||||
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive
|
$count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
|
||||||
|
|
||||||
// --- Querying ---
|
// --- Querying ---
|
||||||
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …]
|
$hits = $rag->search('query', $k = null); / [{score, text, metadata}, …]
|
||||||
$answer = $rag->ask('question', $k = null); // top-K → chat call
|
$answer = $rag->ask('question', $k = null); / top-K → chat call
|
||||||
|
|
||||||
// --- Maintenance ---
|
// --- Maintenance ---
|
||||||
$rag->reset(); // forget everything (deletes file)
|
$rag->reset(); / forget everything (deletes file)
|
||||||
$n = $rag->size(); // number of chunks
|
$n = $rag->size(); / number of chunks
|
||||||
```
|
```
|
||||||
## Réglages
|
## Réglages
|
||||||
|
|
||||||
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
|
|||||||
|
|
||||||
## ¿Qué sigue?
|
## ¿Qué sigue?
|
||||||
|
|
||||||
- [Plugin agent →](/fr/ia/module/agent/) pour les outils, pas la récupération.
|
- [Plugin agent →](/fr/ai/module/agent/) pour les outils, pas la récupération.
|
||||||
- [Formation nibiru-coder →](/fr/ia/module/formation/) pour faire répondre le chat à moitié dans la voix du framework.
|
- [Formation nibiru-coder →](/fr/ai/module/training/) pour faire répondre le chat à moitié dans la voix du framework.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public function pageAction() {
|
|||||||
View::forwardTo('/login');
|
View::forwardTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Pour les vérifications basées sur le rôle, les applications de démonstration utilisent le plugin `Acl` du même module :
|
Pour les vérifications basées sur le rôle, les applications de démonstration utilisent le plugin `Acl` du même module :
|
||||||
@@ -178,7 +178,7 @@ public function submitAction() {
|
|||||||
http_response_code(419);
|
http_response_code(419);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...handle submission...
|
/ ...handle submission...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Intégrez `<input type="hidden" name="csrf" value="{$csrf}">` dans votre formulaire.
|
Intégrez `<input type="hidden" name="csrf" value="{$csrf}">` dans votre formulaire.
|
||||||
|
|||||||
@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
|
|||||||
## Lecture des configurations
|
## Lecture des configurations
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
||||||
$cfg['DATABASE']['driver']; // 'pdo'
|
$cfg['DATABASE']['driver']; / 'pdo'
|
||||||
$cfg['SETTINGS']['page.url']; // 'https://my-app.local'
|
$cfg['SETTINGS']['page.url']; / 'https://my-app.local'
|
||||||
```
|
```
|
||||||
Pour les configurations profondément imbriquées, utilisez les constantes typées de l'interface View :
|
Pour les configurations profondément imbriquées, utilisez les constantes typées de l'interface View :
|
||||||
```php
|
```php
|
||||||
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css']
|
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
|
||||||
```
|
```
|
||||||
## Configurations des modules
|
## Configurations des modules
|
||||||
|
|
||||||
Chaque module situé sous `application/module/<nom>/settings/` peut posséder ses propres fichiers INI. Le Registre les détecte automatiquement et les expose via :
|
Chaque module situé sous `application/module/<nom>/settings/` peut posséder ses propres fichiers INI. Le Registre les détecte automatiquement et les expose via :
|
||||||
```php
|
```php
|
||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime; // from [USERS] section in users.ini
|
$users->session_lifetime; / from [USERS] section in users.ini
|
||||||
```
|
```
|
||||||
Le Registre préfère `<module>.<env>.ini` par rapport à `<module>.ini`, donc vous obtenez des remplacements par environnement gratuitement.
|
Le Registre préfère `<module>.<env>.ini` par rapport à `<module>.ini`, donc vous obtenez des remplacements par environnement gratuitement.
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
|
|||||||
```
|
```
|
||||||
Aides pratiques du contrôleur de base :
|
Aides pratiques du contrôleur de base :
|
||||||
```php
|
```php
|
||||||
$this->getRequest('id', false); // $_REQUEST['id'] ?? false
|
$this->getRequest('id', false); / $_REQUEST['id'] ?? false
|
||||||
$this->getPost('email', ''); // $_POST['email'] ?? ''
|
$this->getPost('email', ''); / $_POST['email'] ?? ''
|
||||||
$this->getGet('page', 1); // $_GET['page'] ?? 1
|
$this->getGet('page', 1); / $_GET['page'] ?? 1
|
||||||
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI']
|
$this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
|
||||||
$this->getFiles('upload'); // $_FILES['upload']
|
$this->getFiles('upload'); / $_FILES['upload']
|
||||||
$this->getSession('auth'); // $_SESSION['auth']
|
$this->getSession('auth'); / $_SESSION['auth']
|
||||||
```
|
```
|
||||||
Ces éléments existent en raison du fait que `Controller` est compatible avec `final` : vous pouvez les simuler dans les tests en remplaçant par une classe enfant.
|
Ces éléments existent en raison du fait que `Controller` est compatible avec `final` : vous pouvez les simuler dans les tests en remplaçant par une classe enfant.
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ Ces éléments existent en raison du fait que `Controller` est compatible avec `
|
|||||||
|
|
||||||
Pour rediriger à l'intérieur d'une action :
|
Pour rediriger à l'intérieur d'une action :
|
||||||
```php
|
```php
|
||||||
View::forwardTo('/login'); // 302 to the URL, exits
|
View::forwardTo('/login'); / 302 to the URL, exits
|
||||||
View::forwardToJsonHeader(); // sets Content-Type: application/json
|
View::forwardToJsonHeader(); / sets Content-Type: application/json
|
||||||
```
|
```
|
||||||
`forwardToJsonHeader()` est le modèle canonique pour les points de terminaison JSON — définissez l'en-tête, attribuez `data`, puis retournez. La couche d'affichage s'occupe du reste.
|
`forwardToJsonHeader()` est le modèle canonique pour les points de terminaison JSON — définissez l'en-tête, attribuez `data`, puis retournez. La couche d'affichage s'occupe du reste.
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ public function run() {
|
|||||||
|
|
||||||
if (Config::getInstance()->getConfig()
|
if (Config::getInstance()->getConfig()
|
||||||
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
||||||
new Model(false); // 1. (re)generate models from schema
|
new Model(false); / 1. (re)generate models from schema
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::getInstance()->route(); // 2. parse the URL
|
Router::getInstance()->route(); / 2. parse the URL
|
||||||
Auto::loader()->loadModelFiles(); // 3. load model files
|
Auto::loader()->loadModelFiles(); / 3. load model files
|
||||||
Auto::loader()->loadModules(); // 4. load module classes
|
Auto::loader()->loadModules(); / 4. load module classes
|
||||||
|
|
||||||
$tpl = Router::getInstance()->tplName();
|
$tpl = Router::getInstance()->tplName();
|
||||||
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
||||||
|
|
||||||
if (is_file($controllerFile)) { // 5. controller file exists
|
if (is_file($controllerFile)) { / 5. controller file exists
|
||||||
require_once $controllerFile;
|
require_once $controllerFile;
|
||||||
$class = "Nibiru\\{$tpl}Controller";
|
$class = "Nibiru\\{$tpl}Controller";
|
||||||
$controller = new $class();
|
$controller = new $class();
|
||||||
@@ -49,7 +49,7 @@ public function run() {
|
|||||||
$action = $_REQUEST['_action'] . 'Action';
|
$action = $_REQUEST['_action'] . 'Action';
|
||||||
$controller->navigationAction();
|
$controller->navigationAction();
|
||||||
if (method_exists($controller, $action)) {
|
if (method_exists($controller, $action)) {
|
||||||
$controller->$action(); // 6. optional named action
|
$controller->$action(); / 6. optional named action
|
||||||
}
|
}
|
||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -57,9 +57,9 @@ public function run() {
|
|||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::getInstance()->display(); // 7. render Smarty
|
Display::getInstance()->display(); / 7. render Smarty
|
||||||
} else {
|
} else {
|
||||||
// 8. soft 404 — render the configured error controller
|
/ 8. soft 404 — render the configured error controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
|
|||||||
```
|
```
|
||||||
### Cycle de vie
|
### Cycle de vie
|
||||||
```
|
```
|
||||||
create // reset the static buffer; call before building a new form
|
create / reset the static buffer; call before building a new form
|
||||||
addForm // wrap the buffer in <form>...</form> and return as a string
|
addForm / wrap the buffer in <form>...</form> and return as a string
|
||||||
```
|
```
|
||||||
:::caution[L'incréance de nommage est intentionnelle, mais pas complètement]
|
:::caution[L'incréance de nommage est intentionnelle, mais pas complètement]
|
||||||
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — le préfixe correspond à ce que l'élément soit un `<input type="…">` (préfixe Input) ou une autre balise (préfixe Type). C'est un peu inconfortable, mais stable ; les modèles de conception utilisés par la CLI `./nibiru -c` suivent le même modèle, donc l'habitude s'impose.
|
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — le préfixe correspond à ce que l'élément soit un `<input type="…">` (préfixe Input) ou une autre balise (préfixe Type). C'est un peu inconfortable, mais stable ; les modèles de conception utilisés par la CLI `./nibiru -c` suivent le même modèle, donc l'habitude s'impose.
|
||||||
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
|
|||||||
```php
|
```php
|
||||||
use Nibiru\Factory\Form;
|
use Nibiru\Factory\Form;
|
||||||
|
|
||||||
Form::create(); // reset the static buffer
|
Form::create(); / reset the static buffer
|
||||||
|
|
||||||
Form::addOpenDiv(['class' => 'form-group']);
|
Form::addOpenDiv(['class' => 'form-group']);
|
||||||
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
||||||
@@ -187,7 +187,7 @@ class formsController extends Controller {
|
|||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
'class' => 'contacts-input form-control',
|
'class' => 'contacts-input form-control',
|
||||||
]);
|
]);
|
||||||
// ...more fields...
|
/ ...more fields...
|
||||||
$this->form = Form::addForm([
|
$this->form = Form::addForm([
|
||||||
'name' => 'newregister',
|
'name' => 'newregister',
|
||||||
'method' => 'post',
|
'method' => 'post',
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
|
|||||||
Lisez-le à partir d'ailleurs :
|
Lisez-le à partir d'ailleurs :
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$cfg->session_lifetime; // 7200
|
$cfg->session_lifetime; / 7200
|
||||||
$cfg->password_min_length; // 12
|
$cfg->password_min_length; / 12
|
||||||
$cfg->allowed_roles; // [admin, editor, standard]
|
$cfg->allowed_roles; / [admin, editor, standard]
|
||||||
```
|
```
|
||||||
Superpositions d'environnement : un fichier nommé `users.production.ini` est préféré par rapport à `users.ini` lorsque `APPLICATION_ENV=production`.
|
Superpositions d'environnement : un fichier nommé `users.production.ini` est préféré par rapport à `users.ini` lorsque `APPLICATION_ENV=production`.
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
|
|||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
||||||
|
|
||||||
$analytics->trackPageView(); // internally calls notify()
|
$analytics->trackPageView(); / internally calls notify()
|
||||||
```
|
```
|
||||||
Chaque observateur reçoit l'instance d'analytics via sa méthode `update($subject)` et extrait les données d'événement dont il a besoin.
|
Chaque observateur reçoit l'instance d'analytics via sa méthode `update($subject)` et extrait les données d'événement dont il a besoin.
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ Les noms sont des **noms de dossiers en minuscules**, exactement tels qu'ils app
|
|||||||
Les classes de plugin se trouvent dans l'espace de noms **pluriel** `Plugins`:
|
Les classes de plugin se trouvent dans l'espace de noms **pluriel** `Plugins`:
|
||||||
```php
|
```php
|
||||||
// application/module/billing/plugins/invoice.php
|
// application/module/billing/plugins/invoice.php
|
||||||
namespace Nibiru\Module\Billing\Plugins; // ← plural
|
namespace Nibiru\Module\Billing\Plugins; / ← plural
|
||||||
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
||||||
```
|
```
|
||||||
Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'autoload. Le squelette CLI (`./nibiru -m billing`) génère l'espace de noms correct pour vous.
|
Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'autoload. Le squelette CLI (`./nibiru -m billing`) génère l'espace de noms correct pour vous.
|
||||||
@@ -210,7 +210,7 @@ Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'aut
|
|||||||
Vous **ne** vous inscrivez pas les fichiers INI de votre module — le [Registry](/en/core/registry/) les découvre automatiquement en parcourant `application/module/<name>/settings/*.ini` après que `[AUTOLOADER]` a chargé la classe du module. Chaque section `[<MODULE>]` (en majuscules) d'un INI devient disponible comme :
|
Vous **ne** vous inscrivez pas les fichiers INI de votre module — le [Registry](/en/core/registry/) les découvre automatiquement en parcourant `application/module/<name>/settings/*.ini` après que `[AUTOLOADER]` a chargé la classe du module. Chaque section `[<MODULE>]` (en majuscules) d'un INI devient disponible comme :
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
||||||
$cfg->invoice_prefix; // [BILLING] invoice.prefix → property
|
$cfg->invoice_prefix; / [BILLING] invoice.prefix → property
|
||||||
```
|
```
|
||||||
Le Registre préfère `<module>.<env>.ini` (par exemple `billing.production.ini`) lorsque `APPLICATION_ENV` correspond.
|
Le Registre préfère `<module>.<env>.ini` (par exemple `billing.production.ini`) lorsque `APPLICATION_ENV` correspond.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class productsController extends Controller
|
|||||||
{
|
{
|
||||||
public function pageAction() {
|
public function pageAction() {
|
||||||
$products = new products();
|
$products = new products();
|
||||||
Pageination::setEntriesPerPage(25); // optional; default from INI
|
Pageination::setEntriesPerPage(25); / optional; default from INI
|
||||||
Pageination::setTable($products);
|
Pageination::setTable($products);
|
||||||
$rows = Pageination::loadTableAsArray();
|
$rows = Pageination::loadTableAsArray();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Pour chaque module sous `application/module/<nom>/settings/` :
|
|||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime;
|
$users->session_lifetime;
|
||||||
$users->password_min_length;
|
$users->password_min_length;
|
||||||
$users->allowed_roles; // array
|
$users->allowed_roles; / array
|
||||||
```
|
```
|
||||||
À l'intérieur du module lui-même, la convention est d'envelopper cela dans un mutateur :
|
À l'intérieur du module lui-même, la convention est d'envelopper cela dans un mutateur :
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Tout ce qui se trouve après le segment d'action devient une clé `$_REQUEST` pa
|
|||||||
// /users/edit/42 → $_REQUEST['id'] = '42'
|
// /users/edit/42 → $_REQUEST['id'] = '42'
|
||||||
public function editAction() {
|
public function editAction() {
|
||||||
$id = (int) ($_REQUEST['id'] ?? 0);
|
$id = (int) ($_REQUEST['id'] ?? 0);
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Pour les paramètres non numériques ou nommés, privilégiez les chaînes de requête :
|
Pour les paramètres non numériques ou nommés, privilégiez les chaînes de requête :
|
||||||
@@ -62,9 +62,9 @@ Lorsqu'une URL correspond au modèle, les groupes capturés sont attribués aux
|
|||||||
|
|
||||||
## Aide aux routages
|
## Aide aux routages
|
||||||
```php
|
```php
|
||||||
Router::getInstance()->currentPage(); // 'products'
|
Router::getInstance()->currentPage(); / 'products'
|
||||||
Router::getInstance()->tplName(); // 'products' (controller stem for templates)
|
Router::getInstance()->tplName(); / 'products' (controller stem for templates)
|
||||||
Router::getInstance()->getController(); // alias for currentPage()
|
Router::getInstance()->getController(); / alias for currentPage()
|
||||||
```
|
```
|
||||||
Ces éléments sont utiles dans les contrôleurs et les modèles :
|
Ces éléments sont utiles dans les contrôleurs et les modèles :
|
||||||
```smarty
|
```smarty
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ description: "Boutons, cartes, appels à l’action, héros — prêts à être
|
|||||||
|
|
||||||
Un rectangle noir sur crème. Éditorial. Pas de dégradé, pas d'éclairage.
|
Un rectangle noir sur crème. Éditorial. Pas de dégradé, pas d'éclairage.
|
||||||
```html
|
```html
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/fr/start/installation/">
|
||||||
<span>Read the docs</span>
|
<span>Read the docs</span>
|
||||||
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -162,7 +162,7 @@ Grille asymétrique à deux colonnes. Un grand numéro éditorial derrière le t
|
|||||||
Nibiru is a modular PHP framework for builders who ship.
|
Nibiru is a modular PHP framework for builders who ship.
|
||||||
</p>
|
</p>
|
||||||
<div class="atelier-hero__cta">
|
<div class="atelier-hero__cta">
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/fr/start/installation/">
|
||||||
Read the docs <span aria-hidden="true">→</span>
|
Read the docs <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,73 +19,25 @@ hero:
|
|||||||
variant: minimal
|
variant: minimal
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
import CometTrail from '../../../components/CometTrail.astro';
|
||||||
|
import MmvcStage from '../../../components/MmvcStage.astro';
|
||||||
|
import MissionControl from '../../../components/MissionControl.astro';
|
||||||
|
import LaunchSequence from '../../../components/LaunchSequence.astro';
|
||||||
|
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
|
||||||
|
import EditorialContent from '../../../components/EditorialContent.astro';
|
||||||
|
import LandingFooter from '../../../components/LandingFooter.astro';
|
||||||
|
import ToTop from '../../../components/ToTop.astro';
|
||||||
|
import LandingScripts from '../../../components/LandingScripts.astro';
|
||||||
|
|
||||||
## Une constellation de capacités
|
<CometTrail />
|
||||||
|
<MmvcStage />
|
||||||
|
<MissionControl />
|
||||||
|
<LaunchSequence />
|
||||||
|
<SpacecraftGrid />
|
||||||
|
<EditorialContent />
|
||||||
|
<LandingFooter />
|
||||||
|
<ToTop />
|
||||||
|
|
||||||
<CardGrid stagger>
|
{/* Loads three.js + the original mockup scene code. MUST be the last node so
|
||||||
<Card title="Architecture MMVC" icon="puzzle">
|
every #id the scene targets exists in the DOM when the script runs. */}
|
||||||
Les modules enveloppent le MVC avec traits, plugins, interfaces et paramètres. Le second **M** est faiblement couplé par conception, avec le pattern observer `SplSubject` intégré.
|
<LandingScripts />
|
||||||
</Card>
|
|
||||||
<Card title="Cœur multi-bases" icon="seti:db">
|
|
||||||
Cinq pilotes en orbite : `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) et `postgresql`. Basculez en modifiant une seule clé INI.
|
|
||||||
</Card>
|
|
||||||
<Card title="Vues Smarty" icon="document">
|
|
||||||
Vues pilotées par templates, un assistant global `View::assign()`, et un cache chaud `templates_c`. Plus partiels partagés, includes de navigation et templates de pagination.
|
|
||||||
</Card>
|
|
||||||
<Card title="Constructeur de formulaires fluide" icon="pencil">
|
|
||||||
Plus de 28 types de champ — text, password, switch, color, range, file upload — composés via des appels statiques `Form::add…` et rendus en une seule chaîne HTML.
|
|
||||||
</Card>
|
|
||||||
<Card title="Véritable outil CLI" icon="seti:powershell">
|
|
||||||
`./nibiru` génère modules, contrôleurs, plugins, migrations et pages CMS. Il exécute les migrations contre `local`, `staging` ou `production`.
|
|
||||||
</Card>
|
|
||||||
<Card title="IA-natif, bientôt" icon="rocket">
|
|
||||||
Nibiru sera le premier framework PHP avec un <a href="/fr/ai/oracle/">Oracle basé sur RAG</a> entraîné sur sa propre connaissance — et un corpus publié pour de futurs fine-tunes.
|
|
||||||
</Card>
|
|
||||||
</CardGrid>
|
|
||||||
|
|
||||||
## Démarrage rapide
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Cloner
|
|
||||||
git clone https://github.com/alllinux/Nibiru mon-app && cd mon-app
|
|
||||||
|
|
||||||
# Installer les dépendances PHP (Smarty, PHPMailer, Guzzle, …)
|
|
||||||
composer install
|
|
||||||
|
|
||||||
# Permissions + amorçage des dossiers
|
|
||||||
./nibiru -s
|
|
||||||
|
|
||||||
# Première migration
|
|
||||||
./nibiru -mi local
|
|
||||||
|
|
||||||
# Générer un contrôleur
|
|
||||||
./nibiru -c products
|
|
||||||
```
|
|
||||||
|
|
||||||
```php
|
|
||||||
// application/controller/productsController.php
|
|
||||||
namespace Nibiru;
|
|
||||||
use Nibiru\Adapter\Controller;
|
|
||||||
|
|
||||||
class productsController extends Controller {
|
|
||||||
public function pageAction() {
|
|
||||||
View::assign([
|
|
||||||
'title' => 'Produits — Nibiru',
|
|
||||||
'products' => [['id' => 1, 'name' => 'Plaquage d\'or Marduk']],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
public function navigationAction() {
|
|
||||||
JsonNavigation::getInstance()->loadJsonNavigationArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Où aller ensuite
|
|
||||||
|
|
||||||
<CardGrid>
|
|
||||||
<LinkCard title="Qu'est-ce que Nibiru ?" href="/fr/start/what-is-nibiru/" description="La visite de 90 secondes : MMVC, le dispatcher, le cycle de vie des requêtes." />
|
|
||||||
<LinkCard title="Architecture" href="/core/architecture/" description="Comment modules, contrôleurs, vues et registre orbitent les uns autour des autres." />
|
|
||||||
<LinkCard title="Vitrine" href="/showcase/projects/" description="De vraies apps en production sur Nibiru — facturation, e-commerce, PIM industriel." />
|
|
||||||
<LinkCard title="Interroger l'Oracle" href="/fr/ai/oracle/" description="Ouvrez le chat flottant. L'Oracle est ancré dans cette documentation même." />
|
|
||||||
</CardGrid>
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Plusieurs suivi sans couplage avec le contrôleur.
|
|||||||
$analytics = new Analytics();
|
$analytics = new Analytics();
|
||||||
$analytics->attach(new Plugin\Matomo());
|
$analytics->attach(new Plugin\Matomo());
|
||||||
$analytics->attach(new Plugin\Plausible());
|
$analytics->attach(new Plugin\Plausible());
|
||||||
$analytics->trackPageView(); // calls notify() internally
|
$analytics->trackPageView(); / calls notify() internally
|
||||||
```
|
```
|
||||||
Chaque observateur appelle uniquement les champs dont il s'occupe avec sa méthode `update($subject)`. Ajouter un suivi est une modification d'une seule ligne.
|
Chaque observateur appelle uniquement les champs dont il s'occupe avec sa méthode `update($subject)`. Ajouter un suivi est une modification d'une seule ligne.
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public function detailAction()
|
|||||||
try {
|
try {
|
||||||
$machine = Machine::init()->getMachine((int) $machineId);
|
$machine = Machine::init()->getMachine((int) $machineId);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$machine = null; // DB blip → page still renders with fallback.
|
$machine = null; / DB blip → page still renders with fallback.
|
||||||
}
|
}
|
||||||
|
|
||||||
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
|
||||||
@@ -120,7 +120,7 @@ Les cinq différences ci-dessous sont tirées des bases de code ci-dessus. Chacu
|
|||||||
| Authentification | 3 lignes dans le constructeur du contrôleur. | Pile de middlewares + classes de politique + portails. |
|
| Authentification | 3 lignes dans le constructeur du contrôleur. | Pile de middlewares + classes de politique + portails. |
|
||||||
| Événements | `SplSubject` + `SplObserver` de la bibliothèque standard PHP. | Dispatcheur d'événements personnalisé + registre d'écouteurs + file d'attente. |
|
| Événements | `SplSubject` + `SplObserver` de la bibliothèque standard PHP. | Dispatcheur d'événements personnalisé + registre d'écouteurs + file d'attente. |
|
||||||
|
|
||||||
Lisez le [détail complet avec des références de code →](/fr/pourquoi-nibiru/)
|
Lisez le [détail complet avec des références de code →](/fr/why-nibiru/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
|
|||||||
use Traits\PageBuilderForm;
|
use Traits\PageBuilderForm;
|
||||||
use Traits\CmsPageStructureModifier;
|
use Traits\CmsPageStructureModifier;
|
||||||
use Traits\FormElements;
|
use Traits\FormElements;
|
||||||
// …8 more traits
|
/ …8 more traits
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Effet net**: aucune injection de constructeur, aucune inscription auprès du fournisseur de services, aucun appel à `bind()` / `singleton()` dans un fichier de configuration. Un nouveau développeur peut rechercher le nom du trait et voir tous les appels.
|
**Effet net**: aucune injection de constructeur, aucune inscription auprès du fournisseur de services, aucun appel à `bind()` / `singleton()` dans un fichier de configuration. Un nouveau développeur peut rechercher le nom du trait et voir tous les appels.
|
||||||
@@ -108,7 +108,7 @@ Le contrôleur API adopte une approche inverse : une liste blanche des points de
|
|||||||
```php
|
```php
|
||||||
// public endpoints can be listed up-front, auth wraps the rest.
|
// public endpoints can be listed up-front, auth wraps the rest.
|
||||||
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
|
||||||
// public, skip auth
|
/ public, skip auth
|
||||||
} else {
|
} else {
|
||||||
$this->user = new User();
|
$this->user = new User();
|
||||||
$this->acl = new Acl();
|
$this->acl = new Acl();
|
||||||
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function indexMachines(): void {
|
public function indexMachines(): void {
|
||||||
// …do the work…
|
/ …do the work…
|
||||||
$this->notify(); // analytics, cache invalidator, audit log all see it.
|
$this->notify(); / analytics, cache invalidator, audit log all see it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -154,4 +154,4 @@ Un développeur solo ou une petite équipe peut construire et *exploiter* une v
|
|||||||
|
|
||||||
Si vous préférez voir votre code, prenez-le.
|
Si vous préférez voir votre code, prenez-le.
|
||||||
|
|
||||||
→ [Lisez la présentation →](/fr/presentation/projets/)
|
→ [Lisez la présentation →](/fr/showcase/projects/)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ OpenAIのfine-tune、AnthropicのAPI評価、ほとんどのLoRAツール(Axol
|
|||||||
"content": "Modules implementing `SplSubject` can broadcast events…"
|
"content": "Modules implementing `SplSubject` can broadcast events…"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
これは[Oracle](/ai/oracle/)が内部で使用している正確なファイルです。
|
これは[Oracle](/ja/ai/oracle/)が内部で使用している正確なファイルです。
|
||||||
|
|
||||||
## チャンクの導出方法
|
## チャンクの導出方法
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ description: "単一または複数のターンで行われる、任意のOllama
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->system('Be terse.'); // optional system prompt
|
$chat->system('Be terse.'); / optional system prompt
|
||||||
$chat->model('qwen2.5-coder:14b'); // override the configured model
|
$chat->model('qwen2.5-coder:14b'); / override the configured model
|
||||||
$chat->temperature(0.2); // override config
|
$chat->temperature(0.2); / override config
|
||||||
$chat->maxTokens(512); // override config
|
$chat->maxTokens(512); / override config
|
||||||
|
|
||||||
$chat->user('Hello'); // append a user message
|
$chat->user('Hello'); / append a user message
|
||||||
$chat->assistant('Hi.'); // append an assistant message (rare)
|
$chat->assistant('Hi.'); / append an assistant message (rare)
|
||||||
|
|
||||||
$reply = $chat->complete(); // run the call, return text
|
$reply = $chat->complete(); / run the call, return text
|
||||||
$reply = $chat->ask('How are you?'); // = ->user(...)->complete()
|
$reply = $chat->ask('How are you?'); / = ->user(...)->complete()
|
||||||
|
|
||||||
$chat->reset(); // clear messages, keep model + system
|
$chat->reset(); / clear messages, keep model + system
|
||||||
$chat->history(); // [{role, content}, …]
|
$chat->history(); / [{role, content}, …]
|
||||||
```
|
```
|
||||||
## 一発
|
## 一発
|
||||||
```php
|
```php
|
||||||
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
|
|||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
|
|
||||||
$chat->user('Name three Nibiru singletons.');
|
$chat->user('Name three Nibiru singletons.');
|
||||||
$singletons = $chat->complete(); // appended to history
|
$singletons = $chat->complete(); / appended to history
|
||||||
|
|
||||||
$chat->user('What does the second one do?');
|
$chat->user('What does the second one do?');
|
||||||
$detail = $chat->complete(); // model has full context
|
$detail = $chat->complete(); / model has full context
|
||||||
```
|
```
|
||||||
## モデルとスタイルを個別呼び出しごとに上書きする
|
## モデルとスタイルを個別呼び出しごとに上書きする
|
||||||
```php
|
```php
|
||||||
@@ -65,7 +65,7 @@ chat.provider = "anthropic"
|
|||||||
anthropic.api_key = "sk-ant-..."
|
anthropic.api_key = "sk-ant-..."
|
||||||
anthropic.model = "claude-haiku-4-5-20251001"
|
anthropic.model = "claude-haiku-4-5-20251001"
|
||||||
```
|
```
|
||||||
チャットプラグインはまだフレームワークモジュールにAnthropicトランスポートを含んでいません — 現在、ドキュメントサイトの`scripts/lib/providers.mjs`パターンが参考となっています。([マイルストーン](/ja/ai/milestones/)をご覧ください。)
|
チャットプラグインはまだフレームワークモジュールにAnthropicトランスポートを含んでいません — 現在、ドキュメントサイトの`scripts/lib/providers.mjs`パターンが参考となっています。([マイルストーン](/ja/ai/roadmap/)をご覧ください。)
|
||||||
|
|
||||||
## 実践的なパターン: チャットをアクションとして
|
## 実践的なパターン: チャットをアクションとして
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ Embedプラグインは、Ollamaの`/api/embeddings`に薄いラッパーであ
|
|||||||
```php
|
```php
|
||||||
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
$embed = (new \Nibiru\Module\Ai\Ai())->embed();
|
||||||
|
|
||||||
$vec = $embed->one('controller'); // float[]
|
$vec = $embed->one('controller'); / float[]
|
||||||
$vectors = $embed->batch(['a', 'b', 'c']); // float[][]
|
$vectors = $embed->batch(['a', 'b', 'c']); / float[][]
|
||||||
|
|
||||||
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1
|
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[]
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
|
||||||
```
|
```
|
||||||
## パターン: 遠近似の文字列を重複除去する
|
## パターン: 遠近似の文字列を重複除去する
|
||||||
```php
|
```php
|
||||||
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
|
|||||||
return $best[0];
|
return $best[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication'
|
echo bestTag('User::isAuthorized', $tagVecs, $embed); / → 'authentication'
|
||||||
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably)
|
echo bestTag('Pageination::setTable', $tagVecs, $embed); / → 'database' (probably)
|
||||||
```
|
```
|
||||||
## ストレージ
|
## ストレージ
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (prob
|
|||||||
|
|
||||||
`Embed::pack()` を使用して、それらを 4 バイトの浮動小数点数として base64 エンコードします。
|
`Embed::pack()` を使用して、それらを 4 バイトの浮動小数点数として base64 エンコードします。
|
||||||
```php
|
```php
|
||||||
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string
|
$compact = Embed::pack($vec); / ~4 KB → ~5.3 KB base64 string
|
||||||
$vec = Embed::unpack($compact);
|
$vec = Embed::unpack($compact);
|
||||||
```
|
```
|
||||||
RAG プラグインは、この形式を使用して内部の JSON ファイルを管理しています。
|
RAG プラグインは、この形式を使用して内部の JSON ファイルを管理しています。
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
|
|||||||
// Multi-turn
|
// Multi-turn
|
||||||
$chat = $ai->chat();
|
$chat = $ai->chat();
|
||||||
$chat->user('How do I scaffold a module?');
|
$chat->user('How do I scaffold a module?');
|
||||||
$chat->user('And add Graylog hooks?'); // referrs to previous turn
|
$chat->user('And add Graylog hooks?'); / referrs to previous turn
|
||||||
echo $chat->complete();
|
echo $chat->complete();
|
||||||
|
|
||||||
// Override per call
|
// Override per call
|
||||||
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
|
|||||||
```
|
```
|
||||||
コンパクトなストレージ:
|
コンパクトなストレージ:
|
||||||
```php
|
```php
|
||||||
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim
|
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
|
||||||
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
||||||
```
|
```
|
||||||
### 3. RAG — 収集、検索、基準化
|
### 3. RAG — 収集、検索、基準化
|
||||||
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
|
|||||||
$rag = $ai->rag('product-help');
|
$rag = $ai->rag('product-help');
|
||||||
|
|
||||||
// One-time ingestion
|
// One-time ingestion
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
$rag->ingestFile('/var/data/manual.pdf.txt');
|
$rag->ingestFile('/var/data/manual.pdf.txt');
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
|
|||||||
$ai = new \Nibiru\Module\Ai\Ai();
|
$ai = new \Nibiru\Module\Ai\Ai();
|
||||||
|
|
||||||
$agent = $ai->agent()->withTools([
|
$agent = $ai->agent()->withTools([
|
||||||
new Tools\PdoQuery(), // read-only SQL
|
new Tools\PdoQuery(), / read-only SQL
|
||||||
new Tools\HttpGet(), // fetch URLs
|
new Tools\HttpGet(), / fetch URLs
|
||||||
new Tools\FileRead(), // read project files
|
new Tools\FileRead(), / read project files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $agent->run('How many active users registered last week?');
|
echo $agent->run('How many active users registered last week?');
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ RAG プラグインは、製品ビルダーにとって AI モジュールのキ
|
|||||||
use Nibiru\Module\Ai\Ai;
|
use Nibiru\Module\Ai\Ai;
|
||||||
|
|
||||||
$ai = new Ai();
|
$ai = new Ai();
|
||||||
$rag = $ai->rag('product-help'); // a named collection
|
$rag = $ai->rag('product-help'); / a named collection
|
||||||
|
|
||||||
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/
|
$rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
|
||||||
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
|
||||||
|
|
||||||
echo $rag->ask('How do I cancel my subscription?');
|
echo $rag->ask('How do I cancel my subscription?');
|
||||||
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
|
|||||||
```
|
```
|
||||||
## API リファレンス
|
## API リファレンス
|
||||||
```php
|
```php
|
||||||
$rag = $ai->rag('name'); // get/create a named collection
|
$rag = $ai->rag('name'); / get/create a named collection
|
||||||
|
|
||||||
// --- Ingestion ---
|
// --- Ingestion ---
|
||||||
$rag->ingestText($text, $metadata = []); // single chunk
|
$rag->ingestText($text, $metadata = []); / single chunk
|
||||||
$count = $rag->ingestFile('path'); // returns chunks added
|
$count = $rag->ingestFile('path'); / returns chunks added
|
||||||
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive
|
$count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
|
||||||
|
|
||||||
// --- Querying ---
|
// --- Querying ---
|
||||||
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …]
|
$hits = $rag->search('query', $k = null); / [{score, text, metadata}, …]
|
||||||
$answer = $rag->ask('question', $k = null); // top-K → chat call
|
$answer = $rag->ask('question', $k = null); / top-K → chat call
|
||||||
|
|
||||||
// --- Maintenance ---
|
// --- Maintenance ---
|
||||||
$rag->reset(); // forget everything (deletes file)
|
$rag->reset(); / forget everything (deletes file)
|
||||||
$n = $rag->size(); // number of chunks
|
$n = $rag->size(); / number of chunks
|
||||||
```
|
```
|
||||||
## チューニングの調整項目
|
## チューニングの調整項目
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public function pageAction() {
|
|||||||
View::forwardTo('/login');
|
View::forwardTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
ロール認識のチェックのために、デモンストレーションアプリケーションは同じモジュールから `Acl` プラグインを使用しています。
|
ロール認識のチェックのために、デモンストレーションアプリケーションは同じモジュールから `Acl` プラグインを使用しています。
|
||||||
@@ -178,7 +178,7 @@ public function submitAction() {
|
|||||||
http_response_code(419);
|
http_response_code(419);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...handle submission...
|
/ ...handle submission...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
フォームに `<input type="hidden" name="csrf" value="{$csrf}">` を埋め込んでください。
|
フォームに `<input type="hidden" name="csrf" value="{$csrf}">` を埋め込んでください。
|
||||||
|
|||||||
@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
|
|||||||
## 設定の読み込み
|
## 設定の読み込み
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
$cfg = \Nibiru\Config::getInstance()->getConfig();
|
||||||
$cfg['DATABASE']['driver']; // 'pdo'
|
$cfg['DATABASE']['driver']; / 'pdo'
|
||||||
$cfg['SETTINGS']['page.url']; // 'https://my-app.local'
|
$cfg['SETTINGS']['page.url']; / 'https://my-app.local'
|
||||||
```
|
```
|
||||||
深くネストされた設定には、View インターフェースから型付けされた定数を使用してください。
|
深くネストされた設定には、View インターフェースから型付けされた定数を使用してください。
|
||||||
```php
|
```php
|
||||||
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css']
|
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
|
||||||
```
|
```
|
||||||
## モジュールの設定
|
## モジュールの設定
|
||||||
|
|
||||||
各モジュールの `application/module/<name>/settings/` 以下には独自の INI ファイルを配置できます。Registry はそれらを自動的に取り込み、以下の通りに公開します:
|
各モジュールの `application/module/<name>/settings/` 以下には独自の INI ファイルを配置できます。Registry はそれらを自動的に取り込み、以下の通りに公開します:
|
||||||
```php
|
```php
|
||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime; // from [USERS] section in users.ini
|
$users->session_lifetime; / from [USERS] section in users.ini
|
||||||
```
|
```
|
||||||
レジストリは `<module>.<env>.ini` を `<module>.ini` よりも優先します。これにより、環境ごとのオーバーライドが無料で利用できます。
|
レジストリは `<module>.<env>.ini` を `<module>.ini` よりも優先します。これにより、環境ごとのオーバーライドが無料で利用できます。
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
|
|||||||
```
|
```
|
||||||
ベースコントローラーからの便利ヘルパー:
|
ベースコントローラーからの便利ヘルパー:
|
||||||
```php
|
```php
|
||||||
$this->getRequest('id', false); // $_REQUEST['id'] ?? false
|
$this->getRequest('id', false); / $_REQUEST['id'] ?? false
|
||||||
$this->getPost('email', ''); // $_POST['email'] ?? ''
|
$this->getPost('email', ''); / $_POST['email'] ?? ''
|
||||||
$this->getGet('page', 1); // $_GET['page'] ?? 1
|
$this->getGet('page', 1); / $_GET['page'] ?? 1
|
||||||
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI']
|
$this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
|
||||||
$this->getFiles('upload'); // $_FILES['upload']
|
$this->getFiles('upload'); / $_FILES['upload']
|
||||||
$this->getSession('auth'); // $_SESSION['auth']
|
$this->getSession('auth'); / $_SESSION['auth']
|
||||||
```
|
```
|
||||||
これらは存在する理由は、`Controller`が`final`に耐えられるように設計されていることです:テストで子クラスを置き換えてモックすることができます。
|
これらは存在する理由は、`Controller`が`final`に耐えられるように設計されていることです:テストで子クラスを置き換えてモックすることができます。
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ $this->getSession('auth'); // $_SESSION['auth']
|
|||||||
|
|
||||||
アクション内でリダイレクトするには:
|
アクション内でリダイレクトするには:
|
||||||
```php
|
```php
|
||||||
View::forwardTo('/login'); // 302 to the URL, exits
|
View::forwardTo('/login'); / 302 to the URL, exits
|
||||||
View::forwardToJsonHeader(); // sets Content-Type: application/json
|
View::forwardToJsonHeader(); / sets Content-Type: application/json
|
||||||
```
|
```
|
||||||
`forwardToJsonHeader()` は、JSON エンドポイントの標準的なパターンです。ヘッダーを設定し、`data` を割り当ててから返す。その後、表示層が残りの処理を行います。
|
`forwardToJsonHeader()` は、JSON エンドポイントの標準的なパターンです。ヘッダーを設定し、`data` を割り当ててから返す。その後、表示層が残りの処理を行います。
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ public function run() {
|
|||||||
|
|
||||||
if (Config::getInstance()->getConfig()
|
if (Config::getInstance()->getConfig()
|
||||||
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
|
||||||
new Model(false); // 1. (re)generate models from schema
|
new Model(false); / 1. (re)generate models from schema
|
||||||
}
|
}
|
||||||
|
|
||||||
Router::getInstance()->route(); // 2. parse the URL
|
Router::getInstance()->route(); / 2. parse the URL
|
||||||
Auto::loader()->loadModelFiles(); // 3. load model files
|
Auto::loader()->loadModelFiles(); / 3. load model files
|
||||||
Auto::loader()->loadModules(); // 4. load module classes
|
Auto::loader()->loadModules(); / 4. load module classes
|
||||||
|
|
||||||
$tpl = Router::getInstance()->tplName();
|
$tpl = Router::getInstance()->tplName();
|
||||||
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
|
||||||
|
|
||||||
if (is_file($controllerFile)) { // 5. controller file exists
|
if (is_file($controllerFile)) { / 5. controller file exists
|
||||||
require_once $controllerFile;
|
require_once $controllerFile;
|
||||||
$class = "Nibiru\\{$tpl}Controller";
|
$class = "Nibiru\\{$tpl}Controller";
|
||||||
$controller = new $class();
|
$controller = new $class();
|
||||||
@@ -49,7 +49,7 @@ public function run() {
|
|||||||
$action = $_REQUEST['_action'] . 'Action';
|
$action = $_REQUEST['_action'] . 'Action';
|
||||||
$controller->navigationAction();
|
$controller->navigationAction();
|
||||||
if (method_exists($controller, $action)) {
|
if (method_exists($controller, $action)) {
|
||||||
$controller->$action(); // 6. optional named action
|
$controller->$action(); / 6. optional named action
|
||||||
}
|
}
|
||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
} else {
|
} else {
|
||||||
@@ -57,9 +57,9 @@ public function run() {
|
|||||||
$controller->pageAction();
|
$controller->pageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::getInstance()->display(); // 7. render Smarty
|
Display::getInstance()->display(); / 7. render Smarty
|
||||||
} else {
|
} else {
|
||||||
// 8. soft 404 — render the configured error controller
|
/ 8. soft 404 — render the configured error controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
|
|||||||
```
|
```
|
||||||
### ライフサイクル
|
### ライフサイクル
|
||||||
```
|
```
|
||||||
create // reset the static buffer; call before building a new form
|
create / reset the static buffer; call before building a new form
|
||||||
addForm // wrap the buffer in <form>...</form> and return as a string
|
addForm / wrap the buffer in <form>...</form> and return as a string
|
||||||
```
|
```
|
||||||
:::caution[名前の不一致は意図的なもの、ある程度]
|
:::caution[名前の不一致は意図的なもの、ある程度]
|
||||||
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — プレフィックスが要素が `<input type="…">` (Input プレフィックス) であるかどうか(Input プレフィックス)か、他のタグ(Type プレフィックス)であるかどうかに一致します。これは奇妙ですが安定しています;CLI の `./nibiru -c` スキャフォールディングは同じパターンを使用しているため、筋肉記憶が働きます。
|
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — プレフィックスが要素が `<input type="…">` (Input プレフィックス) であるかどうか(Input プレフィックス)か、他のタグ(Type プレフィックス)であるかどうかに一致します。これは奇妙ですが安定しています;CLI の `./nibiru -c` スキャフォールディングは同じパターンを使用しているため、筋肉記憶が働きます。
|
||||||
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
|
|||||||
```php
|
```php
|
||||||
use Nibiru\Factory\Form;
|
use Nibiru\Factory\Form;
|
||||||
|
|
||||||
Form::create(); // reset the static buffer
|
Form::create(); / reset the static buffer
|
||||||
|
|
||||||
Form::addOpenDiv(['class' => 'form-group']);
|
Form::addOpenDiv(['class' => 'form-group']);
|
||||||
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
|
||||||
@@ -187,7 +187,7 @@ class formsController extends Controller {
|
|||||||
'required' => 'required',
|
'required' => 'required',
|
||||||
'class' => 'contacts-input form-control',
|
'class' => 'contacts-input form-control',
|
||||||
]);
|
]);
|
||||||
// ...more fields...
|
/ ...more fields...
|
||||||
$this->form = Form::addForm([
|
$this->form = Form::addForm([
|
||||||
'name' => 'newregister',
|
'name' => 'newregister',
|
||||||
'method' => 'post',
|
'method' => 'post',
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
|
|||||||
どこからでも読み返す:
|
どこからでも読み返す:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$cfg->session_lifetime; // 7200
|
$cfg->session_lifetime; / 7200
|
||||||
$cfg->password_min_length; // 12
|
$cfg->password_min_length; / 12
|
||||||
$cfg->allowed_roles; // [admin, editor, standard]
|
$cfg->allowed_roles; / [admin, editor, standard]
|
||||||
```
|
```
|
||||||
環境オーバーレイ:`APPLICATION_ENV=production` の場合、`users.production.ini` が `users.ini` よりも優先されます。
|
環境オーバーレイ:`APPLICATION_ENV=production` の場合、`users.production.ini` が `users.ini` よりも優先されます。
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
|
|||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
|
||||||
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
|
||||||
|
|
||||||
$analytics->trackPageView(); // internally calls notify()
|
$analytics->trackPageView(); / internally calls notify()
|
||||||
```
|
```
|
||||||
各観察者の `update($subject)` は、分析インスタンスを受け取り、関心のあるイベントデータを取得します。
|
各観察者の `update($subject)` は、分析インスタンスを受け取り、関心のあるイベントデータを取得します。
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ class.plugin.pos[] = "" ; reserved
|
|||||||
プラグインクラスは、**複数形の**名前空間 `Plugins` の下にあります:
|
プラグインクラスは、**複数形の**名前空間 `Plugins` の下にあります:
|
||||||
```php
|
```php
|
||||||
// application/module/billing/plugins/invoice.php
|
// application/module/billing/plugins/invoice.php
|
||||||
namespace Nibiru\Module\Billing\Plugins; // ← plural
|
namespace Nibiru\Module\Billing\Plugins; / ← plural
|
||||||
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
||||||
```
|
```
|
||||||
名前空間が一致しないと、オートローダーのミスが発生します。CLI スキャフォールド (`./nibiru -m billing`) は正しい名前空間を生成してくれます。
|
名前空間が一致しないと、オートローダーのミスが発生します。CLI スキャフォールド (`./nibiru -m billing`) は正しい名前空間を生成してくれます。
|
||||||
@@ -210,7 +210,7 @@ class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
|
|||||||
モジュールの INI ファイルを **登録しないでください** — [Registry](/en/core/registry/) は、`[AUTOLOADER]` がモジュールクラスを読み込んだ後、`application/module/<name>/settings/*.ini` を走査して自動的に発見します。各 INI の `[<MODULE>]`(大文字)セクションは次のようになります:
|
モジュールの INI ファイルを **登録しないでください** — [Registry](/en/core/registry/) は、`[AUTOLOADER]` がモジュールクラスを読み込んだ後、`application/module/<name>/settings/*.ini` を走査して自動的に発見します。各 INI の `[<MODULE>]`(大文字)セクションは次のようになります:
|
||||||
```php
|
```php
|
||||||
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
|
||||||
$cfg->invoice_prefix; // [BILLING] invoice.prefix → property
|
$cfg->invoice_prefix; / [BILLING] invoice.prefix → property
|
||||||
```
|
```
|
||||||
レジストリは、`APPLICATION_ENV` が一致する場合に `<module>.<env>.ini`(例:`billing.production.ini`)を優先します。
|
レジストリは、`APPLICATION_ENV` が一致する場合に `<module>.<env>.ini`(例:`billing.production.ini`)を優先します。
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class productsController extends Controller
|
|||||||
{
|
{
|
||||||
public function pageAction() {
|
public function pageAction() {
|
||||||
$products = new products();
|
$products = new products();
|
||||||
Pageination::setEntriesPerPage(25); // optional; default from INI
|
Pageination::setEntriesPerPage(25); / optional; default from INI
|
||||||
Pageination::setTable($products);
|
Pageination::setTable($products);
|
||||||
$rows = Pageination::loadTableAsArray();
|
$rows = Pageination::loadTableAsArray();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ description: "モジュールが自動的に検出され、その設定が実行
|
|||||||
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
|
||||||
$users->session_lifetime;
|
$users->session_lifetime;
|
||||||
$users->password_min_length;
|
$users->password_min_length;
|
||||||
$users->allowed_roles; // array
|
$users->allowed_roles; / array
|
||||||
```
|
```
|
||||||
モジュール自体では、この内容をセッターでラップする慣習があります。
|
モジュール自体では、この内容をセッターでラップする慣習があります。
|
||||||
```php
|
```php
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Nibiru は、どの設定も不要で SEO フレンドリーな URL を自動検
|
|||||||
// /users/edit/42 → $_REQUEST['id'] = '42'
|
// /users/edit/42 → $_REQUEST['id'] = '42'
|
||||||
public function editAction() {
|
public function editAction() {
|
||||||
$id = (int) ($_REQUEST['id'] ?? 0);
|
$id = (int) ($_REQUEST['id'] ?? 0);
|
||||||
// ...
|
/ ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
数値的または名前付きパラメータ以外の場合、クエリ文字列を使用することをお勧めします。
|
数値的または名前付きパラメータ以外の場合、クエリ文字列を使用することをお勧めします。
|
||||||
@@ -62,9 +62,9 @@ api.v1.products.params[] = "id"
|
|||||||
|
|
||||||
## ルーティングヘルパー
|
## ルーティングヘルパー
|
||||||
```php
|
```php
|
||||||
Router::getInstance()->currentPage(); // 'products'
|
Router::getInstance()->currentPage(); / 'products'
|
||||||
Router::getInstance()->tplName(); // 'products' (controller stem for templates)
|
Router::getInstance()->tplName(); / 'products' (controller stem for templates)
|
||||||
Router::getInstance()->getController(); // alias for currentPage()
|
Router::getInstance()->getController(); / alias for currentPage()
|
||||||
```
|
```
|
||||||
これらはコントローラーとテンプレート内で役立ちます:
|
これらはコントローラーとテンプレート内で役立ちます:
|
||||||
```smarty
|
```smarty
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ description: "ボタン、カード、コールアウト、ヒーロー — コ
|
|||||||
|
|
||||||
黒い文字がクレーム色の背景にある長方形。エディタリー風。グラデーションや光沢はありません。
|
黒い文字がクレーム色の背景にある長方形。エディタリー風。グラデーションや光沢はありません。
|
||||||
```html
|
```html
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/ja/start/installation/">
|
||||||
<span>Read the docs</span>
|
<span>Read the docs</span>
|
||||||
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
<span class="atelier-button__arrow" aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -162,7 +162,7 @@ description: "ボタン、カード、コールアウト、ヒーロー — コ
|
|||||||
Nibiru is a modular PHP framework for builders who ship.
|
Nibiru is a modular PHP framework for builders who ship.
|
||||||
</p>
|
</p>
|
||||||
<div class="atelier-hero__cta">
|
<div class="atelier-hero__cta">
|
||||||
<a class="atelier-button atelier-button--primary" href="/en/start/">
|
<a class="atelier-button atelier-button--primary" href="/ja/start/installation/">
|
||||||
Read the docs <span aria-hidden="true">→</span>
|
Read the docs <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user