I'm working on a Symfony 4 Web Project, and I have a database for every Client, so, in every request I have to connect to a database based on client id.
How to use doctrine to connect to a database manually ?
MyController:
/**
* #Route("/api/log", name="log", methods={"GET"})
*/
public function log(Request $request)
{
$this->denyAccessUnlessGranted(['ROLE_CLIENT','ROLE_ADMIN']);
$clientId = $request->query->get('client_id');
$dbName = 'project_'.$clientId;
//I have database credentials: $host,$port,$username,$password & $dbName:
$this->getDoctrine()->........
You can work with multiple connections and managers. Here the official symfony documentation.
So, if you could change manually config/packages/doctrine.yaml, you will solve your problem.
Another way, is to work with entity manager directly:
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
$paths = array('/path/to/entity/mapping/files');
$config = Setup::createAnnotationMetadataConfiguration($paths);
$dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
$entityManager = EntityManager::create($dbParams, $config);
Entity Manager API documenation
Related
I'm trying to use this Symfony bundle:
https://github.com/KnpLabs/KnpPaginatorBundle
In the docs, they use it a controller. So they have easy access to service container or the request object.
But as far as I understand, the Doctrine query should be in a repository, not a controller, right? And I already do have a function returning records. It's just that the pagination service doesn't expect "results" upon instantiating. It wants the query. So I can't return the "results" to the controller, but rather in middle of this function use a paginator.
On the other hand, stuff like playing with services or requests indeed belong to controllers.
So how this should be done? At first I thought about injecting the "knp_paginator" service and the request object into the repository. But I don't think this is the right way.
I'd say that the Request object should not go further down the stack than from the Controller.
Nothing prevents you from injecting the paginator directly into your custom repository, so why not doing that?
your.repository.service.definition:
class: Your\Repository\Class
# for symfony 2.3
factory_service: doctrine
factory_method: getRepository
# for symfony 2.8 and higher
factory: ["#doctrine.orm.entity_manager", getRepository]
arguments:
- YourBundle:YourEntity
calls:
- [setPaginator, ["#knp_paginator"]]
In the repository, you then should have the paginator available for use with the QueryBuilder:
public function setPaginator($paginator)
{
$this->paginator = $paginator;
}
...
$this->paginator->paginate($qb->getQuery(), $page, $limit);
In order to get your $page and $limit variables into the repository, you don't need the Request object. Simply pass them as a parameter to the repository call:
// In your controller
// You can use forms here if you want, but for brevity:
$criteria = $request->get('criteria');
$page = $request->get('page');
$limit = $request->get('limit');
$paginatedResults = $myCustomRepository->fetchPaginatedData($criteria, $page, $limit);
Passing the request object further down the Controller means that you have a leak in your abstractions. It's no concern of your application to know about the Request object. Actually, the request might well come from other sources such as the CLI command. You don't want to be creating a Request object from there because of a wrong level of abstraction.
Assuming that you have a Custom Repository Class, you can have a method in that Repository, which returns a Query or a valid instance of Query Builder and then you call that method from the controller and pass it to the paginate() method.
For example where $qb is returned by the custom repository (not return result but just the querybuilder of it)
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$qb->getQuery(),
$request->query->getInt($pageParameterName, 1),
$perPage,
array('pageParameterName' => $pageParameterName)
);
If I want to translate content in symfony, I will use the translator as it is described in the book:
$translated = $this->get('translator')->trans('Symfony2 is great');
But, if this translations are already existing in a database, how can I access this?
The db looks like
ID | locale | type | field | content
1 | en | message| staff.delete | delete this user?
I wil have to tell the translator where he can get the translation information. Cann you help me with a good tutorial or tipps an tricks?
According to docs you need to register a service in order to load translations from other source like from database
You can also store translations in a database, or any other storage by
providing a custom class implementing the LoaderInterface interface.
See the translation.loader tag for more information.Reference
What i have done,i have a translation bundle where my translation entity resides so i have registered a service in config.yml and passed doctrine manager #doctrine.orm.entity_manager in order to get data from entity
services:
translation.loader.db:
class: Namespace\TranslationBundle\Loader\DBLoader
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: translation.loader, alias: db}
In DBLoader class i have fetched translations from database and sets as mentioned in docs translation.loader
My Loader class
namespace YourNamespace\TranslationBundle\Loader;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
use Doctrine\ORM\EntityManager;
class DBLoader implements LoaderInterface{
private $transaltionRepository;
private $languageRepository;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager){
$this->transaltionRepository = $entityManager->getRepository("YourNamespaceTranslationBundle:LanguageTranslation");
$this->languageRepository = $entityManager->getRepository("YourNamespaceTranslationBundle:Language");
}
function load($resource, $locale, $domain = 'messages'){
//Load on the db for the specified local
$language = $this->languageRepository->findOneBy( array('locale' => $locale));
$translations = $this->transaltionRepository->getTranslations($language, $domain);
$catalogue = new MessageCatalogue($locale);
/**#var $translation YourNamespace\TranslationBundle\Entity\LanguageTranslation */
foreach($translations as $translation){
$catalogue->set($translation->getLanguageToken(), $translation->getTranslation(), $domain);
}
return $catalogue;
}
}
Note: Each time you create a new translation resource (or install a bundle
that includes a translation resource), be sure to clear your cache so
that Symfony can discover the new translation resources:
php app/console cache:clear
I am looking for a good solution to on-the-fly connection of databases within Symfony utilizing Doctrine for entity management.
The scenario I have is that all inbound users to our service will be visiting *.website.com addresses, like client1.website.com.
We would like to use one Doctrine entity for the Client table to then look up their database credentials based on the URL of their account on the fly.
So far I have found the following topics here on stackoverflow that discuss dynamically changing the database credentials--but no clear workable solutions.
I'd like to propose collaborating to put together a working solution, and I'll put together a blog/tutorial post for other folks looking to modify database connection parameters within Symfony.
Here are some related posts:
Dynamic database connection symfony2
Symfony2, Dynamic DB Connection/Early override of Doctrine Service
Thanks!
If $em is existing entity manager and you want to reuse it's configuration, you can use this:
$conn = array(
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'foo'
);
$new = \Doctrine\ORM\EntityManager::create(
$conn,
$em->getConfiguration(),
$em->getEventManager()
);
I needed to do something similar - runtime discovery of an available database server. I did it by overriding the doctrine.dbal.connection_factory.class parameter and substituting my own derivation of the Doctrine bundle's ConnectionFactory class
My services.yml provides the parameter, pointing at my custom class
parameters:
doctrine.dbal.connection_factory.class: Path\To\Class\CustomConnectionFactory
Then fill in your discovery logic in Path\To\Class\CustomConnectionFactory.php
<?php
namespace Path\To\Class;
use Doctrine\Bundle\DoctrineBundle\ConnectionFactory;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
class CustomConnectionFactory extends ConnectionFactory
{
public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array())
{
// Discover and override $params array here.
// A real-world example might obtain them from zookeeper,
// consul or etcd for example. You'll probably want to cache
// anything you obtain from such a service too.
$params['driver'] = 'pdo_mysql';
$params['host'] = '10.1.2.3';
$params['port'] = 3306;
$params['dbname'] = 'foo';
$params['user'] = 'myuser';
$params['password'] = 'mypass';
//continue with regular connection creation using new params
return parent::createConnection($params, $config, $eventManager,$mappingTypes);
}
}
Note also that Symfony 3.2 features the ability to use environment variables in container configurations, and to use their values on-demand (rather than fixing them when the container is compiled). See the blog announcement for more details.
I'm currently implementing Doctrine filters in my Symfony2.1 project with the following setup:
<?php
namespace Acme\Bundle\Entity;
class Article {
/**
* #ORM\Column(type="string")
*/
private $status;
...
}
//app/config/config.yml
doctrine:
orm:
filters:
status:
class: Acme\Bundle\Filter\StatusFilter
enabled: false
....
//src/Acme/Bundle/Filter/StatusFilter.php
namespace Acme\Bundle\Filter;
use Acme\Bundle\Entity\Status;
class StatusFilter extends SQLFilter {
public function addFilterConstraint(ClassMetadata $target, $alias)
{
$filter =
$target->reflClass->implementsInterface('Acme\Bundle\Entity\Status')?
$alias . '.status = ' . Status::PUBLISHED : '';
return $filter;
}
}
Where Acme\Bundle\Entity\Status is just an interface.
The code is working as expected when the filter is enabled in config.yml.
The problem is that I cannot retrieve all articles for administration!
Is there a way to enable this filter for a certain bundle?
p.s. I know how to enable and disable the filter with the EntityManager,
I just cannot find the proper place to do it for the frontend Bundle.
my admin section is accessible by route prefix myadmin
www.example.com/myadmin/ -> admin section = disable filter (disabled by default in config)
www.example.com/... -> anything else = enable filter.
Looking at the Doctrine code, there are methods to enable and disable filters.
Once you have defined your filter in the config.yml file, you can enable/disable in a controller or service:
// 'status' is the unique name of the filter in the config file
$this->getDoctrine()->getManager()->getFilters()->enable('status');
$this->getDoctrine()->getManager()->getFilters()->disable('status');
Note: this was taken from Symfony 2.3. You would need to test this with previous versions of Symfony/Doctrine.
there is no notion of bundle at Doctrine level. The only way I see would be to detect which controller is used, by parsing its className (reflection, ...) during a kernel.request event, or a kernel.controller event.
Then, if you detect that your controller is in FrontendBundle, just disable/enable your doctrine filter.
If you prefer using routing to detect when to disable/enable, just use kernel.request event. You will have access to all request parameters, via $event->getRequest()->attributes->get('_controller') for example.
In my config, I have doctrine.orm.auto_mapping set to true. So the Doctrine Bridge code is automatically looking in my bundle's Entity dir. Is there a gettable service that will return the list of registered entity classes?
Try:
$entityManager = $this->getDoctrine()->getEntityManager();
$metaData = $entityManager->getMetadataFactory()->getAllMetadata();
var_dump($metaData);