Search code examples
phpsymfonyormdoctrine-ormyaml

How to add custom property to Symfony Doctrine YAML mapping file


Can anyone tell me how to add custom property to doctrine ORM yml file?

My idea is to add a property like this:

fields:
    name:
        type: string
        localizable: true

Then I would like to get information about this localizable property by using

protected function getEntityMetadata($entity)
{
    $factory = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine'));

    return $factory->getClassMetadata($entity)->getMetadata();
}

and then:

    $met    = $this->getEntityMetadata($bundle.'\\Entity\\'.$entity);
    $this->metadata = $met[0];
    $fields = $this->metadata->fieldMappings;

    if (isset($fields)) {

        foreach ($fields as $field => $fieldMapping) {
            if (isset($fieldMapping['localizable']) && $fieldMapping['localizable'] == true) {

                // Do sth with it
            }
        }
    }

Solution

  • The way doctrine is written makes this awkward. It seems like you'd like to keep the Yaml mapping but just add a single property. I think you can create your own custom driver extending from the one provided. The Yaml driver has mostly private methods so overriding a little bit of the functionality is difficult, but it is possible.

    I created a custom driver that extends from the SimplifiedYamlDriver. The naming of the driver is important because doctrine extension will try to load one of their drivers based what comes before Driver. It also does a strpos check for Simplified in the name, so I think the safest bet is to keep the original name completely and give the original an alias.

    use Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver as BaseDriver;
    
    class SimplifiedYamlDriver extends BaseDriver
    {
        public function loadMetadataForClass($className, ClassMetadata $metadata)
        {
            parent::loadMetadataForClass($className, $metadata);
    
            $element = $this->getElement($className);
    
            if (!isset($element['fields'])) {
                return;
            }
    
            foreach ($element['fields'] as $name => $fieldMapping) {
                if (isset($fieldMapping['localizable'])) {
                    $original = $metadata->getFieldMapping($name);
                    $additional = ['localizable' => $fieldMapping['localizable']];
                    $newMapping = array_merge($original, $additional);
                    $metadata->fieldMappings[$newMapping['fieldName']] = $newMapping;
                }
            }
        }
    }
    

    Then I told Symfony to use this driver by overriding the class inside app/config/parameters.yml

    parameters:
        doctrine.orm.metadata.yml.class: MyBundle\SimplifiedYamlDriver
    

    Then I updated the mapping like in your example inside MyBundle/Resources/config/doctrine/Foo.orm.yml

    MyBundle\Entity\Foo:
        type: entity
        id:
            id:
                type: integer
                generator:
                    strategy: IDENTITY
        fields:
            text:
                type: string
                localizable: true
    

    And I can fetch this mapping wherever I have access to doctrine with:

    $mapping = $this
                ->getDoctrine()
                ->getEntityManager()
                ->getClassMetadata(Foo::class)
                ->getFieldMapping('text');
    

    Will give me:

    Array
    (
        [fieldName] => text
        [type] => string
        [columnName] => text
        [localizable] => 1
    )