Symfony2 "class not found in the chain" in two bundle relationship - php

In an attempt to define a one-to-many relationship across bundles the following occurs:
The class 'Mana\ClientBundle\Entity\Member' was not found in the chain
configured namespaces Mana\SplitBundle\Entity
Update 3:
I've now seen conflicting answers that the relationship can and cannot be accomplished. On the assumption that it can (because others here at stackoverflow seem to have done it), what configuration is required other than registering the bundles in AppKernel.php and entering the annotations in the entities? The resolve_target_entity_listener did not appear to make a difference.
Update 2:
Well, I know I'm way out of my depth here, but this is what I observed while stepping through the code when trying to show a Client entity.
The error message in the profiler
The target entity 'Mana\ClientBundle\Entity\Member' specified on
Mana\SplitBundle\Entity\Client#members is unknown or not an entity.
occurs because SchemaValidator evaluates $cmf->isTransient($assoc['targetEntity']) to true, where the targetEntity in the Member entity. The PHPdoc comment suggests that this entity's metadata is not loaded. If I understand this correctly, that means that the annotation regarding the relationship is not loaded. But observing variable values suggests that the annotations have been read.
Am I totally missing something that should be painfully obvious? Or am I too far out in left field?
Update 1:
I have confirmed that doctrine:mapping:info will detect improper FQCN. The data fixtures are correct. Use of entity managers and database-connection for both default and split connections are correct. The error persists and can occur for any of the relationships defined in the Client entity, either OneToMany or ManyToOne.
config.yml:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
mapping_types:
enum: string
split:
driver: "%database_driver2%"
host: "%database_host2%"
port: "%database_port2%"
dbname: "%database_name2%"
user: "%database_user2%"
password: "%database_password2%"
charset: UTF8
mapping_types:
enum: string
entity_managers:
default:
connection: default
mappings:
ManaClientBundle: ~
split:
connection: split
mappings:
ManaSplitBundle: ~
Client entity:
/**
* #ORM\OneToMany(targetEntity="Mana\ClientBundle\Entity\Member", mappedBy="client")
* #ORM\OrderBy({"dob" = "ASC"})
*/
protected $members;
Member entity:
/**
* #ORM\ManyToOne(targetEntity="Mana\SplitBundle\Entity\Client",inversedBy="members",cascade={"remove", "persist"})
* #ORM\JoinColumn(name="cid", referencedColumnName="id")
*
*/
protected $client;
Doctrine mapping:
$ php app/console doctrine:mapping:info
Found 12 mapped entities:
[OK] Mana\ClientBundle\Entity\Agency
[OK] Mana\ClientBundle\Entity\Center
[OK] Mana\ClientBundle\Entity\Contact
[OK] Mana\ClientBundle\Entity\Contactdesc
[OK] Mana\ClientBundle\Entity\Counties
[OK] Mana\ClientBundle\Entity\Ethnicity
[OK] Mana\ClientBundle\Entity\Incomehistory
[OK] Mana\ClientBundle\Entity\Incomesrc
[OK] Mana\ClientBundle\Entity\Member
[OK] Mana\ClientBundle\Entity\Note
[OK] Mana\ClientBundle\Entity\Referral
[OK] Mana\ClientBundle\Entity\User
$ php app/console doctrine:mapping:info --em=split
Found 1 mapped entities:
[OK] Mana\SplitBundle\Entity\Client

You should see Using Relationships with Multiple Entity Managers
A cross-bundle relationship cannot be managed by Doctrine if you have separate databases with separate connections and entity managers. Instead, in this case, the Client entity would have to reside in the same schema/bundle and be periodically refreshed from the external source.
But if you have only one connection to the database and one entity manager for it you can manage cross-bundle relationships. (Described here: OneToMany Relation on cross project entities (Symfony2/Doctrine))

Related

Symfony: accessing to another symfony REST API project

This is my context:
I have two symfony REST API projects, and I want to do a relation between both projects. Each project uses his own database, entities and controllers.
From project 1 (client-api), I need to get access to entities of project 2 (product-api).
I have tried to use DoctrineRestDriver
Firstly, I have configured the config.yml file of client-api project:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
memory: "%database_memory%"
path: "%database_path%"
charset: UTF8
product_api:
driver_class: "Circle\\DoctrineRestDriver\\Driver"
host: "http://localhost"
port: 8000
user:
password:
options:
authentication_class: "HttpAuthentication"
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AppBundle:
product_api:
connection: product_api
mappings:
AppBundle:
I want to read a country (id = 1) from the product-api project, so I have created this controller function on client-api project:
/**
* #Route("/countries", name="countries_other_api")
*/
public function getTheCountriesFromOtherApi()
{
$em = $this->getDoctrine()->getManager('product_api');
$country = $em->find("AppBundle\Entity\Country", 1);
}
But I'm getting the eror:
Class 'AppBundle\Entity\Pais' does not exist
Where is my problem ? How can I get access from symfony project 1 to symfony project 2 ?
Thanks.
So, you have 2 services. Each of service is responsible for its own entities. And you want service1 to have access directly to DB of service2...
My opinion is that you try to solve things in wrong way. If you want to get entities from service2 you should use REST API of service2 because you need to reimplement service2 logic in service1.
How to do it in better way? Create RestApi Client library for service2 and add it to service1 composer.json file. Dont forget to use authentication (hardcoded API keys or JWT...).
So, answer to your question 'where is problem?' I can say problem is in solution that you want to implement.

Symfony 2.8 : Doctrine getManagerForClass() not returning the right Entity Manager

tl;dr How does the getManagerForClass() method find out which entity manager is the right one for a specific class?
I've made a generic controller that should be able to handle basic actions for different entities.
I also have connections to two different databases, so I'm using two entity managers.
In my controller, I'm trying to use Doctrine's getManagerForClass() method to find which manager to use for each class, as explained on this blog and this SO answer.
But the method does not seem to differentiate my two entity managers and simply returns the first one in the configuration.
My controller action starts like this:
public function indexAction($namespace, $entityName)
{
$classFullName = "AppBundle:$namespace\\$entityName";
$em = $this->getDoctrine()->getManagerForClass($classFullName);
This is my Doctrine configuration:
dbal:
default_connection: postgres
connections:
postgres:
driver: pdo_pgsql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
oracle:
driver: oci8
host: "%oracle_host%"
port: "%oracle_port%"
dbname: "%oracle_name%"
user: "%oracle_user%"
password: "%oracle_password%"
charset: UTF8
orm:
auto_generate_proxy_classes: true
entity_managers:
postgres:
connection: postgres
mappings:
AppBundle:
type: annotation
dir: Entity\Postgres
oracle:
connection: oracle
mappings:
AppBundle:
type: annotation
dir: Entity\Oracle
And my folder structure is as follows:
AppBundle
|___Controller
| |___EntityController.php
|
|___Entity
|___Postgres
| |___SomePostgresBasedEntity.php
|
|___Oracle
|___SomeOracleBasedEntity.php
Now I don't know exactly how the method works, and how it it supposed to know about the mapping if not through the configuration.
But if I call it this way, for example:
$em = $this->getDoctrine()->getManagerForClass("AppBundle:Oracle\\SomeOracleBasedEntity");
...I get the entity manager for Postgres.
But if I simply switch the entity manager configuration, putting the one for oracle first, the previous call works, but the following doesn't:
$em = $this->getDoctrine()->getManagerForClass("AppBundle:Postgres\\SomePostgresBasedEntity");
Update 1
getManagerForClass() cycles through every manager and for each one, checks if the class is "non-transient":
foreach ($this->managers as $id) {
$manager = $this->getService($id);
if (!$manager->getMetadataFactory()->isTransient($class)) {
return $manager;
}
}
This goes all the way down to AnnotationDriver->isTransient(). Here the doc says the following:
A class is non-transient if it is annotated with an annotation from the AnnotationDriver::entityAnnotationClasses.
#Entity seems to be one of those annotations that makes a class non-transient.
But then, how could any of my entities be transient at all? How could the driver distinguish an entity that belongs to a specific manager based solely on its annotations?
I must have missed something in the higher level classes.
Update 2
The method works when using yml mappings.
I kind of expected this behaviour. The difference comes from the implementations of the isTransient() method in the different drivers. The FileDriver implementation of isTransient returns true if the metadata file exists in the dir: directory of the mapping configuration.
I would have expected the AnnotationDriver to search for annotations only in the entities contained in the specified dir: directory, but it seems to ignore that parameter.
Or should I use another one?
At long last, I solved it.
The solution was using the prefix parameter.
entity_managers:
postgres:
connection: postgres
mappings:
AppBundle:
type: annotation
dir: Entity\Postgres
prefix: AppBundle\Entity\Postgres
alias: Postgres
oracle:
connection: oracle
mappings:
AppBundle:
type: annotation
dir: Entity\Oracle
prefix: AppBundle\Entity\Oracle
alias: Oracle
Explanation
The prefix parameter gets passed to the corresponding Entity Manager service, and is added to the entityNamespaces property, which otherwise defaults to AppBundle/Entity.
The Annotation Driver will then check for annotations in that specific namespace, whereas the File Driver checks for existing mapping files in the directory specified through the dir parameter.
(The alias parameter is not mandatory.)
At least, that's how I understand it.

Multiple connections and entity managers in a symfony 2 / 3 application

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

Symfony2 Doctrine ORM Manager named "customer" does not exist

Hello I coopied the example for using two different database connections from the Symfony2 documentation: Symfony2 multiple connections documentation
So however Symfony does not find the the costumer entity manager.
(The parameters are defined properly)
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
customer:
driver: "%database_driver%"
host: "%database_host2%"
port: "%database_port2%"
dbname: "%database_name2%"
user: "%database_user2%"
password: "%database_password2%"
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AppBundle: ~
customer:
connection: customer
mappings:
AppBundle: ~
The Controller looks like this:
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
$em = $this->get('doctrine')->getManager();
$em = $this->get('doctrine')->getManager('default');
$em = $this->get('doctrine.orm.default_entity_manager');
// Both of these return the "customer" entity manager
$customerEm = $this->get('doctrine')->getManager('customer');
$customerEm = $this->get('doctrine.orm.customer_entity_manager');
return $this->render('default/index.html.twig', array(
'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'),
));
}
I should get the costumer entity manager, but however Symfony throws an invalid argument exception with the message.
[2015-10-19 23:19:18] request.CRITICAL: Uncaught PHP Exception
InvalidArgumentException: "Doctrine ORM Manager named "customer" does
not exist." at /srv/www/htdocs/symfony/my_project_name/app/cache
/prod/classes.php line 7344 {"exception":"[object]
(InvalidArgumentException(code: 0): Doctrine ORM Manager named
\"customer\" does not exist. at /srv/www/htdocs/symfony/my_project_name
/app/cache/prod/classes.php:7344)"} []
I cleaned the cache with php app/console cache:clear but this does not help.
Artamiel had the right answer in a comment:
Symfony2 Doctrine ORM Manager named "customer" does not exist
cache:clear clears the dev environment by default, but looking at your error log, you receive the error in your prod environment. To clear the cache on your production environment, add -e prod flag, like this cache:clear -e prod and refresh your page.
First, I think, one declaration of entity manager in your controller is enough:
/ *** /
public function indexAction(Request $request)
{
$em = $this->get('doctrine')->getManager('default');
$customerEm = $this->get('doctrine')->getManager('customer');
/ *** /
}
Second, are you sure that you can declare the same bundle in your mapping configuration (AppBundle: ~ in this case) ?

[Symfony][Doctrine]File mapping drivers must have a valid directory path

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.

Categories