Change service parameter value dynamically for FOSUserBundle - symfony

I asked about using multiple entity manager for FOSUserBundle before, and it turns out that FOSUserBundle has (partially) supported that. All I need to do is to specify the connection / manager I want to use in the model_manager_name parameter, as explained here
fos_user:
# ........
model_manager_name: account1
Sample app/config/config.yml
With this FOSUserBundle will use account1 connection, and use user information in that connection's database.
doctrine:
dbal:
default_connection: default
connections:
account2:
dbname: account2
user: account2
password: password2
driver: pdo_mysql
host: localhost
port: ~
charset: UTF8
account1:
dbname: account1
user: account1
password: password1
driver: pdo_mysql
host: localhost
port: ~
charset: UTF8
default:
dbname: account
user: account
password: password
driver: pdo_mysql
host: localhost
port: ~
charset: UTF8
My app require that when a user goes to (for example) http://myapp.com/a/account1, the app will use account1 connection, and going to http://myapp.com/a/account2 will use account2's connection. For my application's logic, this is easily done from my controllers as I could use something like the following;
$em = $this->get('doctrine')->getManager('account2');
$repository = $this->get('doctrine')->getRepository($class, 'account2')
For the login part though, it's not that easy. FOSUserBundle runs as a service container, and I don't know where/how to dynamically change the model_manager_name's value. I do know though that in FOS\UserBundle\DependencyInjection\FOSUserExtension I can manually change its value the following way;
class FOSUserExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->processConfiguration($configuration, $configs);
$config['model_manager_name'] = 'account2';
// .................
Any thoughts?

The configuration is stored inside the container, in the fos_user.model_manager_name to be exact.
You can write a compiler pass. This will be executed just before freezing the container, it is the last place where you can change the container and it is the place to change the container based on other services.
Your compiler pass will look like this:
// src/Acme/DemoBundle/DependencyInjection/Compiler/ChangeModelManagerPass.php
namespace Acme\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ChangeModelManagerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$request = $container->get('request');
$uri = $request->getUri();
// check if the uri matches some pattern which will cause a change in the
// `model_manager_name` setting
if (...) {
// ... do some stuff to get the correct model manager name
// set the setting
$container->setParameter('fos_user.model_manager_name', ...);
}
}
}
Read more about compiler passes in the docs or in this great blog post by Richard Miller.

Related

symfony 4.2 load doctrine service in connection wrapper class

iam new to symfony and created some project based on 4.2 as i have to dynamically switch the db and there seems to be a bug in this version, i followed some instruction to setup a wrapper class for connecting to the db, this works fine. But iam struggeling with getting the doctrince method getConnections() here to work, to be able to double check the sended parameter against my db config. Everything i tried by initializing the doctrine as service container etc. doesn't work.
Hope someone could give me a hint, also my first post, so please no hate (:
Greetings
doctrine:
dbal:
default_connection: db_name
connections:
my_connection:
wrapper_class: App\Doctrine\DynamicConnection
url: '%env(DATABASE_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
services:
# default configuration for services in *this* file
_defaults:
public: false
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
App\Doctrine\:
resource: '../src/Doctrine'
tags: ['doctrine.service_arguments']
doctrine.entity_manager:
public: true
class: App\Doctrine\DynamicConnection
arguments: [ '#doctrine.orm.entity_manager' ]
namespace App\Doctrine;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class DynamicConnection extends Connection
{
public function __construct(array $params = array(), Driver $driver = null, $config = null, $eventManager = null)
{
#$request = Request::createFromGlobals();
#$dbname = $request->query->get('db_name');
$containerBuilder = new ContainerBuilder();
var_dump($containerBuilder->getServiceIds());
$doctrine = $containerBuilder->get('doctrine.entity_manager');

Service "fos_elastica.finder.app.user" not found

Symfony can't find service for fos_elastica
Service "fos_elastica.finder.app.user" not found: even though it exists in the app's container, the container inside "App\Controller\DevController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "session" and "twig" services. Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "DevController::getSubscribedServices()".
my config
# Read the documentation: https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
# indexes:
# app: ~
indexes:
app:
client: default
types:
user:
properties:
username: ~
# mappings:
# email: ~
persistence:
# the driver can be orm, mongodb, phpcr or propel
# listener and finder are not supported by
# propel and should be removed
driver: orm
model: App\Entity\User
provider: ~
listener: ~
finder: ~
my controller:
/**
* #Route("/search")
*/
public function searchElastic(){
/** var array of App\Entity\User */
$finder = $this->container->get('fos_elastica.finder.app.user');
return new response('N/A');
}
command php bin/console fos:elastica:populate didn't throw any errors and in phpstorm it isn't highlighted (it mean phpstorm found it)
Please help me.
You should use Dependency Injection like so:
Add use statement in the controller class use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;, then your action should look like this:
/**
* #Route("/search")
*/
public function searchElastic(RepositoryManagerInterface $finder) {
$someResult = $finder->getRepository(User::class)->find(...);
return new response('N/A');
}

In memory sqlite always empty despite setup

I followed sitepoints Testing Symfony Apps with a Disposable Database Tutorial.
I added Fixtures in my Testcase and no Errors appear during SetUp. If i add an Error in the Fixtures (e.g. leaving a nullable=false field empty) the Error is shown, so this code does definitely get executed.
My Config:
doctrine:
dbal:
default_connection: memory
connections:
memory:
driver: pdo_sqlite
memory: true
charset: UTF8
My SetUp in my WebTestCase:
protected function setUp() {
parent::setUp();
self::bootKernel();
DatabasePrimer::prime(self::$kernel);
$this->loadFixtures([
'AppBundle\DataFixtures\ORM\UserData',
'AppBundle\DataFixtures\ORM\ArtistData'
]);
}
Yet, in my WebTestCase it appears that no Tables exist.
The output throws a Doctrine Exception saying my table does not exist.
SQLSTATE[HY000]: General error: 1 no such table: my_user_table
If i switch to sql_lite in a file, everything works fine without any other changes:
dbal:
default_connection: file
connections:
file:
driver: pdo_sqlite
path: %kernel.cache_dir%/test.db
charset: UTF8
Anyone had success with said tutorial or using a sqlite memory db for unit tests and has any hints or ideas?
Update:
I changed my Setup to this to ensure the kernel is not shut down in between. It did not help:
parent::setUp();
$this->client = $this->getClient();
MemoryDbPrimer::prime(self::$kernel);
$this->loadFixtures([
'AppBundle\DataFixtures\ORM\UserData',
'AppBundle\DataFixtures\ORM\ArtistData'
]);
When you
$client->request(<METHOD>, <URL>);
which calls
Symfony\Bundle\FrameworkBundleClient::doRequest($request)
After the request the kernel is shutdown by default, and your in-memory database is trashed.
If you call
client->disableReboot();
in the setup() function of your test, this will behavior is disabled, and you can run the whole suite.
I assume you call createClient() in your test functions. The very first thing that createClient() does is call static::bootKernel(). This basically means that the kernel you booted in your setUp() gets shut down and a new kernel is booted, with a fresh instance of the memory SQLite database.
You can move the createClient() call into your setUp(), replacing the bootKernel(), to avoid this:
class MyTest extends WebTestCase
{
private $client = null;
public function setUp()
{
$this->client = static::createClient();
// prime database
}
public function testSomething()
{
$crawler = $this->client->request('GET', '/');
// ...
}
}

Example on how to config google cloud storage with KnpGaufetteBundle

I am trying to configure a KnpGaufretteBundle to use Google Cloud Storage for storing my files. This is the config:
## definition of the GCS service
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory_class: Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory
factory_method: 'create'
arguments:
- "123#developer.gserviceaccount.com"
- "http://localhost/file.p12"
- "pwd"
## config of knp_gaufrette
knp_gaufrette:
stream_wrapper: ~
adapters:
gcs_minn_images:
google_cloud_storage:
service_id: 'app.google_cloud_storage.service'
bucket_name: 'minn-images'
filesystems:
gcs_minn_images_fs:
adapter: gcs_minn_images
The error message I got is:
ContextErrorException in GoogleCloudStorageAdapterFactory.php line 16:
Catchable Fatal Error: Argument 1 passed to Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create() must be an instance of Symfony\Component\DependencyInjection\ContainerBuilder, string given, called in /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724 and defined
According to the error message, I gave a string of stead of ContainerBuilder. Great! Let's add the ContainerBuilder to the arguments as follows:
## definition of the GCS service
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory_class: Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory
factory_method: 'create'
arguments:
- #service_container
- "123#developer.gserviceaccount.com"
- "http://localhost/file.p12"
- "pwd"
The result is again an error:
Catchable Fatal Error: Argument 1 passed to Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create() must be an instance of Symfony\Component\DependencyInjection\ContainerBuilder, instance of appDevDebugProjectContainer given, called in /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724 and defined
So now, the error is telling me that I provide an instance of appDevDebugProjectContainer in stead of ContainerBuilder!!
Ok, let's have a look to /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724...
class appDevDebugProjectContainer extends Container{
// ...
/**
* Gets the 'app.google_cloud_storage.service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* #return \Google_Service_Storage A Google_Service_Storage instance.
*/
protected function getApp_GoogleCloudStorage_ServiceService()
{
return $this->services['app.google_cloud_storage.service'] =\Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create($this, '123#developer.gserviceaccount.com', 'http://localhost/file.p12', 'pwd');
}
I am really lost...
So, is there any complete example to config google cloud storage?
I finally found the solution. You have to create your own factory class as described in the documentation of the bundle:
Factory class
<?php
namespace Minn\AdsBundle\Factory;
/**
* Description of GoogleCloudStorageServiceFactory
*/
class GoogleCloudStorageServiceFactory {
public function createService() {
// creating the google client
$client = new \Google_Client();
// setting the service acount credentials
$serviceAccountName = '123#developer.gserviceaccount.com';
$scopes = array(
'https://www.googleapis.com/auth/devstorage.read_write',
);
$privateKey = file_get_contents('http://localhost/f.p12');
$privateKeyPassword = 'pwd';
$credential = new \Google_Auth_AssertionCredentials(
$serviceAccountName, $scopes, $privateKey, $privateKeyPassword);
// set assertion credentials
$client->setAssertionCredentials($credential);
// creating and returning the service
return new \Google_Service_Storage($client);
}
}
The config.yml file
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory: [Minn\AdsBundle\Factory\GoogleCloudStorageServiceFactory, createService]
knp_gaufrette:
stream_wrapper: ~
adapters:
gcs_images:
google_cloud_storage:
service_id: 'app.google_cloud_storage.service'
bucket_name: 'images'
filesystems:
gcs_images_fs:
adapter: gcs_images
vich_uploader:
db_driver: orm
storage: gaufrette
mappings:
motors_files:
upload_destination: gcs_images_fs
namer: vich_uploader.namer_origname
delete_on_remove: true
That's was it...
Hope it will help others...

FosElasticaBundle: define mapping for GUID / UUID fields

How do I define the mappings in config_yml if my entities uses GUID field type as identifier?
Details
I have a project with Symfony 2.7.3 and FosElasticaBundle 3.1.4.
I have configured the mapping and everything works fine when I use the populate command or if I edit an existing entity; but when creating a new entity I get an exception
"expected a simple value for field [_id] but found [START_OBJECT]]"
Checking the JSON response I have noticed that _id is set to an empty object ..."_id":{}....
The Uuid class already has a __toString method which I expect should do the trick, but I am probably missing something.
Current mappings
fos_elastica:
clients:
default: { host: %elasticsearch_host%, port: %elasticsearch_port%, logger: true }
indexes:
app:
types:
user:
mappings:
name: ~
surname: ~
persistence:
driver: orm
model: AppBundle\Entity\User
provider: ~
listener: ~
finder: ~
Thank you.
There are no specific mappings for UUID/GUID fields.
The identifier is retrieved using the standard Symfony property accessor, so the solution to my problem was to change the getId() method in my entity, and make it cast to string be
public function getId()
{
return (string)$this->id;
}

Resources