Skip to content

[SonataImportBundle] Skip-cell marker to preserve existing values on partial imports#339

Merged
kiloumap merged 5 commits into
masterfrom
DEV-3653-skip-cell-marker
May 6, 2026
Merged

[SonataImportBundle] Skip-cell marker to preserve existing values on partial imports#339
kiloumap merged 5 commits into
masterfrom
DEV-3653-skip-cell-marker

Conversation

@kiloumap
Copy link
Copy Markdown
Collaborator

@kiloumap kiloumap commented May 1, 2026

What

The Sonata import (/admin/draw/sonataimport/import/create) writes every mapped CSV cell to the entity. When a content editor wants to update only one locale of a translatable field (or one of several scalar columns), they currently have to repeat every other locale/column — otherwise the importer overwrites the stored value with whatever is in the cell (often a placeholder like n/a or an empty string).

This PR adds a per-cell skip sentinel: when a cell equals the configured marker (default _SKIP_), assignment for that (row, column) pair is short-circuited and the existing value on the entity is preserved.

id,translation#en.title,translation#fr.title,translation#pl.title
42,Ocean kingdom,Royaume océanique,_SKIP_

For row id=42, the English and French titles are updated and the Polish title is left exactly as it is in the database.

Laurent Martinez added 4 commits May 1, 2026 11:08
…lues on partial imports

Add a configurable sentinel value (default "_SKIP_") that, when present in
a CSV cell, makes Importer::assignValue() short-circuit so the existing
value on the entity is preserved for that (row, column) pair. The check
runs before any type coercion (date, etc.) so the marker is honoured on
all column types — including dates, booleans and translation columns —
and applies uniformly to every ColumnExtractorInterface implementation
without each one having to reimplement it.

The marker is exposed as draw_sonata_import.skip_value (cannot be empty)
and surfaced in the Sonata Admin file-upload help text so content
editors can see how to opt out of overwriting a column.

Empty cells are not treated as skip — they still clear the field, which
preserves the prior "set to empty string" behaviour. The marker only
makes sense on update; on insertWhenNotFound=true the field simply stays
at its default value.

Refs DEV-3653 (downstream pnp-api ticket).
ImporterTest covers:
  - default and custom skip markers via getSkipValue() / isSkipValue()
  - assignValue() bypasses extractors and date coercion when the marker
    is present
  - assignValue() runs extractors normally for regular values
  - empty string is not treated as skip (regression)
  - non-exact matches (whitespace, case, padding, integer 0/-1) are
    rejected so the marker stays unambiguous

ConfigurationTest covers the new draw_sonata_import.skip_value node:
  default value, custom value, and the cannot-be-empty constraint.
Adds a 'Preserving existing values on partial imports' section that
explains the use case (partial-locale CSV updates), shows the YAML
configuration of skip_value, and clarifies that empty cells still
clear the field while the marker is the explicit "keep existing
value" signal.
- Rename data provider to match test method name convention
  (provideIsSkipValueRejectsNonExactMatchesCases) and place it directly
  after the test method.
- Return iterable instead of array from the data provider.
- Use static::createStub() rather than $this->createStub() to match the
  Draw/class_static_call rule.
@kiloumap kiloumap requested a review from DumitracheAdrian May 1, 2026 17:24
PHPStan level 5 cannot see properties declared on an anonymous class
when accessed through the BaseColumnExtractor return type. Promote the
call-tracking double to a named fixture (CallTrackingColumnExtractor)
with explicit $callCount and $lastValue properties, instantiated
directly in the tests so PHPStan resolves the concrete type.
@kiloumap kiloumap merged commit 7f71388 into master May 6, 2026
31 checks passed
@kiloumap kiloumap deleted the DEV-3653-skip-cell-marker branch May 6, 2026 17:50
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