Is there a way to access the FlashBag from outside Symfony? - symfony

I'm working in app that interacts with a Symfony app, and renders a template from the Symfony app that uses the FlashBag. What I have so far:
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
/*snipped code that creates a Symfony container*/
$container->enterScope('request');
$container->set('request', new \Symfony\Component\HttpFoundation\Request(), 'request');
$twig = $container->get('twig');
$twig->addGlobal('app', array('session' => array('flashbag' => new FlashBag())));
echo $twig->render('AdminBundle::flashMessages.html.twig');
The problem is the FlashBag object I've created does not pick up the Flash messages that are in the session, so while technically the twig template does render, it never actually displays the flash messages.
An additional point of difficulty is that the outside app makes use of session, so just creating a Symfony session object also causes problems.

I discovered that all of Symfony's flash messages are stored in $_SESSION['_sf2_flashes']. After I create the FlashBag, I call initialize and pass the flash messages in $_SESSION, like so, before adding flashBag as a global to Twig:
$flashMessages = isset($_SESSION['_sf2_flashes']) ? $_SESSION['_sf2_flashes'] : array();
$flash->initialize($flashMessages);
I also found that when done this way, the flash messages don't seem to get consumed from $_SESSION like when you access them the normal way, so I also had to call:
unset($_SESSION['_sf2_flashes']);

You need to use Request::createFromGlobals() to populate your Request object. Then you can use that request object to get the flash messages.

Related

How to reuse KafkaListenerContainerFactory with a custom message converter for batch listeners and non-batch/record listeners?

The spring-kafka documentation mentions:
Starting with version 2.8, you can override the factory’s batchListener propery using the batch property on the #KafkaListener annotation. This, together with the changes to Container Error Handlers allows the same factory to be used for both record and batch listeners.
I want to use it like this. So reuse the same factory for record and batch listeners. The factory is provided by an internal library that is used by multiple services.
However, I also need to define a custom MessageConverter.
But I found out that for batch listeners I need to wrap my message converter in BatchMessagingMessageConverter otherwise the message converter will not be used correctly and the wrong type will be supplied to my batch listener.
So: Is there a simple way to reuse KafkaListenerContainerFactory with a custom messageConverter for batch listeners and non-batch/record listeners?
My current workaround looks like this, but I do not like it as it depends on how spring-kafka internally sets up its configuration, so it might break in future updates:
factory.setContainerCustomizer(container -> {
var messageListener = container.getContainerProperties().getMessageListener();
if (messageListener instanceof FilteringBatchMessageListenerAdapter) {
var meessageListenerDelegate =
((FilteringBatchMessageListenerAdapter<?, ?>) messageListener).getDelegate();
if (meessageListenerDelegate instanceof BatchMessagingMessageListenerAdapter) {
((BatchMessagingMessageListenerAdapter<?, ?>) meessageListenerDelegate).setBatchMessageConverter(
new BatchMessagingMessageConverter(messageConverter));
}
}
});
Another option is to create a separate factory for batch listeners. With this, I am afraid that someone might use #KafkaListener(batch="true") without supplying the correct library, which only works partly.
Currently, I am using version 2.8.9 of spring-kafka.
It is not currently possible; please open a new feature suggestion on GitHub to allow provisioning both types of converter on the factory.

Symfony Forms manual submission of both GET and POST requests

Is there any way to get merged data from Request class? Because currently we are submitting forms manually for API controller that accepts both POST and GET queries (this is not an REST API because of legacy project).
$data = array_merge($request->query->all(), $request->request->all());
$form->submit($data);
Is there any way to write something cleaner instead of code below?
$data = array_merge($request->query->all(), $request->request->all());
I think that is not possible. (Maybe I'm wrong)
If you look at the source code of Request, you can see that when Symfony create the request, Symfony put the global variable $_GET in $this->query and $_POST in $this->request.
There is no Symfony variable that takes both.
If you need it in only one place, I think what you have done is fine.
If not, create a service that will or another solution that factors this code.
Another solution is to use the global variable $_REQUEST, because Symfony makes the merge, but it depends on your php configuration (request_order parameter of your php.ini).
But I do not think using Superglobals variables with Symfony is a good idea ... (Besides symfony overwrites them)

How to rollback any transaction when doing test with phpUnit in symfony2

I'm testing the controllers using the crawler, but when I'm posting a form that doesn't generate any errors, it save the form in the database.
How can I prevent him to do so without changing the controller, and without testing something else.
Is there best practice about this kinds of test ?
I tried the rollback, but in the ControllerTest there is no more active transactions
You need to write your own test client class extending Symfony\Bundle\FrameworkBundle\Client.
It's because default client doesn't share connection object between requests (so you can't use transactions outside test client). If you extend test client you can handle transaction by your own.
In your client class you need make static connection object, and override method doRequest() to avoid creating new connection object every time but use our static one instead.
It's well described here:
http://alexandre-salome.fr/blog/Symfony2-Isolation-Of-Tests
When you have your own doRequest method all you need is handle transaction, so you wrap handle() method with begin and rollback. Your doRequest method could look sth like that:
protected function doRequest($request)
{
// here you need create your static connection object if it's doesn't exist yet
// and put it into service container as 'doctrine.dbal.default_connection'
(...)
self::$connection->beginTransaction();
$response = $this->kernel->handle($request);
self::$connection->rollback();
(...)
return $response
}
You can read the documentation of PHPUnit for database testing
http://www.phpunit.de/manual/3.6/en/database.html
You will need setup your database and teardown the changes you made.
If you think that the above is too complicated maybe you are interested in make a mockup of your database layer
http://www.phpunit.de/manual/3.6/en/test-doubles.html
Mockup is create a custom object based in the original object where put your own test controls. Probably in this case you are interested in mockup the Entity Manager of Doctrine

using another bundle in Symfony 2, can't access $this->get

I have a REST API built with Symfony2 and the FOSRestBundle. This all works fine, however in one service I combine data form another service in a different bundle - this seemed like it would be simple but it is not.
I am creating a new request object and adding in my parameters, from there I fire off the request to the other service, the service receives the request fine, however, when I try to use $this->get it gives me the good old Call to a member function get() on a non-object in ...
I know that I am missing the service container (I don't entirely understand why it's available when I call a hit the first bundle but not the second), that's all well and fine but how do I inject it or a component of it so I can use $this->get to hit my custom services defined in services.yml? (easy to pass them the service container using arguments:
container: "#service_container")
Setting this bundle up as a service won't work as FOSRestBundle does not call it as a service.
In Short: I want to be able to get data from bundle2 when inside bundle1 by doing
namespace MyVendor\Bundle1\Controller
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use MyVendor\Bundle2\Controller\Bundle2ClassName;
class Bundle1 {
//if i wanted to do this here it would work fine:
// $this->get('my.service.defined.in.service.yml');
$bundle2 = new Bundle2ClassName();
$returned_data = $bundle2->myFunction();
}
Then once inside myFunction in bundle2 if I try to call the exact same service function i get the dreaded get error. If I call bundle2 directly through the FOSRest route i obviously don't have that problem.
namespace MyVendor\Bundle2\Controller
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class Bundle2 {
//this does not work
$this->get('my.service.defined.in.service.yml');
//do some stuff then return response
return($response);
}
I've read all the service container docs over and over again, so if you're going to link to them i'd appreciate it if you could point out the exact section where it explains how this stuff is handled. This has been the one problem i've never been able to fully understand since I started working with Symfony a few months back.
P.S. could someone with enough points add the FOSRestBundle as a tag?
Thanks!
First of all you should use $this->forward to forward request to another controller.
Second, the reason you don't have access to service container in second controller is probably because you're trying to manually initialize it - never do that unless you absolutely know what you're doing (specifically, you forgot to pass service container as controller dependency).
Third, just as an example on how things work - your original controller dependency on service container is handled by the same container and extends ContainerAware, on that controller initialization a setContainer() is called, which you most likely forgot to do when manually initializing second controller. So to get it working (which again I strongly recommend not doing), you should do this:
class Bundle1 {
//if i wanted to do this here it would work fine:
// $this->get('my.service.defined.in.service.yml');
$bundle2 = new Bundle2ClassName();
$bundle2->setContainer($this->container);
$returned_data = $bundle2->myFunction();
}
The reason you're getting the $this->get() on a non-object... error is because $this->get() in your controller is actually a shortcut to $this->container->get() (defined in Symfony\Bundle\FrameworkBundle\Controller\Controller)

How do I get an ID after saving an ExtBase Model?

After creating a model and adding it to a repository I want to have the new ID for different purposes (creating a mail, updating other fields outside the Extbase world)
$page = t3lib_div::makeInstance('Tx_MyExt_Domain_Model_Page');
$page->setTitle('Hello World');
$this->pageRepository->add($page);
At this point $page hasn't got an ID yet, uid is null.
$page->getUid(); // returns null
When does it get it? And how can I retrieve in on runtime?
In ExtBase, objects are "managed". This means every persistence transaction (add/remove/update) is simply noted in the underlying logic, but not yet executed until the appropriate time (like the end of processing a request). So, just because you add an object to a repository doesn't mean that it's actually added yet. That actually happens once $persistenceManager->persistAll() is called, which isn't something you need to do manually, ever. The point is, your $page object won't have a UID until it's saved and that's why $page->getUid() returns null. Look here for a great explanation.
I suspect that you are trying to do something outside of the ExtBase object/MVC lifecycle. At least, last time I got null when I tried to get the UID of an object, it was because I wasn't operating within the framework appropriately.
However, if you post some more code and give us a bigger picture of what you're trying to achieve, maybe we can help you get to a point where that object actually has a UID. For instance, if you're in a Controller object, tell us which Action method you're in, or if you're in a Repository object, tell us what you're trying to get from the repository and where/how you plan on using the query results.
EDIT
Just guessing here, but I'm assuming you're executing this code in some action of a controller. Since after the controller is executed a view is rendered, you can just pass the page object to the view:
$this->view->assign('page', $page);
And then in your view you can use the page object in a link:
<f:link.action action="show" arguments="{page:page}">
See this page object
</f:link.action>
And then in the show action of your controller you can show the page:
public function showAction(Tx_MyExt_Domain_Model_Page $page) {
// Do whatever you need to show the page in the `Show.html` template
}
I really am just guessing here. If you can give us a larger picture of what you're trying to do, what your action methods are supposed to do and things like that, we can answer your question a little more confidently.
(I'm also assuming that your page object isn't a replacement for the regular TYPO3 pages and that they are something totally different. It's much easier to deal with those TYPO3 pages through the backend interface than at the php level.)
You can call persistence manager explicitly in Your controller like this
#TYPO3 4.x
$persistenceManager = $this->objectManager->create('Tx_Extbase_Persistence_Manager');
$persistenceManager->persistAll();
#TYPO3 6.x
$persistenceManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager');
$persistenceManager->persistAll();

Resources