diff options
| author | Lester Caine <lester@lsces.co.uk> | 2026-06-14 16:23:51 +0100 |
|---|---|---|
| committer | Lester Caine <lester@lsces.co.uk> | 2026-06-14 16:23:51 +0100 |
| commit | 4c104c66860804be236136622f8d203e5d6a3d7a (patch) | |
| tree | 18dcecefb8117d140fa609e8fd09ad9ae18a60c1 /CLAUDE.md | |
| parent | 924893d051920a93553a16b148efb33d1691285c (diff) | |
| download | liberty-4c104c66860804be236136622f8d203e5d6a3d7a.tar.gz liberty-4c104c66860804be236136622f8d203e5d6a3d7a.tar.bz2 liberty-4c104c66860804be236136622f8d203e5d6a3d7a.zip | |
Add CLAUDE.md with liberty xref machinery developer notes
Covers LibertyXrefType, dual-guid schema, xref display path,
parseDataHash, storeXref, owner change, Firebird GROUP BY.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2ac7950 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,106 @@ +# Liberty Package — Developer Notes + +## LibertyXref / xorder +`liberty_xref.xorder` — used for BOM grouping and sort. Must be explicitly selected +in queries; it is not auto-included in standard SELECT lists. + +## LibertyXrefType — instance class +`LibertyXrefType` is an **instance class**, not a bag of statics. Construct with +`new LibertyXrefType( $contentTypeGuid, $packageGuid = null )`. In page/class code, +always access it via `LibertyContent::xrefType()` which lazily creates and caches the +instance. The five runtime query methods (`getDisplayGroups`, `getTypeMarkers`, +`getAvailableItems`, `getTemplateFormats`, `getContentTypeMarkers`) are instance methods. +Admin cross-type queries (`getXrefTypeList`, `getContentTypeGuids`, `getGroupList`) +remain static. + +## Dual-guid xref schema (package-level + content-type-level) +A package with multiple content types can define xref groups/items at two levels: +- **Package-level** — groups shared across all content types in the package, keyed by the package guid +- **Content-type-level** — groups specific to one content type, keyed by the content type guid + +**Stock is the reference implementation:** +- Package-level (`'stock'`): `stgrp`, `supplier`, `kitlocker` — apply to both assemblies and components +- Content-type-level (`'stockcomponent'`): `quantity`, `values`; (`'stockassembly'`): `quantity` + +To support this, pass `$packageGuid` when constructing `LibertyXrefType` or `LibertyXrefInfo` +(both accept it as an optional second argument). The `mPackageGuid` property on `LibertyContent` +is set automatically by `registerContentType()` when `handler_package` differs from the content +type guid — so subclasses get it for free. + +When writing xref JOIN queries that span both levels, always join item↔group on +`t.content_type_guid = s.content_type_guid` (self-consistent); apply the guid `IN()` filter +only in the WHERE clause on `s`. Putting the filter in the JOIN ON instead causes +cross-matching when two guids share an `x_group` name. + +## LibertyXrefGroup display path + +**PHP pattern** — display and edit pages: +```php +$gContent->loadXrefInfo(); +$gBitSmarty->assign( 'gXrefInfo', $gContent->mXrefInfo ); +``` + +**Template pattern** — view and edit templates: +```smarty +{foreach $gXrefInfo->mGroups as $xrefGroup} + {include file=$gContent->getXrefListTemplate($xrefGroup->mTemplate) + xrefGroup=$xrefGroup allow_edit=false} {* true for edit pages *} +{/foreach} +``` + +Group templates receive `$xrefGroup` (LibertyXrefGroup object). First two lines must be: +```smarty +{assign var=xrefAllowEdit value=$allow_edit|default:false} +{assign var=isHistory value=($xrefGroup->mXGroup eq 'history')} +``` + +Fallback for groups with no specific template → `liberty/list_xref.tpl`. + +View pages pass `allow_edit=false` (or omit), edit pages pass `allow_edit=true`. + +**Linked content fields (`linked_title` / `linked_data`)** — `LibertyXrefType::loadContent()` +LEFT JOINs `liberty_content lc_linked ON lc_linked.content_id = x.xref` and exposes +`lc_linked.title AS linked_title` and `lc_linked.data AS linked_data` on every xref row. +These come from the **linked content item's** `liberty_content` row (via the `x.xref` FK), +NOT from the xref row's own `xkey`/`xkey_ext`/`data` columns (which are already available +as `$xrefInfo.xkey`, `$xrefInfo.xkey_ext`, `$xrefInfo.data` without any join). +When `x.xref > 0` these fields hold the title and description of the linked item (contact, +component, assembly, etc.). `liberty_content` has no `xkey_ext` equivalent — if further +fields from the linked item are needed, add them to the SELECT in `loadContent()` as +additional `lc_linked.*` aliases, or use a correlated subquery for linked xref data. + +- **View templates**: use `$xrefInfo.linked_title` and `$xrefInfo.linked_data` directly — no + separate enrichment query needed. +- **Edit templates** (`edit_xref.php` path): `enrichXrefDisplay()` is called on the single row + before display. Override this in the content class (e.g. `StockBase::enrichXrefDisplay()`) + to set `xref_title` for the edit form. The two paths use different field names by design. +- **Extra fields** (e.g. `part_size` from a second xref): override `loadXrefInfo()` in the + content class, call `parent::loadXrefInfo()` first, then enrich the group rows. Use + `array_map( fn($r) => $r['xref'], $group->mXrefs )` — NOT `array_column()` — to extract + xref values from `LibertyXref` objects (ArrayAccess; `array_column` ignores offsetGet on + some PHP builds). + +## Firebird GROUP BY strictness +Firebird requires every non-aggregate column in SELECT to appear in GROUP BY — including +`lc.data`, `lc.title` etc. Correlated scalar subqueries in SELECT (e.g. `SELECT FIRST 1 ...`) +are exempt. MySQL is more lenient; Firebird is not. + +## parseDataHash +`LibertyContent::parseDataHash( &$pParamHash )` takes its argument **by reference** — always +assign to a named variable before calling, never pass a literal array. +```php +$parseHash = [ 'data' => $row['data'], 'format_guid' => $row['format_guid'] ?? 'bithtml' ]; +$row['parsed_data'] = LibertyContent::parseDataHash( $parseHash ); +``` + +## storeXref +`storeXref()` takes `&$pParamHash` by reference — always assign hash to a named variable +before calling. Passing a literal array is a fatal error. + +## Content owner change +`edit_content_owner_inc.tpl` provides an Owner dropdown gated on: +- Feature `liberty_allow_change_owner` active +- Permission `p_liberty_edit_content_owner` + +Include inside any edit form to allow reassigning `user_id`. `LibertyContent::store()` +handles `owner_id` + `current_owner_id` → updates `lc.user_id`. |
