0

I have the following form type:

<?php

namespace Myproject\App\ErpBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormEvent;
use Myproject\OrderBundle\Entity\OrderProductType;
use Myproject\OrderBundle\Entity\OrderOrder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\HttpFoundation\RequestStack;

class OrderSearchType extends AbstractType
{
    protected $em;
    protected $container;
    protected $requestStack;

    public function __construct(ObjectManager $em, ContainerInterface $container, RequestStack $requestStack)
    {
        $this->em = $em;
        $this->container = $container;
        $this->requestStack = $requestStack;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $data = $builder->getData();
        $currentRequest = $this->requestStack->getCurrentRequest();
        $pt = [
            'class' => OrderProductType::class,
            'choices' => [],
            'required' => false
        ];
        if ($productType = $currentRequest->query->get('ProductType', null)) {
            $pt["data"] = $productType;
        }
        $id = [
            'label' => 'id',
            'required' => false
        ];
        if ($idValue = $currentRequest->query->get('id', null)) {
            $id["data"] = $idValue;
        }
        $name = [
            'label' => 'név',
            'required' => false
        ];
        if ($nm = $currentRequest->query->get('name', null)) {
            $name["data"] = $nm;
        }
        $builder
            ->add('productType', EntityType::class, $pt)
            ->add('id', IntegerType::class, $id)
            ->add('name', TextType::class, $name)
            ->add('status', ChoiceType::class, [
                'choices' => [  'Bármilyen',
                                OrderOrder::$ORDER_STATUS_TEXT[OrderOrder::$ORDER_STATUS_ORDERED],
                                OrderOrder::$ORDER_STATUS_TEXT[OrderOrder::$ORDER_STATUS_ACCEPTED],
                                OrderOrder::$ORDER_STATUS_TEXT[OrderOrder::$ORDER_STATUS_REFUSED]
                            ],
                'data' => $currentRequest->query->get('status', "1")
            ])
            ->add('search', SubmitType::class, [
                'label' => 'Keresés',
                'attr' => ['class' => 'btn btn-primary btn-sm']
            ]);

        $builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPresubmit'));
        $builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPostSetData'));
    }

    public function onPostSetData(FormEvent $event)
    {
        $form = $event->getForm();
        $data = $form->getData();
        $event->getForm()
            ->add('productType', EntityType::class, [
                'class' => OrderProductType::class,
                'choices' => $this->em->getRepository(OrderProductType::class)->findAll(),
                'choice_label' => function($orderProductType = null) {
                    return sprintf($orderProductType ? $orderProductType->getTitle() : "Kérem válasszon");
                },
                'label' => 'Termékfajta',
                'required' => false,
                'attr' => [
                    'class' => 'selectpicker',
                    'data-live-search' => 'true'
                ]
            ]);
    }

    public function onPresubmit(FormEvent $event)
    {
        $form = $event->getForm();
        $data = $event->getData();
        $event->getForm()
            ->add('productType', EntityType::class, [
                'class' => OrderProductType::class,
                'choices' => $this->em->getRepository(OrderProductType::class)->findAll(),
                'choice_label' => function($orderProductType = null) {
                    return sprintf($orderProductType ? $orderProductType->getTitle() : "Kérem válasszon");
                },
                'label' => 'Termékfajta',
                'required' => false,
                'attr' => [
                    'class' => 'selectpicker',
                    'data-live-search' => 'true'
                ]
            ]);
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => null,
            'em' => $this->em,
        ]);

        parent::configureOptions($resolver);
    }

    public function getBlockPrefix()
    {
        return 'route';
    }
}

It is displayed on the ui like this:

            {{ form_start(form, { 'attr': {'class': 'myprojectAdminForm pull-right', 'id': form.vars.name } }) }}
                Szűrés: 
                {{ form_widget(form.productType) }}
                {{ form_widget(form.id, {'attr': {'placeholder': form.id.vars.label, 'class':'form-control'}}) }}
                {{ form_widget(form.name, {'attr': {'placeholder': form.name.vars.label, 'class':'form-control' }}) }}
                {{ form_widget(form.status, {'attr': {'class':'form-control'}}) }}
                {{ form_widget(form.search) }}
                {{ form_widget(form._token) }}
                {{ form_errors(form) }}
            {{ form_end(form, {'render_rest': false}) }}

and whenever the user clicks on search, the following Javascript is executed:

    $("#erpordersearch_search").click(function(e) {
        e.preventDefault();
        var productTypeID = document.querySelector("#erpordersearch_productType").value;
        var ID = document.getElementById("erpordersearch_id").value;
        var name = document.getElementById("erpordersearch_name").value;
        var status = document.getElementById("erpordersearch_status").value;
        var url = window.location.href.substring(0, window.location.href.lastIndexOf("/page"));
        url = url + "/page1";
        window.location.href = url + "[ProductTypeID=" + productTypeID + "&id=" + ID + "&name=" + name + "&status=" + status + "]";
    });

This works, but the problem is that I am tracking user action history on the ui and if the system loads the full page, then I am losing all the history of the user's action, as the history is saved in the Javascript. I know I could save the history in the database or localStorage, but either of those are not viable options in my case, as they would violate the policies of the project. I was asked to do modifications which will consist of the following features:

  • the URL will contain [queryString=somevalue] after the request is responded, even if it did not contain it before the GET request
  • after the request all the defined Javascript variables should exist. If I have written var foo = 'bar'; in the console, after the request the variable should be defined

I have been given some vague description about how this should be achieved via the combination of Symfony and the particular project I am working on, but it is unclear and contains project-specific information I cannot share here.

So my question is: is there a way using PHP and Symfony, to change the URL when sending a request and still keeping all the variables which were defined in Javascript?

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175

1 Answers1

1

The only way I would say this is possible (prepare for more vague explanations) is to have your JavaScript variables stored in invisible form elements that are submitted on pressing the search button.

These should then be sent to the Symfony controller via post data, form here you can then use the method outlined here to send variables to JavaScript from PHP (but modified for twig syntax) - How can I store JavaScript variable output into a PHP variable?

If in a script file, you can try passing arguments this way - https://www.gun.io/blog/pass-arguments-to-embedded-javascript-tutorial-example


EDIT

After reading over this all again (and discussing this in the comments below), I think I understand the question better.

I personally agree with you, it looks like this will be impossible to do in pure Symfony via GET requests.

Even if there was a "hacky" method, it wouldn't be scaleable or dynamic and thus not worth doing.

Hex
  • 241
  • 1
  • 10
  • I understand your answer, but if I implement this, it will not be accepted. The expectation is to have a GET request upon search, which will modify the URL and will change the page content, without full reloading. As far as I know the URL cannot be changed without reloading the page (with the exception of changing the hash, which is not an option here). If I am right that this is impossible, then I can tell the others that their idea is impossible. If it is possible, I have to implement it. – Lajos Arpad Jul 04 '18 at 16:16
  • In this Symfony project there is a breadcrumb and whenever we click a link, the breadcrumb is expanded. However, if we click on a breadcrumb link, the given page should be loaded without full page load. If the URL does not contain what I need, the history will be lost. I can implement hacks, but they will not be accepted, unless I prove that the initial idea is impossible. – Lajos Arpad Jul 04 '18 at 16:18
  • @LajosArpad have you thought about just changing the URL in Javascript and using something like vue.js to display the results with JSON? Related question on changing the url - https://stackoverflow.com/questions/824349/modify-the-url-without-reloading-the-page – Hex Jul 06 '18 at 15:01
  • I am not interested in VueJS in the context of this question, however, the link you have provided answers the essence of this question. Since this is a Symfony question, we need to know whether there is something in Symfony, which is responsible for this behavior. – Lajos Arpad Jul 07 '18 at 13:22
  • @LajosArpad as far as my understanding of PHP and Java goes this isn't a Symfony issue and just a fundamental way that the web works (if I understand your question correctly), as Java is client-side and Symfony is server side there's nothing you can change in either to remove this behavior as it's not caused by either. You'll need a "hacky" solution or to look at JSON frameworks to solve the issue – Hex Jul 09 '18 at 18:32
  • By "Java" I guess you have meant Javascript. Symfony is a large toolbox which provides a lot of tools. If I start to use a tool outside of Symfony, whereas Symfony provides an equivalent tool for the same thing, then my solution will not be accepted. You pointed out that the feature is possible and recommended VueJS, however, VueJS is not an option, unless there is not a Symfony-based solution for this. Symfony provides a server-side framework, but that does not exclude the possibility of the provision of client-side features as well. – Lajos Arpad Jul 10 '18 at 11:16
  • Read more here about the client-side features of Symfony: https://symfony.com/doc/current/frontend.html – Lajos Arpad Jul 10 '18 at 11:17
  • @lajosarpad even those features you've linked to however aren't technically "client side" as they are just wrappers for serving content to the client and managing assets. After reading over this all again, I think I understand the question better. I personally agree with you, it looks like this will be impossible to do in pure Symfony via GET requests. Even if there was a "hacky" method, it wouldn't be scaleable or dynamic – Hex Jul 10 '18 at 12:25
  • Please, edit your answer with this information so I can accept it and let me know in the comment section about it. Thanks. – Lajos Arpad Jul 11 '18 at 09:13