Can I use MongoDB to store sessions in Symfony 2? - php

Is it possible to use MongoDB to store sessions in Symfony 2? If so, how?

Just because I think the question is not really answered. In Symfony 2.1 is now possible to store session data in MongoDB with the MongoDbSessionHandler which is included in the HttpFoundation component.
Main configuration of config.yml looks like:
session.handler.mongo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
arguments: [#mongo, %mongo.session.options%]
mongo.connection:
class: MongoDoctrine\MongoDB\Connection
factory_service: doctrine.odm.mongodb.document_manager
factory_method: getConnection
calls:
- [initialize, []]
mongo:
class: Mongo
factory_service: mongo.connection
factory_method: getMongo
mongo.session.options:
database: app_session
collection: session
framework:
session:
handler_id: session.handler.mongo
Read more here: http://blog.servergrove.com/2012/11/05/storing-sessions-in-mongodb-with-symfony2/

I've done this with Mandango, but it should be easily convertable to use eg Doctrine's MongoDB ODM or similar. I made a start, but it hasn't been tested and I'm fairly sure it should be passed something different to a Mongo instance ;-) I've added placeholders or example code where appropriate eg:
public function __construct(\Mongo $con, ...)
which I'm fairly sure will need to change :-)
Code is up on Github at https://github.com/richsage/Symfony2-MongoDB-session-storage - PRs welcome when you get it working!
Essentially, I've extended the NativeSessionStorage class, and adjusted the various methods to handle inserting, retrieving and updating session records in my Mongo database where appropriate. The class needs to be configured as a service, with the appropriate dependencies added in, and then this service is passed to the session configuration. Et voila :-)

Related

Reading external file to configure doctrine in Symfony 5

I'm looking for a way to have doctrine (in a symfony 5 application) connect with the database details stored in an external XML file (instead of .env).
However I can't seem to be finding a solution.
Reading the XML file isn't the problem, I just can't find how to pass the parameters on the doctrine.
I've tried to use a doctrine.php file but fails to connect.
I've also tried to use a wrapper class and called the parent construct method but it seems it isn't recommended.
Has anybody every managed to achieve something of the sort ?
Thanks !
Symfony lets you add configuration written in php. See https://symfony.com/doc/current/configuration.html#configuration-formats
That allows to do the following:
Import an additional configuration file in services.yaml
imports:
- { resource: 'my-db-config.php' }
my-db-config.php:
<?php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
function readConfig() {
$myConfig = '' // read from your xml file
return $myConfig
}
return static function (ContainerConfigurator $container) {
$container
->parameters()
->set('my.param', readConfig());
};
in use your new param in doctrine.yaml
doctrine:
dbal:
connections:
default:
url: '%my.param%'

Why/how does this work: Symfony 2.8 + FOSRestBundle automatically deserializes request object

I am working on migrating an existing Symfony 2.8 which I did not create. The previous developers are not available for questions.
Most of the code easy to understand, but at one point I simply cannot figure out how are why this works:
A controller/action parameter is automatically de-serialized from JSON data to a custom object. This is nothing special, I do not understand how/where the Symfony is told what to do. Form my point of view important config data is missing but it works anyway.
That's would be fine, but without understanding why it works in this case, I cannot figure out why it does not work anymore when migrating the project to Symfony 3.4...
Sorry for the (very) long question, but I tried different ways to solve the problem and answer the questions but they all resulted in different problems...
Using the following Bundles
sensio/framework-extra-bundle v3.0.29
friendsofsymfony/rest-bundle 2.4.0
jms/serializer 1.13.0
jms/serializer-bundle 1.5.0
Config:
// app/config/config.yml
sensio_framework_extra:
request: { converters: true }
fos_rest:
...
body_converter:
enabled: true
#jms_serializer: (not configured)
# ...
Code:
// src/AppBundle/Util/SortInfo.php
class SortInfo {
public $sortOrder;
public $sortBy;
}
// src/AppBundle/Resources/serializer/Util.SortInfo.yml
AppBundle\Util\SortInfo:
exclusion_policy: ALL
properties:
sortOrder:
type: string
expose: true
sortBy:
type: string
expose: true
// src/AppBundle/Controller/SortingController.php
class SortingController extends Controller {
...
/**
* #FOSRest\View()
*/
public function sortAction(SortInfo $sortInfo) {
$this->logger->info("sortAction");
$this->logger->info(" ".gettype($sortInfo)." -- ".get_class($sortInfo));
$this->logger->info(" ".$sortInfo->sortOrder." -- ".$sortInfo->sortBy);
...
}
}
That's it. After digging for some hours I could not find any explicit configuration that would tell the sortAction method to convert the JSON data from the request automatically. But it works.
A request with the following JSON content results in the following log output:
// JSON content
{"sortOrder":"ASC", "sortBy":"name"}
// Log output
sortAction
object -- AppBundle\Util\SortInfo
ASC -- name
Why does this work?
As said before this is fine, but the problem is, that the same code does not work with Symfony 3.4 and newer versions of FOSRest, ExtrasBundle and JMSSerializer:
sensio/framework-extra-bundle v5.2.4
friendsofsymfony/rest-bundle 2.5.0
jms/serializer 2.1.0
jms/serializer-bundle 3.0.0
A request with the same JSON data results in the following log output in `Symfony 3.4':
// Log output
sortAction
object -- AppBundle\Util\SortInfo
--
So although the SortInfo is created (not null), the sortBy and sortOrder properties are empty/null.
How can this be? I do not understand why the data is de-serialized in the first place, but how it is possible to create a SortInfo object without setting the properties is even more mysterious.
In Symfony 2.8 the Util.SortInfo.yml file is necessary for the process. Removing this files results in an exception:
Symfony\Component\HttpKernel\Exception\BadRequestHttpException: "You
must define a type for
AppBundle\Util\SortInfo::$sortBy."
at /.../vendor/friendsofsymfony/rest-bundle/Request/RequestBodyParamConverter.php
Here I do not understand why SortInfo::$sortBy is a problem but not SortInfo it self. Additionally removing the file in Symfony 3.4 does not change anything.
But at least this error message confirms, that the FOS RequestBodyParamConverter is involved in the process.
Following the Symfony docs it should be necessary to manually specify that the parameter that should be converted (although it works just fine without this config in Symfony 2.8):
/**
* #FOSRest\View()
* #ParamConverter("sortInfo", converter="fos_rest.request_body")
*/
public function sortAction(SortInfo $sortInfo) {
...
}
However, adding this config results in another error in Symfony 3.4:
Uncaught PHP Exception RuntimeException: "Converter
'fos_rest.request_body' does not support conversion of parameter
'$sortInfo'
Bottom line:
This is all very mysterious.
Why does it work without any problem in Symfony 2.8 although regarding to the docs essential config is missing (explicitly specify ParamConverter)
Why does it NOT work in Symfony 3.4?

Symfony 2 - how to parse %parameter% in my own Yaml file loader?

I have a Yaml loader that loads additional config items for a "profile" (where one application can use different profiles, e.g. for different local editions of the same site).
My loader is very simple:
# YamlProfileLoader.php
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Yaml\Yaml;
class YamlProfileLoader extends FileLoader
{
public function load($resource, $type = null)
{
$configValues = Yaml::parse($resource);
return $configValues;
}
public function supports($resource, $type = null)
{
return is_string($resource) && 'yml' === pathinfo(
$resource,
PATHINFO_EXTENSION
);
}
}
The loader is used more or less like this (simplified a bit, because there is caching too):
$loaderResolver = new LoaderResolver(array(new YamlProfileLoader($locator)));
$delegatingLoader = new DelegatingLoader($loaderResolver);
foreach ($yamlProfileFiles as $yamlProfileFile) {
$profileName = basename($yamlProfileFile, '.yml');
$profiles[$profileName] = $delegatingLoader->load($yamlProfileFile);
}
So is the Yaml file it's parsing:
# profiles/germany.yml
locale: de_DE
hostname: %profiles.germany.host_name%
At the moment, the resulting array contains literally '%profiles.germany.host_name%' for the 'hostname' array key.
So, how can I parse the % parameters to get the actual parameter values?
I've been trawling through the Symfony 2 code and docs (and this SO question and can't find where this is done within the framework itself. I could probably write my own parameter parser - get the parameters from the kernel, search for the %foo% strings and look-up/replace... but if there's a component ready to be used, I prefer to use this.
To give a bit more background, why I can't just include it into the main config.yml: I want to be able to load app/config/profiles/*.yml, where * is the profile name, and I am using my own Loader to accomplish this. If there's a way to wildcard import config files, then that might also work for me.
Note: currently using 2.4 but just about ready to upgrade to 2.5 if that helps.
I've been trawling through the Symfony 2 code and docs (and this SO question and can't find where this is done within the framework itself.
Symfony's dependency injection component uses a compiler pass to resolve parameter references during the optimisation phase.
The Compiler gets the registered compiler passes from its PassConfig instance. This class configures a few compiler passes by default, which includes the ResolveParameterPlaceHoldersPass.
During container compilation, the ResolveParameterPlaceHoldersPass uses the Container's ParameterBag to resolve strings containing %parameters%. The compiler pass then sets that resolved value back into the container.
So, how can I parse the % parameters to get the actual parameter values?
You'd need access to the container in your ProfileLoader (or wherever you see fit). Using the container, you can recursively iterate over your parsed yaml config and pass values to the container's parameter bag to be resolved via the resolveValue() method.
Seems to me like perhaps a cleaner approach would be for you to implement this in your bundle configuration. That way your config will be validated against a defined structure, which can catch configuration errors early. See the docs on bundle configuration for more information (that link is for v2.7, but hopefully will apply to your version also).
I realise this is an old question, but I have spent quite a while figuring this out for my own projects, so I'm posting the answer here for future reference.
I tried a lot of options to resolve %parameter% to parameters.yml but no luck at all. All I can think of is parsing %parameter% and fetch it from container, no innovation yet.
On the other hand I don't have enough information about your environment to see the big picture but I just come up with another idea. It can be quite handy if you declare your profiles in your parameters.yml file and load it as an array in your controller or service via container.
app/config/parameters.yml
parameters:
profiles:
germany:
locale: de_DE
host_name: http://de.example.com
uk:
locale: en_EN
host_name: http://uk.example.com
turkey:
locale: tr_TR
host_name: http://tr.example.com
You can have all your profiles as an array in your controller.
<?php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$profiles = $this->container->getParameter('profiles');
var_dump($profiles);
return $this->render('AcmeDemoBundle:Default:index.html.twig');
}
}
With this approach
you don't have to code a custom YamlLoader
you don't have to worry about importing parameters into other yml files
you can have your profiles as an array anytime you have the $container in your hand
you don't have to load/cache profile files one by one
you don't have to find a wildcard file loading solution
If I got your question correctly, this approach can help you.

Integrate Zend Framework 1 and Doctrine 2

I would like to use Doctrine (v2.4) in my ZendFramework (v1.11) application, which I am starting from scratch. There are some articles describing such integration, but they seem quite complicated and a little out of date. Is there any fairly simple way to connect ZF1 and Doctrine2?
I've implemented this as an application resource (extending \Zend_Application_Resource_ResourceAbstract)
The code is quite long so below is a top level check list of the requirements.
Create a doctrine entity manager configuration instance (Doctrine\ORM\Configuration).
$config = new Doctrine\ORM\Configuration();
Populate the configuration with the required data (metadata driver, cache config etc). Doctrine's documentation is a good reference here to what would be required (http://docs.doctrine-project.org/en/latest/reference/configuration.html)
Example here uses the Annotation driver:
$driver = new Driver\AnnotationDriver(
new Annotations\CachedReader(new Annotations\AnnotationReader(), new Cache\ArrayCache()),
$entityDirs
);
$config->setMetadataDriverImpl($driver);
Lastly pass this new config instance to the static entity manager EntityManager::create
E.G. ($options here is the database connection info as exampled in the above link)
$entityManager = EntityManager::create($options['database'], $config);
Take a look at my full source, at the very least it will give you a head start:
https://github.com/alex-patterson-webdev/Multiverse/blob/master/lib/Multiverse/Application/Resource/Entitymanager.php

Symfony 2 odbc component

Just configuring my Standard Symfony2 edition to be used with the odbc driver. I need that for the Teradata connection.
I completely don't need any ORM tools, I will only use it to return the results from the Teradata Stored Procedure or complex query.
Currently it works in the procedural PHP code by following few simple steps:
- $conn = odbc_connect(HOST, USER, PASSWORD)
- $stmt = odbc_prepare($conn, $query)
- $params = array(1, 2, 3)
- odbc_execute($stmt, $params)
- next just fetch results using odbc_fetch_array($stmt)
Just thinking now about how and where to implement this in the Symfony2 MVC stack according to the Sf2 best practices... Perhaps a separate service?
Will only mention that the ease of use for fresh developers is a priority.
Thanks in advance for any help!
From what you describe I would suggest creating a service which, instead of using global constants receives HOST, USER and PASSWORD via Service Container, i.e. you store that information in your parameters.yml, create a service with these parameters in your bundle's service.yml and finally you create your Service-class which basically just wraps your odbc-calls.
This way you can keep the sensitive data (user, password) out of your git-repo, assuming you are not tracking parameters.yml as is suggested in Symfony's documentation and you can easily access your service from everywhere where you have access to the Service Container like this:
$this->getContainer()->get('acme_demo.teradata_service')
parameters.yml:
parameters:
teradata_host: localhost
teradata_user: username
teradata_password: password
Acme\DemoBundle\Resources\config\services.yml:
services:
acme_demo.teradata_service:
class: Acme\DemoBundle\Service\TeradataService
arguments:
- %teradata_host%
- %teradata_user%
- %teradata_password%
Acme\DemoBundle\Service\TeradataService:
class TeradataService
{
public function __construct($host, $user, $password)
...
}
You could even go further and make the teradata service private in your service.yml and then just create other services which access your TeradataService. This way you can make sure, that deleopers do not interfere with the service directly, but only via ObjectManagers.
Acme\DemoBundle\Resources\config\services.yml:
services:
acme_demo.teradata_service:
public: false
class: Acme\DemoBundle\Service\TeradataService
arguments:
- %teradata_host%
- %teradata_user%
- %teradata_password%
acme_demo.another_service:
class: Acme\DemoBundle\Service\ServiceAccessingTeradata
arguments:
- #acme_demo.teradata_service
This way your services are not bound to your teradata-connection directly, which makes it easier to replace Acme\DemoBundle\Service\TeradataService with something else.

Categories