1
votes

I've followed the How to Work with multiple Entity Managers and Connections chapter in the manual to configure and use different entity managers in my app, as some entities are stored in different database servers.

In the controller I need to use the 2 entity managers: the default for clients, numbers and payments, and the other for contracts.

However, when I call the repository custom method fetchMainByClient() for contracts, which should use the "custom" entity manager instead of the default one, I get a database error, which shows that's using the default entity manager in the repository.

An exception occurred while executing 'SELECT ... FROM CONTRACTS c0_ WHERE c0_.ClientId = ? AND c0_.Type = 'Main'' with params [35736]:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'DEFAULTDB.CONTRACTS' doesn't exist

The same error occurs if I try to use an already available repository method, like findOneBy().

What am I doing wrong?

Here's the Controller's, the Repository's code and the Contract entity header, as well as the doctrine configuration. It's not the real one, but it's similar.

Controller

public function index(Request $request, PaymentsRepository $payments_repository): Response
{
    $payments = $payments_repository->findLatest($page, $this->getUser()->getId());

    $form = $this->createForm(CreatePaymentType::class);

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $payment = $form->getData();

        $em = $this->get('doctrine.orm.default_entity_manager');
        $cr = $em->getRepository(Numbers::class);
        $number = $tr->findOneByNumber($payment->getNumber());

        if (!$number) {
            $this->addFlash('error', 'numbers.missing_number');
        } else {
            $client = $number->getClients();
            if (!$client) {
                $this->addFlash('error', 'clients.missing_client');
            } else {
                //$em_custom = $this->get('doctrine.orm.custom_entity_manager');
                $em_custom = $this->getDoctrine()->getManager('custom');
                $contracts_repo = $em_custom->getRepository(Contracts::class);
                //dump($em, $em_custom, $contracts_repo);

                $contract = $contracts_repo->fetchMainByClient($client->getId());
                $contracts_repo->increaseCredit($payment->getValue());

                $payment->setDate(...);
                $payment->setClientId($client->getId());
                ...
                $em->persist($payment);
                $em->flush();

                $this->addFlash('success', 'flash.success_insert');

                return $this->redirectToRoute('payments_paginated', ['page' => $page]);
            }
        }
    }

    return $this->render('carregamentos/index.html.twig', [
        'payments' => $payments,
        'form' => $form->createView(),
    ]);
}

Repository

namespace App\Repository;

use App\Entity\Custom\Contratos;

...
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class ContractsRepository extends ServiceEntityRepository

    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Contracts::class);
    }

    /**
     * @return Contracts Returns the Contracts object for the ClientId
     */
    public function fetchMainByClient($value): ?Contracts
    {
        return $this->createQueryBuilder('c')
            ->andWhere('c.clientid = :val')
            ->andWhere('c.type = \'Main\'')
            ->setParameter('val', $value)
            ->getQuery()
            ->getOneOrNullResult()
        ;
    }
}

Doctrine configuration

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver: 'pdo_mysql'
                server_version: '5.6'
                charset: utf8
                default_table_options:
                    charset: utf8
                    collate: utf8_unicode_ci

                url: '%env(resolve:DATABASE_URL)%'

            custom:
                driver: 'pdo_mysql'
                server_version: '5.6'
                charset: utf8
                default_table_options:
                    charset: utf8
                    collate: utf8_unicode_ci

                url: '%env(resolve:DATABASE_CUSTOM_URL)%'
                mapping_types:
                    enum: string
                    set:  string

    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                naming_strategy: doctrine.orm.naming_strategy.underscore
                mappings:
                    Main:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity'
                        prefix: 'App\Entity'
                        alias: Main
            custom:
                connection: custom
                naming_strategy: doctrine.orm.naming_strategy.underscore
                mappings:
                    Custom:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/Custom'
                        prefix: 'App\Entity\Custom'
                        alias: Custom

Entity header with ORM mapping

namespace App\Entity\Custom;

use Doctrine\ORM\Mapping as ORM;

/**
 * Contracts.
 *
 * @ORM\Table(name="CONTRACTS", indexes={@ORM\Index(name="clientid", columns={"clientid"})})
 * @ORM\Entity(repositoryClass="App\Repository\ContractsRepository")
 */
class Contracts
{
    ...
}
1
I'm confused, you say the contract class is in the custom entity manager, but it has the namespace of the default one?Liora Haydont
The default namespace is App\Entity, and the Contract class is in the App\Entity\Ims namespace. Why do you say so, @LioraHaydont?Nuno Pereira
I might be wrong, I have never worked with multiple entities manager, but in your custom em you set prefix: 'App\Entity\Custom', so my guess would have been that the entity namespace would be App\Entity\Custom\ImsLiora Haydont
That's an error when passing the original code to this example. It's updated and fixed. For The namespace is App\Entity\Custom, as well as the namespace. Ims refers to the original one.Nuno Pereira
I guess you checked manually the database and the tables are in the right databases?Liora Haydont

1 Answers

1
votes

The problem is that your default connection will pick up all entities in App\Entity as well as any sub-directories such as App\Entity\Custom. So your custom entities are being mapped to both entity managers.

The service repository constructor just looks through each manager to see if it supports a given entity. First one found get's the repository.

Move App\Entity\Custom to something like App\EntityCustom and adjust the config.