. */ declare(strict_types=1); namespace App\Services; use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; /** * Workaround for using snake_case properties with ReflectionExtractor until this PR is merged: * https://github.com/symfony/symfony/pull/51697 */ #[AsTaggedItem('property_info.access_extractor', priority: 0)] class SnakeCasePropertyAccessExtractor implements PropertyAccessExtractorInterface { public function __construct(#[Autowire(service: 'property_info.reflection_extractor')] private readonly PropertyAccessExtractorInterface $reflectionExtractor) { //$this->reflectionExtractor = new ReflectionExtractor(); } public function isReadable(string $class, string $property, array $context = []): ?bool { //Null means skip this extractor return null; } /** * Camelizes a given string. */ private function camelize(string $string): string { return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); } public function isWritable(string $class, string $property, array $context = []): ?bool { //Check writeablity using a camelized property name $isWriteable = $this->reflectionExtractor->isWritable($class, $this->camelize($property), $context); //If we found a writeable property that way, return true if ($isWriteable === true) { return true; } //Otherwise skip this extractor return null; } }