{"record":{"id":"dfcfc4a91e19c402","repo":"doctrine/orm","slug":"attempting-to-change-readonly-property-s-s","errorCode":null,"errorMessage":"Attempting to change readonly property %s::$%s.","messagePattern":"Attempting to change readonly property (.+?)::\\$(.+?)\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Mapping/PropertyAccessors/ReadonlyAccessor.php","lineNumber":43,"sourceCode":"            ));\n        }\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        /* For lazy properties, skip the isInitialized() check\n           because it would trigger the initialization of the whole object. */\n        if (\n            PHP_VERSION_ID >= 80400 && $this->reflectionProperty->isLazy($object)\n            || ! $this->reflectionProperty->isInitialized($object)\n        ) {\n            $this->parent->setValue($object, $value);\n\n            return;\n        }\n\n        if ($this->parent->getValue($object) !== $value) {\n            throw new LogicException(sprintf(\n                'Attempting to change readonly property %s::$%s.',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n    }\n\n    public function getValue(object $object): mixed\n    {\n        return $this->parent->getValue($object);\n    }\n\n    public function getUnderlyingReflector(): ReflectionProperty\n    {\n        return $this->reflectionProperty;\n    }\n}\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Mapping/PropertyAccessors/ReadonlyAccessor.php#L25-L61","documentation":"ReadonlyAccessor::setValue() permits the initial write to an uninitialized (or PHP 8.4 lazy) readonly property, and permits re-writing the identical value, but throws LogicException when asked to store a different value into an already-initialized readonly property. This keeps hydration from silently breaking readonly semantics.","triggerScenarios":"Hydrating or refreshing an already-loaded entity whose readonly property receives different data; custom code calling the property accessor's setValue() with a new value on an initialized readonly property; fixtures/importers that load an entity then rewrite its readonly fields in place.","commonSituations":"Entity refresh/partial queries returning changed data for readonly fields; data-fixture scripts mutating readonly promoted properties via reflection/accessors; double hydration of the same entity with conflicting values (e.g. different default values per query).","solutions":["Treat readonly properties as immutable: do not re-assign after initialization; replace the whole entity (remove + persist) when the value must change.","In custom hydration, skip the write when the property is already initialized, or only write when the new value is identical.","Remove 'readonly' from the property (and mapping) if the value legitimately changes during the entity lifecycle."],"exampleFix":"// before\n$accessor->setValue($entity, $newPrice); // initialized readonly prop + different value -> LogicException\n\n// after\nif (! $refl->isInitialized($entity)) {\n    $accessor->setValue($entity, $newPrice);\n} elseif ($accessor->getValue($entity) !== $newPrice) {\n    throw new DomainException('Cannot modify readonly property after initialization.');\n}","handlingStrategy":"try-catch","validationCode":"if (! $refl->isInitialized($entity) || $accessor->getValue($entity) === $value) {\n    $accessor->setValue($entity, $value);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $accessor->setValue($entity, $value);\n} catch (\\LogicException $e) {\n    // readonly property already initialized with a different value\n    throw new DomainException('Cannot update readonly field: ' . $e->getMessage(), 0, $e);\n}","preventionTips":["Model truly immutable values as readonly and never rewrite them during hydration.","Exclude readonly fields from partial refresh queries.","In fixtures, construct a fresh entity instead of mutating readonly fields."],"tags":["doctrine-orm","property-accessor","readonly","hydration"],"backgroundTag":"readonly-property-write","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}