I have Entity(s) and EntityHtml(s) entities which has one-to-one relashionship (Entity stores metadata and EntityHtml acts like a cache, storing ready HTML chunks for rendering).
I have defined a relationship in Entity class:
/**
* #ORM\OneToOne(targetEntity="EntityHtml")
* #ORM\JoinColumn(name="entityId", referencedColumnName="entityId")
*/
private $entityHtml;
but it isn't working. Also I have a kind of feeling, that annotations don't work at all, because changing them has no effect upon workability of the application.
On the other hand, messing with .orm.xml(s) reflects in how application works.
Can I tell Symfony to update ORM XMLs based on changes to annotations?
Should I duplicate relation meta to XML?
Does Symfony use info at both XML and annotations or does it choose one source?
config.yml is default one:
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
Does it make any difference if you try this:
/**
* #ORM\OneToOne(targetEntity="EntityHtml")
* #ORM\JoinColumn(name="entity_Id", referencedColumnName="entityId")
*/
I'm thinking that "entityId" is the Id in EntityHtml, and you need to specify a different "name" value in JoinColumn. I think I ran into that problem.
Try it - I'm not sure if it will work.
Figured this one out. It's a configuration issue. In order to make annotations work (xml is default option), you have to explicitly configure it:
# Doctrine Configuration
doctrine:
...
orm:
...
mappings:
AppBundle:
type: annotation
Unfortunatelly that's not specified in tutorials.
Related
On doctrine/doctrine-migrations-bundle 2.* this was relatively simple - use the --em option (and use ContainerAwareInterface to skip any migrations from a different em/connection).
Now (on doctrine/doctrine-migrations-bundle 3.2.2), it seems the --em option is ignored, and the default em/connection is always specified, meaning the migrations for the default em are applied to every database. Edit: As pointed out in comments - --em is not ignored, it's passed through directly, it's rather our ContainerAwareInterface approach that's no longer valid.
There is a lot of conflicting information on how to set this up, some suggesting it should "just work" (Symfony Docs) and other describing workarounds (Issue):
https://symfony.com/doc/current/doctrine/multiple_entity_managers.html
https://github.com/doctrine/DoctrineMigrationsBundle/issues/38
How does one configure this new version (3) of doctrine/doctrine-migrations-bundle to apply migrations only to their matching entity/db?
Edit: I've included below our config previous to upgrading, which along with the ContainerAwareInterface connection filtering approach, allowed filtering migrations to run only against the appropriate entity manager.
Our existing "doctrine/doctrine-bundle": "1.12.8" config (shortened, but shows multiple entity managers):
doctrine:
dbal:
connections:
default:
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
driver: '%database_driver%'
server_version: mariadb-10.4.11
host: '%database_host%'
port: '%database_port%'
dbname: autotempest
user: '%database_user%'
password: '%database_password%'
mapping_types:
enum: string
model:
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
driver: '%database_driver%'
server_version: mariadb-10.4.11
host: '%database_host%'
port: '%database_port%'
dbname: autotempest_models
user: '%database_user%'
password: '%database_password%'
mapping_types:
enum: string
wrapper_class: App\Doctrine\ConnectionWrapper\ConnectionModel
persistent: true
orm:
auto_generate_proxy_classes: '%kernel.debug%'
entity_managers:
default:
connection: default
mappings:
App:
type: 'annotation'
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
model:
connection: model
mappings:
TempestModelBundle:
type: 'annotation'
dir: 'Entity'
prefix: 'Tempest\Bundle\ModelBundle\Entity'
Our "doctrine/doctrine-migrations-bundle": "2.1.2" config:
doctrine_migrations:
dir_name: '%kernel.project_dir%/src/Migrations'
namespace: Application\Migrations
Also mentioned in my question, there is an open issue on the DoctrineMigrationsBundle from 2012 describing the problem of dealing with migrations when using multiple entity managers: https://github.com/doctrine/DoctrineMigrationsBundle/issues/38. It seems there are several options for workarounds to this issue as described there, we just needed to dig and try each of them to find the best one for our situation.
Container Aware Migrations
On Symfony 3, we were using the ContainerAwareInterface approach. Described in the above issue:
Currently this can be achieved by using Container Aware Migrations. If one can have the service container injected, he can obtain an instance of some entity manager and its connection.
This is no longer really a valid solution when moving to Symfony 4 however, due to ContainerAware classes being deprecated in favor of dependency injection.
Pass configuration directly
Another approach mentioned in the github issue above. The idea here is to have a separate configuration file for each entity manager like the following:
# config/packages/migrations/base.yaml
em: default
transactional: false
migrations_paths:
Hyra\Migrations\Base: src/Migrations/Base
table_storage:
table_name: migration_versions
This is passed directly to the command, along with the entity manager like this: bin/console doctrine:migrations:migrate --em default --configuration config/packages/migrations/base.yaml. These separate config files replace the single config/packages/doctrine_migrations.yaml configuration file.
This was also not viable for us, as we still needed to inject services into our migrations using the services configuration option of DoctrineMigrationsBundle, and --configuration only passes configuration options directly through to doctrine/migrations, which doesn't support the services configuration option.
Initially on DoctrineMigrationsBundle 3.0, this approach was complicated by the fact that the --em and --conn options were dropped completely, so it was also necessary to create a wrapper on top of the DoctrineMigrationsBundle commands to re-implement these options (described in more detail here). This is no longer necessary on DoctrineMigrationsBundle 3.1+ (which restored these options).
Use DoctrineMigrationsMultipleDatabaseBundle
Also mentioned in the github issue thread, this bundle implements what we needed exactly (and ended up using) - per-entity configuration for DoctrineMigrationsBundle, so we can also include our services config for migration dependency injection. Initially I misconfigured this - it's important that the base doctrine_migrations.yaml config only includes config for the default entity manager. Sample working config provided by the package author (version 0.3.3):
# doctrine.yaml
doctrine:
dbal:
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
server_version: mariadb-10.1.26
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
mapping_types:
enum: string
geonames:
url: '%env(resolve:GEONAMES_DATABASE_URL)%'
server_version: mariadb-10.1.26
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
mapping_types:
enum: string
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '5.7'
orm:
auto_generate_proxy_classes: true
default_entity_manager: default
entity_managers:
default:
connection: default
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/App/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
geonames:
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
connection: geonames
mappings:
Geonames:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/App/Entity/Geonames'
prefix: 'App\Entity\Geonames'
alias: Geonames
# doctrine_migrations.yaml
doctrine_migrations:
em: default
migrations_paths:
DoctrineMigrations: '%kernel.project_dir%/migrations/Main'
# doctrine_migrations_multiple_database.yaml
doctrine_migrations_multiple_database:
entity_managers:
default:
migrations_paths:
DoctrineMigrations\Main: '%kernel.project_dir%/migrations/Main'
geonames:
migrations_paths:
DoctrineMigrations\Geonames: '%kernel.project_dir%/migrations/Geonames'
In my bundle i create a separate database connection and an EntityManager for it. Everything works fine, except those two things don't show up in the development profiler. There is only the default EntityManager and the default connection.
So basically i created 3 new service definitions for an Doctrine\Common\EventManager, an Doctrine\DBAL\Connection and an Doctrine\ORM\EntityManager. I've already tried to add these new service definition to the ContainerBuilder with the same naming convention which is used by the doctrine bridge, but they still won't show up in the profiler. The connection works fine, but i want debug it with and integrate it in the Symfony lifecycle.
The question is:
What is the best practice to create a separate database connection via Doctrine inside of a Symfony Bundle if the Symfony application is only configured to support one connection?
I believe you should take a look at this doc. They described there how to add another EntityManager, which mean another connection. First step is to create configuration.
Especialy take a look at doctrine.yaml configuration:
# config/packages/doctrine.yaml
doctrine:
doctrine:
dbal:
default_connection: default
connections:
default:
# configure these for your database server
url: '%env(DATABASE_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
customer:
# configure these for your database server
url: '%env(DATABASE_CUSTOMER_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
customer:
connection: customer
mappings:
Customer:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Customer'
prefix: 'App\Entity\Customer'
alias: Customer
Above are two entity managers, 'default' and 'customer'. There are also two cennections, one for each manager.
If configuration is valid you will have access to those managers by passing its names to 'getManager' method.
$entityManager = $this->getDoctrine()->getManager('default');
$customerEntityManager = $this->getDoctrine()->getManager('customer');
If you cant edit configuration:
What what about creating custom class (Manager or something) in which you will manually create connection. Take a look at this, it should help you.
getting-a-connection
My multi-tenant-app uses a master database, that holds information about tenants (like name, etc.) and a app-specific database per tenant.
I configured a master and some_tenant connection and entity manager in the doctrine section inside config.yml.
This gives me access to the master database from a controller (eg. for validating and getting tenant information for some_tenant based on the subdomain some_tenant.my-app.com). And it lets me use a tenant-specific database and entity manager during the application life-cycle.
The doctrine section in my config looks like this:
doctrine:
dbal:
default_connection: 'master'
connections:
master:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
some_tenant:
driver: pdo_mysql
host: "%database_host_some_tenant%"
port: "%database_port_some_tenant%"
dbname: "%database_name_some_tenant%"
user: "%database_user_some_tenant%"
password: "%database_password_some_tenant%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
master:
connection: master
mappings:
BEMultiTenancyBundle: ~
some_tenant:
connection: some_tenant
mappings:
AppBundle: ~
Here comes the part, which I am unhappy with and cannot find a solution:
First of all, tenants will be more than 20. And it starts to get messy, altering the doctrine config this way.
Then there is another config file, called tenants.yml which holds more information like enabled/disabled app-features, tenant-specific themes, etc.
The file is loaded, validated using the Config Component, and a container parameter is set, so that tenants configurations are available app-wide.
I would like to store the database credentials also in that file.
I want to create connections and entity managers based on that config. One per each tenant, which can be used during the app life-cycle.
I need to have them available also at the console command bin/console doctrine:schema:update --em=some_tenant for updating each tenant's database schem and bin/console doctrine:schema:update --em=master for updating the master database scheme.
By now I guess, the only way to achieve this is to add configuration parameters to the doctrine section programmatically after the AppBundle is loaded, and before the doctrine registry is constructed with the given managers and connections.
But I cannot even find a point, where I could achive this.
Is there another way to get to point 1 and 2?
Even though there is a similiar question, which I am not sure if it is exactly about the same problem and is about 3 years old, I wanted to post this question with a bit more explanation.
The solution in comments is a straight way easy solution and it will work. The other solution I've ever meet is to dynamically replace tenant database credentails using some external conditions, i.e request HOST.
You can decorate or extend the connection factory with a service in a way, that having the request stack available (or providing the domain with ENV or console argument for other SAPI's) you can have the same entity manager (even default!) being configured on demand.
on a brief look this would look like
use Doctrine\Bundle\DoctrineBundle\ConnectionFactory;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Symfony\Component\HttpFoundation\RequestStack;
class DynamicConnectionFactory extends Factory
{
/** #var RequestStack */
private $requestStack;
public function __construct(array $types, RequestStack $stack)
{
parent::__construct($types);
$this->requestStack = $stack;
}
public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array())
{
$host = $this->requestStack->getMasterRequest()->getHost();
$params = $this->replaceParamsForHost(array $params, $host);
return parent::createConnection($params, $config, $eventManager, $mappingTypes);
}
private function replaceParamsForHost(array $params, $host)
{
//do your magic, i.e parse config or call Memcache service or count the stars
return array_replace($params, ['database' => $host]);
}
}
I think that, To manage multi-tenant with symfony 2/3.
We can config auto_mapping: false for ORM of doctrine.
file: config.yml
doctrine:
dbal:
default_connection: master
connections:
master:
driver: pdo_mysql
host: '%master_database_host%'
port: '%master_database_port%'
dbname: '%master_database_name%'
user: '%master_database_user%'
password: '%master_database_password%'
charset: UTF8
tenant:
driver: pdo_mysql
host: '%tenant_database_host%'
port: '%tenant_database_port%'
dbname: '%tenant_database_name%'
user: '%tenant_database_user%'
password: '%tenant_database_password%'
charset: UTF8
orm:
default_entity_manager: master
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
master:
connection: master
auto_mapping: false
mappings:
AppBundle:
type: yml
dir: Resources/master/config/doctrine
tenant:
connection: tenant
auto_mapping: false
mappings:
AppBundle:
type: yml
dir: Resources/tenant/config/doctrine
After that, we cannot handle connection of each tenant by override connection info in request_listener like article: http://mohdhallal.github.io/blog/2014/09/12/handling-multiple-entity-managers-in-doctrine-the-smart-way/
I hope that, this practice can help someone working with multi-tenant
Regards,
Vuong Nguyen
I have a problems with Doctrine on Sf2.7, when I run a command such as "doctrine: schema: update" I get:
[Doctrine\Common\Persistence\Mapping\MappingException] File mapping drivers must have a valid directory path, however the given path 0 seems to be incorrect!
This worked well until I make a drop of a database to recreate it in "clean" after a lot changed entities ...
Here is an excerpt from my config.yml
# Doctrine Configuration
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
dbname: "%database_name%"
host: "%database_host%"
port: "%database_port%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
auto_mapping: true
all entities begin with this type of annotations:
/**
* User
*
* #ORM\Entity
* #ORM\Table(name="usr_user")
* #ORM\Entity(repositoryClass="Acme\UserBundle\Repository\UserRepository")
*/
I searched our friend Google, OpenClassRooms and here of course but not found ... someone an idea ?
Got the same error when I imported my project to another pc. The solution worked for me is to try clearing configuration cache using php artisan command.
php artisan config:cache
then try to create or update doctrine schema.
For Symfony, you need to clear var/cache directory.
It solved problem for me.
NOTE: Its not a Duplicate issue cause I have tried everything I get on google but nothing have helped us.
I am trying to import Tables using Doctrine Reverse Engineering tool, but I m getting this message:
Database does not have any mapping information.
My Connection Details in Config.yml
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: localhost
port: null
dbname: pixel_ashish
user: root
password: abc123
charset: UTF8
schema_filter: ~^(?!some_table1|some_table2)~
orm:
default_entity_manager: default
auto_generate_proxy_classes: true
proxy_dir: "%kernel.cache_dir%/doctrine/orm/Proxies"
proxy_namespace: Proxies
resolve_target_entities: []
What I have tried So Far:
Running php app/console doctrine:mapping:import --force AcmeBlogBundle xml
gives same error
Tried to convert mapping also which does not make any sense cause mappings are not there but still tried didn't worked out.
Created a new project and tried above given configuration didn't worked.
Now I am out of ideas please help me to solve this.
You need to create some mapping information in your config.
You can do it by passing auto_mapping: true under orm section:
doctrine:
orm:
auto_mapping: true
or by manually defining it under orm section:
doctrine:
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
YourBundleName:
type: "xml"
dir: "Entity"
prefix: "Your\BundleName\Entity"