Skip to content

refactor!: modernize Article, Category and StructureElement#6531

Merged
gharlan merged 1 commit into
6.xfrom
refactor/modernize-content-classes
May 31, 2026
Merged

refactor!: modernize Article, Category and StructureElement#6531
gharlan merged 1 commit into
6.xfrom
refactor/modernize-content-classes

Conversation

@gharlan
Copy link
Copy Markdown
Member

@gharlan gharlan commented May 31, 2026

Summary

Applies the same modernization pattern as the language (#6521) and mediapool (#6516) PRs to the structure-element classes.

StructureElement (abstract base)

  • typed promoted readonly properties (camelCase: parentId → removed from base, clangId, createDate, …)
  • additionalData array carries the remaining cache fields (meta-info etc.)
  • new require(int $id, ?int $clang = null): static next to get(), analogous to Language::require()
  • abstract fromCache(array): ?static factory dispatched from StructureElement::get
  • $path is now list<int> (mirroring MediaCategory); getPath() / getPathAsArray() removed, getValue('path') still returns the pipe-string for BC
  • metaInfoPrefix is an abstract get-only property on the base (PHP 8.4 syntax)
  • removed StructureElement::$classVars / getClassVars / resetClassVars (only existed to support the old dynamic-property design); the one caller (ArticleHandler::article2startarticle) uses the SQL field-name list directly
  • removed dead last_update_stamp cache column

Article (final)

  • own private __construct(array $data) parses the cache row, calls parent::__construct(...) once with named args, then sets its own readonly properties — no parent-arg duplication, single-phase init
  • categoryId (nullable), templateKey, startArticle as own properties
  • no longer carries parentId (semantically didn't make sense — see comment thread)
  • getCategory() exposes the containing/own category; getParent() is protected and used internally for the tree walk
  • isSiteStartArticle(), isNotFoundArticle(), isStartArticle() live here (article concepts)
  • hasTemplate() removed — check null !== $article->templateKey instead

Category (final)

  • own private __construct(array $data) like Article
  • parentId (nullable for root) sits here
  • getParent() is public (visibility widened from protected base)
  • fromCache() returns null for non-start-article cache rows (former Category::class === static::class guard inside the base get() is gone)
  • categories no longer carry name/priority/template/startarticle fields — those are article-only; cross-type access via getValue returns null

Rector rules

Cover the visible signature changes for downstream addons:

  • Article::getCategoryIdcategoryId (changed int?int)
  • Article::getTemplateKeytemplateKey
  • Article::hasTemplatetemplateKey (changed bool?string, callers using the bool need manual adjustment)
  • StructureElement::getPathpath (changed stringlist<int>)
  • StructureElement::getPathAsArraypath
  • StructureElement getters → properties (getId, getParentId, getClangId, getName, getPriority, getCreateDate/getUpdateDate/getCreateUser/getUpdateUser)
  • Category::getName/getPriorityname/priority (both classes have name/priority properties — Article from name/priority columns, Category from catname/catpriority)

Other

  • StructurePermission::hasCategoryPerm(?int) — accepts null (mirrors the now-nullable Article::$categoryId)
  • CategoryTreeRenderer::formatLi() removed — only one caller (ArticleList::listItem) and only ever invoked with Article, dead Category branch eliminated; logic inlined in ArticleList
  • tests: ArticleTest extended with getClosest/getClosestValue/isOnlineIncludingParents data-provider tests, mirroring CategoryTest

Same modernization pattern applied as in the language and mediapool PRs:

- typed promoted readonly properties on `StructureElement` (camelCase),
  `additionalData` array carries the remaining cache fields (meta-info etc).
- subclass-specific properties (`Article::$categoryId`/`$templateKey`/
  `$startArticle`, `Category::$parentId`) sit on the subclass and are
  populated by its own `private __construct(array $data)` after the
  parent constructor is invoked — keeps `readonly` while avoiding the
  forwarding of every parent argument.
- abstract `fromCache(array): ?static` factory shared via
  `StructureElement::get`; `Category::fromCache` returns null for
  non-start-article rows (former type guard inside `get` is gone).
- new `require(int $id, ?int $clang = null): static` next to `get()`.
- `path` is now `list<int>` (mirroring `MediaCategory`), `getPath()` /
  `getPathAsArray()` removed; `getValue('path')` still returns the
  pipe-string for BC.
- `getParent` only lives on `Category` publicly; `Article` has a
  protected `getParent` returning the containing or parent category
  (used internally by `getClosest`/`getClosestValue`/
  `isOnlineIncludingParents`).
- `isStartArticle`/`isSiteStartArticle`/`isNotFoundArticle`/`hasTemplate`
  moved out of the base class — they are article concepts.
  `hasTemplate` removed entirely; check `null !== $article->templateKey`.
- `metaInfoPrefix` is an abstract get-only property on the base.
- `Article` no longer carries a `parentId`; it has `categoryId` (nullable).
- removed dead `last_update_stamp` cache column and the
  `StructureElement::$classVars` / `getClassVars` / `resetClassVars`
  machinery that only existed for the old dynamic-property design.
- rector rules cover the visible signature changes
  (`getTemplateKey -> templateKey`, `getCategoryId -> categoryId`,
  `getPath/getPathAsArray -> path`, `hasTemplate -> templateKey`,
  StructureElement getters -> properties, etc.).
- `StructurePermission::hasCategoryPerm` accepts `?int` now (mirrors
  the nullable `Article::$categoryId`).
@gharlan gharlan added this to the REDAXO 6.0 milestone May 31, 2026
@gharlan gharlan merged commit 010ecb4 into 6.x May 31, 2026
16 checks passed
@gharlan gharlan deleted the refactor/modernize-content-classes branch May 31, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants