Skip to content

refactor: split the sections metabox into per-row renderers - #242

Merged
erseco merged 1 commit into
mainfrom
refactor/split-sections-metabox
Jul 29, 2026
Merged

refactor: split the sections metabox into per-row renderers#242
erseco merged 1 commit into
mainfrom
refactor/split-sections-metabox

Conversation

@erseco

@erseco erseco commented Jul 29, 2026

Copy link
Copy Markdown
Member

Follows #239, now merged; this branch is rebased onto main and carries a single commit.

(Reopened as a new PR: #240 was auto-closed when #239's branch was deleted on merge, and GitHub would not let it reopen.)

Fourth round on the PHPMD Code Size alerts. This one takes the worst method in the plugin.

render_sections_metabox()

Before Now
Cyclomatic complexity 37 resolved
NPath complexity 906,854,404 resolved
Length 184 lines resolved

It opened the table, then ran a loop whose body held four skip conditions, the label and title resolution, a ~45-line repeater branch and a ~55-line scalar branch — all inline. Split along the seams that were already there:

Method Responsibility
prepare_schema_row() skip rules and label/type resolution; null for a row that cannot be drawn
render_schema_row() dispatch; returns the meta key the row claims
render_repeater_field_row() the repeater table row
render_scalar_field_row() the scalar table row
render_scalar_control() single / rich / textarea
get_repeater_rows() stored rows, or one blank row to type into

render_sections_metabox() is now the table and the loop.

Five copies, not three

#239 deduplicated the help-text block across the three repeater controls. Tracing what the repeater rendering actually depends on — 30 methods in the transitive closure, 18 of them shared with render_sections_metabox — turned up two more copies of the same block inside the metabox itself, one per branch:

$before_description = $this->get_before_description_context( $meta_key, $slug, $raw_field );
$description        = $this->get_field_description( $raw_field );
$validation         = $this->get_field_validation_message( $raw_field );
$description_id     = '' !== $description ? $meta_key . '-description' : '';
$validation_id      = '' !== $validation ? $meta_key . '-validation' : '';
$describedby        = $this->build_describedby_ids( ... );

Both branches now call build_field_help_context() and render_help_descriptions().

That closure analysis also ruled out the plan stated in #239 — moving repeater rendering into its own class. With 18 helpers shared with the metabox, that would have duplicated exactly what #239 removed. Sharing the help subsystem first is the prerequisite; the class extraction becomes viable afterwards.

Result

main #239 This PR
Plugin-wide alerts 76 58 55

Remaining in class-documentate-documents.php: render_array_field() (NPath 576), render_array_field_item() (CC 15 · NPath 6,152), and the four class-level alerts.

Verification

This is a pure rendering refactor, so the suite passing is necessary but not sufficient. The markup was diffed directly: a schema covering every control type, both branches, help text, title/patternmsg resolution, stored values and skipped rows was rendered against the pre-refactor code and against this one.

Byte-for-byte identical, with one deliberate exception: the repeater row's help paragraphs now carry the same ids the scalar row's always had. Nothing references them, and it only surfaces when a schema declares a field and a repeater under one slug. The omission was an inconsistency between two copies of the same block, not a decision.

Two things that nearly produced a false result, and how they were caught:

  • The first harness used invented filter names to inject a schema. They do not exist, so it rendered the empty-schema path — a comparison of nothing against nothing. It now uses the real SchemaStorage setup the existing tests use, and asserts on the dump's size and content before the comparison is trusted.
  • The first diff came back identical but never reached the repeater's help paragraphs, because the description lived in repeaters while that branch reads from fields. Forcing that case is what surfaced the id change above.

prepare_schema_row() is pure, so the skip rules can finally be tested on their own rather than only through a rendered metabox. DocumentateSchemaRowTest covers them, plus the title/pattern precedence and the repeater row fallback.

make lint      ✓ PHPCS/WPCS clean
make test      ✓ 1871 tests, 13186 assertions
make test-e2e  ✓ 73 passed, 21 skipped

render_sections_metabox() was the worst method in the plugin: cyclomatic
complexity 37, NPath 906,854,404, 184 lines. It opened the table, then ran a
loop whose body held four skip conditions, the label and title resolution,
a ~45-line repeater branch and a ~55-line scalar branch, all inline.

Split along the seams that were already there:

  prepare_schema_row()      the skip rules and the label/type resolution,
                            returning null for a row that cannot be drawn
  render_schema_row()       dispatch, returning the meta key the row claims
  render_repeater_field_row()
  render_scalar_field_row()
  render_scalar_control()   single / rich / textarea
  get_repeater_rows()       stored rows, or one blank row to type into

All three of its alerts are gone; the method is now the table and the loop.

The scalar and repeater branches each carried their own copy of the help
text block - collect description, validation and leading text, derive the
ids, echo the trailing paragraphs. That is the same block deduplicated in
the previous commit for the three repeater controls, so there were five
copies, not three. Both now call build_field_help_context() and
render_help_descriptions().

prepare_schema_row() is pure, so the skip rules can finally be tested
directly instead of only through a rendered metabox.

Markup is byte-for-byte identical, verified by rendering a schema covering
every control type, both branches, help text, stored values and skipped
rows, then diffing the output against the pre-refactor code. One deliberate
exception: the repeater row's help paragraphs now carry the same ids the
scalar row's always had. Nothing references them, and it only shows up when
a schema declares a field and a repeater under one slug - the omission was
an inconsistency between two copies of the same block, not a decision.
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.36364% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../custom-post-types/class-documentate-documents.php 96.36% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@erseco
erseco merged commit 5eae1fc into main Jul 29, 2026
12 checks passed
@erseco
erseco deleted the refactor/split-sections-metabox branch July 29, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant