Symfony requestStack->getCurrentRequest() returning empty post array - event-listener

Trying to get POST parameters in doctrine listener. But, getting an empty array. Tried to push the request to requeststack in controller as well. But, still coming in as empty.
class OrderListener
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function postPersist(LifecycleEventArgs $args)
{
$entity = $this->getObject();
if ($entity instanceof Order) {
$request = $this->requestStack->getCurrentRequest();
$this->handleEvent($entity, $args->getEntityManager());
}
}
public function handleEvent(Order $order, EntityManagerInterface $em)
{
// here $request->request->all() is empty
$request = $this->requestStack->getCurrentRequest();
$transaction = new Transaction();
$transaction->setOrder($order);
$transaction->setRemoteId($request->request->get('stripeInvoiceId'));
...
}
}

So, json_decode($request->getContent(), true) gave me the correct params. Returned an array of post data. Strange thing is $request->request->all() should have worked too.

Related

ContentEntityForm buildForm method parent out of memory

I have the following issue with my form class that extends the ContentEntityForm class.
When calling the parent buildForm which is needed my system runs out of memory.
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
// Here is already runs of memory. $form is never initiated.
/* #var $entity \Drupal\sg_configuration_rule\Entity\ConfigurationRule */
$entity = $this->entity;
$form_state->set('old_cron_value', $entity->get('cron_settings')->first()->value);
$type = FALSE;
if (!$entity->isNew()) {
$type = $entity->getPluginInstance()->getPluginId();
}
if ($entity->isNew()) {
$type = \Drupal::request()->query->get('type');
if (!$type) {
return new RedirectResponse(Url::fromRoute('configuration_rule.add_form_step1')->toString());
}
}
if ($type) {
try {
/** #var \Drupal\sg_base_api\Plugin\BaseApiPluginInterface $enabled_api */
$enabled_api = $this->baseApiPluginManager->createInstance($type);
}
catch (PluginException $exception) {
LoggerService::error($exception->getMessage());
return new RedirectResponse(Url::fromRoute('configuration_rule.add_form_step1')->toString());
}
$enabled_api->configRuleForm($form, $entity);
$form['plugin_type']['widget'][0]['value']['#value'] = $type;
$form['plugin_type']['widget'][0]['value']['#access'] = FALSE;
$form['plugin_type']['widget'][0]['value']['#disabled'] = TRUE;
$form['server_node']['widget']['#options'] = $this->getServerNodesByType($enabled_api->entityType());
}
$form['user_id']['#access'] = FALSE;
return $form;
}
When i check the parent function i noticed that the line:
$form = $this->form($form, $form_state); is causing this in the class EntityForm(Core method).
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// During the initial form build, add this form object to the form state and
// allow for initial preparation before form building and processing.
if (!$form_state->has('entity_form_initialized')) {
$this->init($form_state);
}
// Ensure that edit forms have the correct cacheability metadata so they can
// be cached.
if (!$this->entity->isNew()) {
\Drupal::service('renderer')->addCacheableDependency($form, $this->entity);
}
// Retrieve the form array using the possibly updated entity in form state.
// This is causing my memory timeout.
$form = $this->form($form, $form_state);
// Retrieve and add the form actions array.
$actions = $this->actionsElement($form, $form_state);
if (!empty($actions)) {
$form['actions'] = $actions;
}
return $form;
}
If i comment that line out it is working fine but this is needed to save my values in config. Also this is core and should work.
Anyone else have this problem and knows the solutions to this?
Thanks.
This is solved, the error was that too much results where loaded in a select field.

Redirect inside a service class?

I've created my own service class and I have a function inside it, handleRedirect() that's supposed to perform some minimal logical check before choosing to which route to redirect.
class LoginService
{
private $CartTable;
private $SessionCustomer;
private $Customer;
public function __construct(Container $SessionCustomer, CartTable $CartTable, Customer $Customer)
{
$this->SessionCustomer = $SessionCustomer;
$this->CartTable = $CartTable;
$this->Customer = $Customer;
$this->prepareSession();
$this->setCartOwner();
$this->handleRedirect();
}
public function prepareSession()
{
// Store user's first name
$this->SessionCustomer->offsetSet('first_name', $this->Customer->first_name);
// Store user id
$this->SessionCustomer->offsetSet('customer_id', $this->Customer->customer_id);
}
public function handleRedirect()
{
// If redirected to log in, or if previous page visited before logging in is cart page:
// Redirect to shipping_info
// Else
// Redirect to /
}
public function setCartOwner()
{
// GET USER ID FROM SESSION
$customer_id = $this->SessionCustomer->offsetGet('customer_id');
// GET CART ID FROM SESSION
$cart_id = $this->SessionCustomer->offsetGet('cart_id');
// UPDATE
$this->CartTable->updateCartCustomerId($customer_id, $cart_id);
}
}
This service is invoked in the controller after a successful login or registration. I'm not sure what's the best way to access redirect()->toRoute(); from here (or if I should do it here).
Also if you have other comments on how my code is structured please feel free to leave them.
Using plugins within your services is a bad idea as they require a controller to be set. When a service is created and you inject a plugin it has no idea of the controller instance so it will result in an error exception. If you want to redirect the user you might just edit the response object as the redirect plugin does.
Notice that I stripped the code to keep the example clear and simple.
class LoginServiceFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new LoginService($container->get('Application')->getMvcEvent());
}
}
class LoginService
{
/**
* #var \Zend\Mvc\MvcEvent
*/
private $event;
/**
* RedirectService constructor.
* #param \Zend\Mvc\MvcEvent $event
*/
public function __construct(\Zend\Mvc\MvcEvent $event)
{
$this->event = $event;
}
/**
* #return Response|\Zend\Stdlib\ResponseInterface
*/
public function handleRedirect()
{
// conditions check
if (true) {
$url = $this->event->getRouter()->assemble([], ['name' => 'home']);
} else {
$url = $this->event->getRouter()->assemble([], ['name' => 'cart/shipping-info']);
}
/** #var \Zend\Http\Response $response */
$response = $this->event->getResponse();
$response->getHeaders()->addHeaderLine('Location', $url);
$response->setStatusCode(302);
return $response;
}
}
Now from within your controller you can do the following:
return $loginService->handleRedirect();

Symfony2 pass two variables from the service to the Controller $slug

To clean up my controller code I want to move the "newPostAction" to a service. The problem I get is that now I cannot pass as a result of the funtion in the service two variables to the controller. I use the function to create a form, and the get the slug from the form's post and render it. I do not know how to pass it to the controller. I tried using the "list()" function but it does not get the info right.
How can I call the pos's "slug" from inside the controller?
Here is the controller code:
/**
* #param Request $request
* #return array
*
* #Route("/new/_post", name="_blog_backend_post_new")
* #Template("BlogBundle:Backend/Post:new.html.twig")
*/
public function newPostAction(Request $request)
{
$form_post = $this->getPostManager()->createPost($request);
$slug_post = ¿How do I get it from inside the createPost()?;
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}
Here is the PostManager service to create the new post entity:
/**
* Create and validate a new Post
*
* #param Request $request
* #return bool|FormInterface
*/
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return true;
}
return $form_post;
}
You just need to return an array from the service and access the values from the controller.
UPDATE
Some changes need to be made to your code in order to get things to work.
Explanation: when the form is valid, the previous code (I deleted it) returned true therefore $ret["form_post"] didn't make sense because $ret was not an array. It surprises me that it didn't throw you an error.
Anyway, that could explain why Doctrine didn't persist your entity. Talking about the redirection, the error could be due to the same reason. $ret was true (a boolean) and $ret["form_slug"] didn't make sense either.
I hope this fixes the problems. Please, let me know if it works.
Service
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return array("form_post" => true, "slug_post" => $slug_post);;
}
return array("form_post" => $form_post, "slug_post" => $slug_post);
}
Controller:
public function newPostAction(Request $request)
{
$ret = $this->getPostManager()->createPost($request);
$form_post = $ret["form_post"];
$slug_post = $ret["slug_post"];
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}

Zend ACL Dynamic Assertion

I want to restrict my users to edit/delete only the comments which they added. I found an example on youtube by a guy named intergral30 and followed his instruction. And now my admin account has the possibility to edit/delete everything, but my user has no access to his own comment.
Here's the code:
Resource
class Application_Model_CommentResource implements Zend_Acl_Resource_Interface{
public $ownerId = null;
public $resourceId = 'comment';
public function getResourceId() {
return $this->resourceId;
}
}
Role
class Application_Model_UserRole implements Zend_Acl_Role_Interface{
public $role = 'guest';
public $id = null;
public function __construct(){
$auth = Zend_Auth::getInstance();
$identity = $auth->getStorage()->read();
$this->id = $identity->id;
$this->role = $identity->role;
}
public function getRoleId(){
return $this->role;
}
}
Assertion
class Application_Model_CommentAssertion implements Zend_Acl_Assert_Interface
{
public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $user=null,
Zend_Acl_Resource_Interface $comment=null, $privilege=null){
// if role is admin, he can always edit a comment
if ($user->getRoleId() == 'admin') {
return true;
}
if ($user->id != null && $comment->ownerId == $user->id){
return true;
} else {
return false;
}
}
}
In my ACL I have a function named setDynemicPermissions, which is called in an access check plugin's preDispatch method.
public function setDynamicPermissions() {
$this->addResource('comment');
$this->addResource('post');
$this->allow('user', 'comment', 'modify', new Application_Model_CommentAssertion());
$this->allow('admin', 'post', 'modify', new Application_Model_PostAssertion());
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$this->_acl->setDynamicPermissions();
}
And I'm calling the ACL-s isAllowed method from my comment model where I return a list of comment objects.
public function getComments($id){
//loading comments from the DB
$userRole = new Application_Model_UserRole();
$commentResource = new Application_Model_CommentResource();
$comments = array();
foreach ($res as $comment) {
$commentResource->ownerId = $comment[userId];
$commentObj = new Application_Model_Comment();
$commentObj->setId($comment[id]);
//setting the data
$commentObj->setLink('');
if (Zend_Registry::get('acl')->isAllowed($userRole->getRoleId(), $commentResource->getResourceId(), 'modify')) {
$commentObj->setLink('Edit'.'Delete');
}
$comments[$comment[id]] = $commentObj;
}
}
Can anyone tell me what have I done wrong?
Or what should I use if I want to give my admins the right to start a post and other users the right to comment on them. Each user should have the chance to edit or delete his own comment and an admin should have all rights.
You seem to be using the dynamic assertions in a wrong manner, as you are still passing the roleId to isAllowed().
What these dynamic assertions really do, is take a complete object and work with it. Zend will determine which rule has to be used by calling getResourceId() and getRoleId() on your objects.
So all you have to do is pass your objects instead of the strings to isAllowed():
public function getComments($id){
//loading comments from the DB
$userRole = new Application_Model_UserRole();
$commentResource = new Application_Model_CommentResource();
$comments = array();
foreach ($res as $comment) {
$commentResource->ownerId = $comment[userId];
$commentObj = new Application_Model_Comment();
$commentObj->setId($comment[id]);
//setting the data
$commentObj->setLink('');
// This line includes the changes
if (Zend_Registry::get('acl')->isAllowed($userRole, $commentResource, 'modify')) {
$commentObj->setLink('Edit'.'Delete');
}
$comments[$comment[id]] = $commentObj;
}
}
But in can be done better
You would not have to implement a total new Application_Model_CommentResource, but instead you can use your actual Application_Model_Comment like this:
// we are using your normal Comment class here
class Application_Model_Comment implements Zend_Acl_Resource_Interface {
public $resourceId = 'comment';
public function getResourceId() {
return $this->resourceId;
}
// all other methods you have implemented
// I think there is something like this among them
public function getOwnerId() {
return $this->ownerId;
}
}
Assertion would then use this object and retrieve the owner to compare it with the actually logged in person:
class Application_Model_CommentAssertion implements Zend_Acl_Assert_Interface {
public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $user=null,
Zend_Acl_Resource_Interface $comment=null, $privilege=null){
// if role is admin, he can always edit a comment
if ($user->getRoleId() == 'admin') {
return true;
}
// using the method now instead of ->ownerId, but this totally depends
// on how one can get the owner in Application_Model_Comment
if ($user->id != null && $comment->getOwnerId() == $user->id){
return true;
} else {
return false;
}
}
And the usage is like this:
public function getComments($id) {
//loading comments from the DB
$userRole = new Application_Model_UserRole();
$comments = array();
foreach ($res as $comment) {
$commentObj = new Application_Model_Comment();
$commentObj->setId($comment[id]);
//setting the data
$commentObj->setLink('');
// no $commentResource anymore, just pure $comment
if (Zend_Registry::get('acl')->isAllowed($userRole, $comment, 'modify')) {
$commentObj->setLink('Edit'.'Delete');
}
$comments[$comment[id]] = $commentObj;
}
}

Symfony2: Delete entity through post request (improvements required)

I need some improvements about my actual way to delete entities:
public function deleteAction($path)
{
$form = $this->createFormBuilder(array('path' => $path))
->add('path')
->setReadOnly(true)
->getForm();
if ($this->getRequest()->getMethod() === 'POST') {
$form->bindRequest($this->getRequest());
if ($form->isValid()) {
$image = $this->getImageManager()->findImageByPath($path);
$this->getImageManager()->deleteImage($image);
return $this->redirect($this->generateUrl('AcmeImageBundle_Image_index'));
}
}
return $this->render('AcmeImageBundle:Image:delete.html.twig', array(
'form' => $form->createView(),
));
}
Two improvements I already found while writting:
CreateFormBuilder in extra method in controller
Hidden field and overgive extra image-entity to get rendered
Are there other thing I could make better?
Regards
(my answer is too long for the comment so i add it here)
First you have to create a Type file (generally in YourApp\YourBundle\Form\yourHandler.php), some basique code to put inside if you don't know:
<?php
namespace ***\****Bundle\Form;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManager;
use ***\****Bundle\Entity\your_entity;
class *****Handler
{
protected $form;
protected $request;
protected $em;
public function __construct(Form $form, Request $request, EntityManager $em)
{
$this->form = $form;
$this->request = $request;
$this->em = $em;
}
public function process()
{
if( $this->request->getMethod() == 'POST' )
{
$this->form->bindRequest($this->request);
if( $this->form->isValid() )
{
$this->onSuccess($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(your_entity $object)
{
// Make your stuff here (remove,....)
}
}
And in your controller i just call it this way:
if (!empty($_POST))
{
$formHandler = new *****Handler($my_form, $this->get('request'), $this->getDoctrine()->getEntityManager());
$formHandler->process();
}
Hope i'm clear enough