Как предоставить классы моделей для нескольких реализациях Doctrine

При создании пакета, который может использоваться не только с Doctrine ORM, но также и с CouchDB ODM, MongoDB ODM или PHPCR ODM, вы должны всё равно прописывать только один класс модели. Пакеты Doctrine предоставляют пропуск компилятора для регистрации отображений для ваших классов моделей.

Note

Для пакетов, не имеющих повторного использования, наиболее лёгкой опцией будет разместить классы вашей модели в локациях по умолчанию: Entity для Doctrine ORM или Document для одной из ODM. Для пакетов с повторным использованием, вместо того, чтобы дублировать классы модели, просто используйте авто-отображение, используйте пропуск компилятора.

В вашем классе пакета, напишите следующий код, чтобы зарегистрировать пропуск компилятора. Этот код написан для пакета CmfRoutingBundle, так что некоторые его части нужно адаптировать для вашего случая:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass;
use Doctrine\Bundle\CouchDBBundle\DependencyInjection\Compiler\DoctrineCouchDBMappingsPass;
use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass;
use Symfony\Cmf\RoutingBundle\Model;

class CmfRoutingBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        // ...

        $modelDir = realpath(__DIR__.'/Resources/config/doctrine/model');
        $mappings = array(
            $modelDir => Model::class,
        );

        if (class_exists(DoctrineOrmMappingsPass::class)) {
            $container->addCompilerPass(
                DoctrineOrmMappingsPass::createXmlMappingDriver(
                    $mappings,
                    array('cmf_routing.model_manager_name'),
                    'cmf_routing.backend_type_orm',
                    array('CmfRoutingBundle' => Model::class)
            ));
        }

        if (class_exists(DoctrineMongoDBMappingsPass::class)) {
            $container->addCompilerPass(
                DoctrineMongoDBMappingsPass::createXmlMappingDriver(
                    $mappings,
                    array('cmf_routing.model_manager_name'),
                    'cmf_routing.backend_type_mongodb',
                    array('CmfRoutingBundle' => Model::class)
            ));
        }

        if (class_exists(DoctrineCouchDBMappingsPass::class)) {
            $container->addCompilerPass(
                DoctrineCouchDBMappingsPass::createXmlMappingDriver(
                    $mappings,
                    array('cmf_routing.model_manager_name'),
                    'cmf_routing.backend_type_couchdb',
                    array('CmfRoutingBundle' => Model::class)
            ));
        }

        if (class_exists(DoctrinePhpcrMappingsPass::class)) {
            $container->addCompilerPass(
                DoctrinePhpcrMappingsPass::createXmlMappingDriver(
                    $mappings,
                    array('cmf_routing.model_manager_name'),
                    'cmf_routing.backend_type_phpcr',
                    array('CmfRoutingBundle' => Model::class)
            ));
        }
    }
}

Заметьте отметку class_exists(). Это важно, так как вы не хотите, чтобы ваш пакет имел жёсткую зависимость от всех пакетов Doctrine, а хотите дать пользователю решить, какой пакет использовать.

Пропуск компилятора предоставляет заводские методы для всех драйверов, предоставленных Doctrine: Аннотаций, XML, Yaml, PHP и StaticPHP. Их аргументами являются:

  • Карта/хэширование абсолютного пути каталога к пространству имён;

  • Массив параметров контейнера, который использует ваш пакет для определения имени используемого менеджера Doctrine. В примере выше, CmfRoutingBundle сохраняет имя менеджера, котороеиспользуется под параметром cmf_routing.model_manager_name. Пропуск компилятора дополнит используемый Doctrine параметр, чтобы обозначить имя менеджера по умолчанию. Первый найденный параметр будет использован, и отображения будут зарегистрированы с этим менеджером;

  • Необязательное имя параметра контейнера, которое будет использовано пропуском компилятора, чтобы определить, используется ли этот тип Doctrine вообще. Это имеет значение, если ваш пользователь имеет больше одного типа установленных пакетов Doctrine, но ваш пакет используется только с одним типом Doctrine;

  • Карта/хэширование псевдоимён пространства имён. Они должны использовать те же договорённости, что и авто-отображение Doctrine. В примере выше, это позволяет пользователю вызвать

    $om->getRepository('CmfRoutingBundle:Route').

Note

Заводской метод использует SymfonyFileLocator в Doctrine, что означает, что он будет видеть файлы отображения XML и YML только, если они не имеют полного пространства имён в качестве имени файла. По задумке: SymfonyFileLocator упрощает всё, предполагая, что файлы - это просто "короткая" версия класса их имён файлов (например, BlogPost.orm.xml)

Если вам также нужно отобразить базовый класс, вы можете так же зарегистрировать пропуск компилятора с DefaultFileLocator. Этот код взят из DoctrineOrmMappingsPass и адаптирован к использованию DefaultFileLocator вместо SymfonyFileLocator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
use Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use AppBundle\Model;

// ...
private function buildMappingCompilerPass()
{
    $locator = new Definition(DefaultFileLocator::class, array(
        array(realpath(__DIR__ . '/Resources/config/doctrine-base')),
        '.orm.xml'
    ));
    $driver = new Definition(XmlDriver::class, array($locator));

    return new DoctrineOrmMappingsPass(
        $driver,
        array(Model::class),
        array('your_bundle.manager_name'),
        'your_bundle.orm_enabled'
    );
}

Отметьте, что вам не нужно предоставлять псевдоимя пространства имён, кроме случаев, когда ваши пользователи будут запрашивать у Doctrine базовые классы.

Теперь поместите ваш файл отображения в /Resources/config/doctrine-base с полностью квалифицированным именем класса, отделённым . вместо \, например, Other.Namespace.Model.Name.orm.xml. Вы не можете смешивать два вида, так как тогда SymfonyFileLocator запутается.

Таким же образом можно адаптировать и другие релизации Doctrine.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.