summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLester Caine <lester@lsces.co.uk>2026-06-14 16:23:51 +0100
committerLester Caine <lester@lsces.co.uk>2026-06-14 16:23:51 +0100
commit4c104c66860804be236136622f8d203e5d6a3d7a (patch)
tree18dcecefb8117d140fa609e8fd09ad9ae18a60c1
parent924893d051920a93553a16b148efb33d1691285c (diff)
downloadliberty-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>
-rw-r--r--CLAUDE.md106
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`.