Skip to content

fix(media): restore Media state on unserialize from Doctrine cache#88

Open
BriceFab wants to merge 3 commits intojolicode:mainfrom
BriceFab:fix/media-unserialize-slc-87
Open

fix(media): restore Media state on unserialize from Doctrine cache#88
BriceFab wants to merge 3 commits intojolicode:mainfrom
BriceFab:fix/media-unserialize-slc-87

Conversation

@BriceFab
Copy link
Copy Markdown

@BriceFab BriceFab commented Feb 12, 2026

Summary

  • add Media::__unserialize() so cached Media instances restore typed properties correctly
  • keep backward compatibility with legacy indexed payloads ([0 => path, 1 => library])
  • register Media::$resolverInitializer in bundle boot (same resolver used by Doctrine media types)
  • add regression tests for serialize/unserialize behavior

Problem

When an entity containing JoliCode\MediaBundle\Model\Media is read from Doctrine second-level cache, unserialization may create dynamic properties ($0, $1) instead of restoring constructor-backed typed properties.

This can leave $path uninitialized and later crash at runtime when getPath() is accessed.

Validation

  • added tests in tests/src/Model/MediaTest.php for:
    • serialize/unserialize roundtrip
    • legacy indexed payload support
  • syntax checks on modified files

Fixes #87

cc @xavierlacot

@BriceFab BriceFab changed the title Fix Media unserialization when loaded from Doctrine cache fix(media): restore Media state on unserialize from Doctrine cache Feb 12, 2026
@BriceFab BriceFab requested a review from loic425 March 24, 2026 18:04
@loic425
Copy link
Copy Markdown
Contributor

loic425 commented Apr 3, 2026

@BriceFab Thx a lot, could you run castor qa:cs command please?

Media entities cached by Doctrine (e.g. second level cache) are recreated via __wakeup/__unserialize without running the constructor. Restore path, storage, and related state from serialized payload and resolver.

- Implement __serialize/__unserialize with backward-compatible array shape
- Register __unserialize in JoliMediaBundle for Symfony 7.1+ native lazy ghost compatibility
- Add regression test for unserialization round-trip

Made-with: Cursor
@BriceFab BriceFab force-pushed the fix/media-unserialize-slc-87 branch from 925e45a to 0cc9424 Compare April 6, 2026 17:09
@BriceFab
Copy link
Copy Markdown
Author

BriceFab commented Apr 6, 2026

@loic425 done

$resolvedMedia = $this->getResolver()->resolveMedia($path, $libraryName);
} catch (MediaNotFoundException) {
$resolvedMedia = $this->getResolver()->createUnresolvedMedia($path, $libraryName);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xavierlacot Any opinion about this resolver embedded in the DTO? 🤔 I'm not sure we need to force those resolution when unserializing. Having the resolver attached to the DTO feels weird.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we store something with a state, there is no reason to retrieve the same object from cache with a different state. It should be the same state, the same object.

Introducing a resolver here leads to unexpected output from the cache.

For example, when you get your doctrine entity from the cache, there is no refresh with the original entity from the table, you get what you stored.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could serialize the storage into the __serialize method and then we will be able to deserialize the media without the resolver.

if (!isset($data['storage']) {
    throw Invalid...
}

$this->path = $data['path'];
$this->storage = $data['storage'];

return [$this->path, $this->storage->getLibrary()->getName()];
return [
'path' => $this->path,
'library' => $this->storage->getLibrary()->getName(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that'd be cool to serialize the storage to be able to deserialize the media without the resolver.

Suggested change
'library' => $this->storage->getLibrary()->getName(),
'storage' => $this->storage->__serialize(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum the serialize method on the storage seems not ok...

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.

Doctrine second-level cache unserializes Media with uninitialized typed properties

3 participants