Tom
Tom

Reputation: 1223

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
            }
        }
    }

Upvotes: 2

Views: 1480

Answers (2)

mickadoo
mickadoo

Reputation: 3483

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
)

Upvotes: 4

romaricdrigon
romaricdrigon

Reputation: 1587

Unfortunately, this is impossible without rewriting a significant part of Doctrine DBAL. This would impact drivers (YAML, annotation...), meta data generator...

In your case, the simplest I see would be to add a custom type let's say LocalizableString (I guess at most you will need that and maybe LocalizableText). Adding a type is relatively straightforward, since you can extend a base type so you don't have to write any SQL. You can refer to Doctrine documentation here and Doctrine bundle one here.

Then you can just do:

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

if (isset($fields)) {

    foreach ($fields as $field => $fieldMapping) {
        if ($this->getClassMetadata()->getTypeOfField($field) === 'localized_string') {

            // Do sth with it
        }
    }
}

Upvotes: 0

Related Questions